From 9c10af43bc565a6407dbc70818542c6db9af5cce Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 22 Feb 2023 10:51:04 +0000 Subject: [PATCH 01/65] Added code to find acquisition settings This is most of the data needed for TEthys --- src/tethys/output/TethysExporter.java | 112 +++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 2 deletions(-) diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 559c336c..adc76c02 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -1,22 +1,29 @@ package tethys.output; import java.util.ArrayList; +import java.util.List; import org.w3c.dom.Document; import Acquisition.AcquisitionControl; +import Acquisition.AcquisitionParameters; import Acquisition.AcquisitionProcess; +import Acquisition.DaqStatusDataUnit; import Array.ArrayManager; import Array.Hydrophone; import Array.PamArray; import Array.SnapshotGeometry; import PamController.PamControlledUnit; import PamController.PamController; +import PamController.PamSettings; import PamController.settings.output.xml.PamguardXMLWriter; import PamUtils.PamCalendar; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.dataSelector.DataSelector; +import dataMap.OfflineDataMap; +import dataMap.OfflineDataMapPoint; +import generalDatabase.DBControlUnit; import generalDatabase.DBSchemaWriter; import generalDatabase.SQLLogging; import metadata.MetaDataContol; @@ -66,6 +73,29 @@ public class TethysExporter { SnapshotGeometry arrayGeometry = findArrayGeometrey(); + /** + * Doug populate instrument fields - may need to add a few things. Marie + * to define what we mean by instrument. + * Instrument names probably need to be added to the PAMGuard Array dialog and can + * then be extraced from there. We had some useful discussion about what constitutes + * an instrumnet in Tinas dataset where there was a deployment of 10 MARU's, but the + * files from all of them were merged into a single 10 channel wav file dataset and + * processed together for detection and localisation. Clearly this goes into a single + * Tethys database, but do we put 'MARU Array' as the instrument and then include + * serial numbers of individual MARU's with the hydrophone data, or what ? + */ + + + /** + * Doug write something here to get most of the 'samplingdetails' schema. + * This all comes out of the Sound Acquisition module. Code below shows how + * to find this and iterate through various bits of information ... + * (I've put it in a separate function. Currently returning void,but could + * presumably return a Tethys samplingdetails document?) + */ + getSamplingDetails(); + + /* * Call some general export function */ @@ -151,6 +181,84 @@ public class TethysExporter { return currentGeometry; + } + + private void getSamplingDetails() { + // first find an acquisition module. + PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); + if (aModule instanceof AcquisitionControl == false) { + // 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; + } + // 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(); + if (allStatusData != null && allStatusData.size() > 0) { + long dataStart = Long.MAX_VALUE; + long dataEnd = Long.MIN_VALUE; + // 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 () +// } + + + } /** @@ -201,7 +309,7 @@ public class TethysExporter { * (though I'd assume that having the Document is more useful) */ String schemaXMLString = pamXMLWriter.getAsString(tethysSchema.getXsd(), false); -// System.out.printf("Schema for %s is %s\n", aDataBlock.getDataName(), schemaXMLString); + System.out.printf("Schema for %s is %s\n", aDataBlock.getDataName(), schemaXMLString); /* @@ -209,7 +317,7 @@ public class TethysExporter { * the parameters that were controlling that module, with adequate data about * upstream modules). I think this has to go somewhere into the Detections document. */ - Document doc = pamXMLWriter.writeOneModule(pamXMLWriter, System.currentTimeMillis()); + Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); String moduleXML = null; if (doc != null) { // this string should be XML of all the settings for the module controlling this datablock. From bfed9cfa009fdae7b29ad5923435d58f6eff4f63 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 23 Feb 2023 13:37:53 +0000 Subject: [PATCH 02/65] Update to Nilus 3 --- .classpath | 6 +- src/tethys/TethysControl.java | 4 +- src/tethys/deployment/DeploymentWrapper.java | 100 +++++++++---------- src/tethys/deployment/PamDeployment.java | 14 +-- 4 files changed, 60 insertions(+), 64 deletions(-) diff --git a/.classpath b/.classpath index d60e2620..5e7eb460 100644 --- a/.classpath +++ b/.classpath @@ -17,10 +17,6 @@ - - - - - + diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 25f62a7f..c7744e3d 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -11,8 +11,8 @@ import javax.swing.JMenuItem; import PamController.PamControlledUnit; import PamController.PamController; import PamguardMVC.PamDataBlock; -import nilus.Deployment; -import nilus.Deployment.Instrument; +//import nilus.Deployment; +//import nilus.Deployment.Instrument; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.output.TethysExporter; diff --git a/src/tethys/deployment/DeploymentWrapper.java b/src/tethys/deployment/DeploymentWrapper.java index 1d1d98ca..01126c92 100644 --- a/src/tethys/deployment/DeploymentWrapper.java +++ b/src/tethys/deployment/DeploymentWrapper.java @@ -12,10 +12,10 @@ import javax.xml.bind.Marshaller; import javax.xml.namespace.QName; import javax.xml.validation.Schema; -import nilus.Deployment.Data.Audio; -import nilus.Deployment; -import nilus.Deployment.Data; -import nilus.Deployment.Instrument; +//import nilus.Deployment.Data.Audio; +//import nilus.Deployment; +//import nilus.Deployment.Data; +//import nilus.Deployment.Instrument; public class DeploymentWrapper { @@ -34,52 +34,52 @@ public class DeploymentWrapper { public static void main(String[] args) { // quick play with some JAXB objects to see what they can do. - Deployment deployment = new Deployment(); - - Class deploymentClass = deployment.getClass(); - Annotation[] annots = deploymentClass.getAnnotations(); - AnnotatedType[] annotInterfaces = deploymentClass.getAnnotatedInterfaces(); - Annotation[] declAnnots = deploymentClass.getDeclaredAnnotations(); - - Instrument instrument = new Instrument(); - instrument.setID("22"); - instrument.setType("SoundTrap"); - QName qName = new QName("Instrument"); - JAXBElement jInst = new JAXBElement(qName, Instrument.class, instrument); - deployment.getContent().add(jInst); - - Deployment.Data data = new Data(); - Audio audio = new Audio(); - audio.setProcessed("??"); - data.setAudio(audio); - JAXBElement jData = new JAXBElement(new QName("Data"), Data.class, data); - deployment.getContent().add(jData); - - String project = "Project Name"; - JAXBElement jProj = new JAXBElement(new QName("Project"), String.class, project); - deployment.getContent().add(jProj); - - String aaa = "Project Something else"; - JAXBElement jProj2 = new JAXBElement(new QName("Region"), String.class, aaa); - deployment.getContent().add(jProj2); - - - try { - JAXBContext jContext = JAXBContext.newInstance(Deployment.class); - Marshaller mar = (Marshaller) jContext.createMarshaller(); - mar.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, true); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - mar.marshal(deployment, bos); - String xml = new String(bos.toByteArray()); - System.out.println(xml); -// Schema schema = mar.getSchema(); // is null. Can't generate it's own it seems. - - } catch (Exception e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - +// Deployment deployment = new Deployment(); +// +// Class deploymentClass = deployment.getClass(); +// Annotation[] annots = deploymentClass.getAnnotations(); +// AnnotatedType[] annotInterfaces = deploymentClass.getAnnotatedInterfaces(); +// Annotation[] declAnnots = deploymentClass.getDeclaredAnnotations(); +// +// Instrument instrument = new Instrument(); +// instrument.setID("22"); +// instrument.setType("SoundTrap"); +// QName qName = new QName("Instrument"); +// JAXBElement jInst = new JAXBElement(qName, Instrument.class, instrument); +// deployment.getContent().add(jInst); +// +// Deployment.Data data = new Data(); +// Audio audio = new Audio(); +// audio.setProcessed("??"); +// data.setAudio(audio); +// JAXBElement jData = new JAXBElement(new QName("Data"), Data.class, data); +// deployment.getContent().add(jData); +// +// String project = "Project Name"; +// JAXBElement jProj = new JAXBElement(new QName("Project"), String.class, project); +// deployment.getContent().add(jProj); +// +// String aaa = "Project Something else"; +// JAXBElement jProj2 = new JAXBElement(new QName("Region"), String.class, aaa); +// deployment.getContent().add(jProj2); +// +// +// try { +// JAXBContext jContext = JAXBContext.newInstance(Deployment.class); +// Marshaller mar = (Marshaller) jContext.createMarshaller(); +// mar.setProperty(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, true); +// +// ByteArrayOutputStream bos = new ByteArrayOutputStream(); +// mar.marshal(deployment, bos); +// String xml = new String(bos.toByteArray()); +// System.out.println(xml); +//// Schema schema = mar.getSchema(); // is null. Can't generate it's own it seems. +// +// } catch (Exception e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } } diff --git a/src/tethys/deployment/PamDeployment.java b/src/tethys/deployment/PamDeployment.java index 4d1a2d43..24a17065 100644 --- a/src/tethys/deployment/PamDeployment.java +++ b/src/tethys/deployment/PamDeployment.java @@ -2,7 +2,7 @@ package tethys.deployment; import java.io.Serializable; -import nilus.DeploymentRecoveryDetails; +//import nilus.DeploymentRecoveryDetails; /** * Wrapper and functions associated with the Tethys Deployment object which can @@ -49,12 +49,12 @@ public class PamDeployment { public PamDeployment() { } - - public DeploymentRecoveryDetails getDeploymentRecoveryDetails() { - DeploymentRecoveryDetails drd = new DeploymentRecoveryDetails(); - - return null; - } +// +// public DeploymentRecoveryDetails getDeploymentRecoveryDetails() { +// DeploymentRecoveryDetails drd = new DeploymentRecoveryDetails(); +// +// return null; +// } private void ripApart(Serializable object) { Class cls = object.getClass(); From 43ea481c3f15df36f623d8d8aec0e89907eed24c Mon Sep 17 00:00:00 2001 From: kbolaughlin Date: Mon, 13 Mar 2023 11:05:10 -0700 Subject: [PATCH 03/65] adding deployment recovery pair --- src/tethys/output/DeploymentRecoveryPair.java | 11 +++ src/tethys/output/TethysExporter.java | 84 ++++++++++++++++++- 2 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/tethys/output/DeploymentRecoveryPair.java diff --git a/src/tethys/output/DeploymentRecoveryPair.java b/src/tethys/output/DeploymentRecoveryPair.java new file mode 100644 index 00000000..00863788 --- /dev/null +++ b/src/tethys/output/DeploymentRecoveryPair.java @@ -0,0 +1,11 @@ +package tethys.output; + +import nilus.DeploymentRecoveryDetails; + +public class DeploymentRecoveryPair { + + public DeploymentRecoveryDetails deploymentDetails; + + public DeploymentRecoveryDetails recoveryDetails; + +} diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index adc76c02..3e3ad8ae 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -28,6 +28,7 @@ import generalDatabase.DBSchemaWriter; import generalDatabase.SQLLogging; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; +import nilus.DeploymentRecoveryDetails; import tethys.TethysControl; import tethys.dbxml.DBXMLConnect; import tethys.pamdata.TethysDataProvider; @@ -70,6 +71,7 @@ public class TethysExporter { */ return false; } + SnapshotGeometry arrayGeometry = findArrayGeometrey(); @@ -93,8 +95,80 @@ public class TethysExporter { * (I've put it in a separate function. Currently returning void,but could * presumably return a Tethys samplingdetails document?) */ - getSamplingDetails(); + + + //1. grab DeploymentRecoveryPair that has deployment details and recovery details + //a. this is based on start and end times + //Douglas calculates out dutycycles to only grab the + + //2. loop through the pairs to populate the extra information + //one pair is one deployment + //see below for matching + + + //id => unique + //project => project in pamguard + //deploymentId == id + //deploymentAlias => blank + //site => UI addition in pamguard, not done, can be blank + //siteAlias => blank + //cruise => UI addition, optional + //Platform=> UI addition in pamguard + //region => UI addition + //Instrument/Type => UI, array manager details (hydrophone names area) + //Instrument/Id => UI, array manager details + //Instrument/Geometry => in pamguard array manager + //SamplingDetails/Channel + //ChannelNumber => in pamguard, hyrdrophone array + //SensorNumber => in pamguard, + //Start => same as timestamp deployment detail + //End => same as timestamp recovery detail + //Sampling/Regimen (change sample rate, pamgauard doesnt handle, only on, get channel info in that loop) + //TimeStamp => start time + //SampleRate_kHz => + //SampleBits => + //Gain (another func call to get gain info) + //DutyCycles => needs to be calculated, not fields in pamguard, have fun Douglas + //QualityAssurance => not in pamguard, UI, maybe deployment notes, optional + //Data/Audio (static) + //URI => folder where audio is saved + //Data/Tracks + //Track => GPS datatable (granularity filter) + //TrackId => not unique between deployments, + //TrackEffort + //OnPath => scattered throughout pamguard + //URI => option, check with Shannon on how they are doing deployments + //Sensors/Audio (per hydrophone not quad array) streamer info + individual hydrophone data together + //pamguard hydrophone data + //number => hydrophoneId + //sensorId => sensor serial number + //Geometry => array geometry field goes to + //Sensors/Depth + //optional + //Sensors/Sensor + //Number => hydrophoneId in pamguard + //SensorId => addition to UI + //Geometry => array geometry fields + //Type => Hydrophone Type + + + + + + //get list of deployment recovery details (start, stop times and lat/long) + //deployment details and recovery details are same structure + //per pair, go through a loop to fill in each deployment + ArrayList deployRecover = getSamplingDetails(); + if (deployRecover == null) { + return false; + } + for (DeploymentRecoveryPair drd : deployRecover) { + + + + + } /* * Call some general export function @@ -183,13 +257,14 @@ public class TethysExporter { return currentGeometry; } - private void getSamplingDetails() { + //in each channel + private ArrayList getSamplingDetails() { // first find an acquisition module. PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); if (aModule instanceof AcquisitionControl == false) { // 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; + return null; } // cast it to the right type. AcquisitionControl daqControl = (AcquisitionControl) aModule; @@ -257,9 +332,10 @@ public class TethysExporter { //// for () // } - + return null; } + /** * No idea if we need this or not. May want to return something different to void, e.g. From 24c7baab6e1c32b9bec324b52d005718139250a2 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 13 Mar 2023 18:31:04 +0000 Subject: [PATCH 04/65] Notes --- src/tethys/output/TethysExporter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 3e3ad8ae..b8a478d4 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -96,7 +96,9 @@ public class TethysExporter { * presumably return a Tethys samplingdetails document?) */ - + /* + * A load of notes Katie put in .... + */ //1. grab DeploymentRecoveryPair that has deployment details and recovery details //a. this is based on start and end times //Douglas calculates out dutycycles to only grab the From 7d69992d44e1379c01ee7215d7b24301f9174490 Mon Sep 17 00:00:00 2001 From: kbolaughlin Date: Mon, 13 Mar 2023 11:33:46 -0700 Subject: [PATCH 05/65] Update TethysExporter.java testing --- src/tethys/output/TethysExporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index b8a478d4..993e4dec 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -97,7 +97,7 @@ public class TethysExporter { */ /* - * A load of notes Katie put in .... + * A load of notes Katie put in ....654654654 */ //1. grab DeploymentRecoveryPair that has deployment details and recovery details //a. this is based on start and end times From 534765d1ccf590dc225629c53927c0df49483a9b Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 13 Mar 2023 18:35:25 +0000 Subject: [PATCH 06/65] notes --- src/tethys/output/TethysExporter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index b8a478d4..eb0b3538 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -165,6 +165,9 @@ public class TethysExporter { return false; } + /* + * This will become the main loop over deployment documents + */ for (DeploymentRecoveryPair drd : deployRecover) { From 98cf9048308f9a7c7cae6c71f2b2a4802fa65d18 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:45:51 +0000 Subject: [PATCH 07/65] first very incomplete deployment doc --- src/PamModel/PamModel.java | 3 +-- src/tethys/TethysLocationFuncs.java | 26 ++++++++++++++++++ src/tethys/TethysTimeFuncs.java | 39 +++++++++++++++++++++++++++ src/tethys/output/TethysExporter.java | 37 ++++++++++++++++++++++--- 4 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 src/tethys/TethysLocationFuncs.java create mode 100644 src/tethys/TethysTimeFuncs.java diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index 005c16d0..8506197d 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -473,8 +473,7 @@ final public class PamModel implements PamModelInterface, PamSettings { mi.setToolTipText("Interface to Tethys Database"); mi.setModulesMenuGroup(utilitiesGroup); mi.setMaxNumber(1); - } - + } /* diff --git a/src/tethys/TethysLocationFuncs.java b/src/tethys/TethysLocationFuncs.java new file mode 100644 index 00000000..d3d084c1 --- /dev/null +++ b/src/tethys/TethysLocationFuncs.java @@ -0,0 +1,26 @@ +package tethys; + +import GPS.GpsData; +import nilus.Deployment; + +/** + * Function(s) to get location information for Tethys in the required format. + * @author dg50 + * + */ +public class TethysLocationFuncs { + + + /** + * Get everything we need for a deployment document including the track # + * and the deployment / recovery information. Basically this means we + * have to load the GPS data, then potentially filter it. Slight risk this + * may all be too much for memory, but give it a go by loading GPS data for + * the deployment times. + * @param deployment + */ + public static void getTrackAndPositionData(Deployment deployment) { + long start = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); + } + +} diff --git a/src/tethys/TethysTimeFuncs.java b/src/tethys/TethysTimeFuncs.java new file mode 100644 index 00000000..53f6a666 --- /dev/null +++ b/src/tethys/TethysTimeFuncs.java @@ -0,0 +1,39 @@ +package tethys; + +import java.util.GregorianCalendar; + +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +public class TethysTimeFuncs { + + /* + * Copied from http://www.java2s.com/Code/Java/Development-Class/ConvertsagiventimeinmillisecondsintoaXMLGregorianCalendarobject.htm + */ + public static XMLGregorianCalendar xmlGregCalFromMillis(long millis) { + try { + final GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeInMillis(millis); + return DatatypeFactory.newInstance().newXMLGregorianCalendar( + calendar); + } + catch (final DatatypeConfigurationException ex) { + System.out.println("Unable to convert date '%s' to an XMLGregorianCalendar object"); + return null; + } + } + + /** + * Convert a Gregorian calendar value back to milliseconds. + * @param xmlGregorian + * @return + */ + public static Long millisFromGregorianXML(XMLGregorianCalendar xmlGregorian) { + if (xmlGregorian == null) { + return null; + } + GregorianCalendar gc2 = xmlGregorian.toGregorianCalendar(); + return gc2.getTimeInMillis(); + } +} diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index da49566f..b425811e 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -28,8 +28,11 @@ import generalDatabase.DBSchemaWriter; import generalDatabase.SQLLogging; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; +import nilus.Deployment; import nilus.DeploymentRecoveryDetails; import tethys.TethysControl; +import tethys.TethysLocationFuncs; +import tethys.TethysTimeFuncs; import tethys.dbxml.DBXMLConnect; import tethys.pamdata.TethysDataProvider; import tethys.pamdata.TethysSchema; @@ -168,9 +171,10 @@ public class TethysExporter { /* * This will become the main loop over deployment documents */ + int i = 0; for (DeploymentRecoveryPair drd : deployRecover) { - + Deployment deployment = createDeploymentDocument(i++, drd); } @@ -200,6 +204,19 @@ public class TethysExporter { return true; } + private Deployment createDeploymentDocument(int i, DeploymentRecoveryPair drd) { + Deployment deployment = new Deployment(); + deployment.setDeploymentDetails(drd.deploymentDetails); + deployment.setRecoveryDetails(drd.recoveryDetails); + + TethysLocationFuncs.getTrackAndPositionData(deployment); + + + + return deployment; + } + + /** * find Deployment data. This is stored in a separate PAMGuard module, which may not * be present. @@ -295,9 +312,9 @@ public class TethysExporter { // 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) { - long dataStart = Long.MAX_VALUE; - long dataEnd = Long.MIN_VALUE; // find the number of times it started and stopped .... int nStart = 0, nStop = 0, nFile=0; for (DaqStatusDataUnit daqStatus : allStatusData) { @@ -336,8 +353,20 @@ public class TethysExporter { // */ //// for () // } + DeploymentRecoveryPair pair = new DeploymentRecoveryPair(); + DeploymentRecoveryDetails deployment = new DeploymentRecoveryDetails(); + DeploymentRecoveryDetails recovery = new DeploymentRecoveryDetails(); + pair.deploymentDetails = deployment; + pair.recoveryDetails = recovery; - return null; + 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; } From 501c0ec292798daca97c27648136d391bd8e8bbd Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 13 Mar 2023 21:44:19 +0000 Subject: [PATCH 08/65] Deployment positions --- src/generalDatabase/SQLLogging.java | 65 +++++++++++++++++++++++++++++ src/tethys/TethysLocationFuncs.java | 59 ++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/src/generalDatabase/SQLLogging.java b/src/generalDatabase/SQLLogging.java index 154e1df1..1a7dd2e6 100644 --- a/src/generalDatabase/SQLLogging.java +++ b/src/generalDatabase/SQLLogging.java @@ -551,6 +551,71 @@ public abstract class SQLLogging { // } return resultSet; } + + /** + * Find the data point which is closest in time to that given, or null + * returning whatever type of data unit this deals with. + * @param timeMillis + * @return + */ + public PamDataUnit findClosestDataPoint(PamConnection con, long timeMillis) { + + PamCursor pamCursor = loggingCursorFinder.getCursor(con, pamTableDefinition); + + // can't really do any math with the string based dates, so will have to query from + // a few s before the time we want. + PamDataUnit[] beforeNafter = new PamDataUnit[2]; + + SQLTypes sqlTypes = con.getSqlTypes(); + + for (int i = 0; i < 2; i++) { + String clause; + + if (i == 0) { + clause = String.format("WHERE UTC <= %s ORDER BY UTC DESC", sqlTypes.formatDBDateTimeQueryString(timeMillis)); + } + else { + clause = String.format("WHERE UTC >= %s ORDER BY UTC ASC", sqlTypes.formatDBDateTimeQueryString(timeMillis)); + } + + ResultSet result = pamCursor.openReadOnlyCursor(con, clause); + if (result==null) { + return null; + } + + PamTableItem tableItem; + try { + if (result.next()) { + // for (int i = 0; i < pamTableDefinition.getTableItemCount(); i++) { + // tableItem = pamTableDefinition.getTableItem(i); + // tableItem.setValue(result.getObject(i + 1)); + // } + // return true; + boolean ok = transferDataFromResult(con.getSqlTypes(), result); + result.close(); + beforeNafter[i] = createDataUnit(sqlTypes, lastTime, lastLoadIndex); + } + } catch (SQLException ex) { + ex.printStackTrace(); + continue; + } + } + // now pick the closest + if (beforeNafter[0] == null) { + return beforeNafter[1]; + } + if (beforeNafter[1] == null) { + return beforeNafter[0]; + } + long t1 = timeMillis-beforeNafter[0].getTimeMilliseconds(); + long t2 = beforeNafter[1].getTimeMilliseconds()-timeMillis; + if (t1 < t2) { + return beforeNafter[0]; + } + else { + return beforeNafter[1]; + } + } /** * Called when a new database is connected to read the last values back in diff --git a/src/tethys/TethysLocationFuncs.java b/src/tethys/TethysLocationFuncs.java index d3d084c1..fb624fbc 100644 --- a/src/tethys/TethysLocationFuncs.java +++ b/src/tethys/TethysLocationFuncs.java @@ -1,7 +1,19 @@ package tethys; +import Array.ArrayManager; +import Array.HydrophoneLocator; +import Array.PamArray; +import Array.Streamer; +import GPS.GPSControl; import GPS.GpsData; +import GPS.GpsDataUnit; +import PamUtils.LatLong; +import PamUtils.PamUtils; +import PamguardMVC.PamDataUnit; +import generalDatabase.DBControlUnit; +import generalDatabase.PamConnection; import nilus.Deployment; +import nilus.DeploymentRecoveryDetails; /** * Function(s) to get location information for Tethys in the required format. @@ -21,6 +33,53 @@ public class TethysLocationFuncs { */ public static void getTrackAndPositionData(Deployment deployment) { long start = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); + long end = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); + /* + * Need to load data for GPS, Hydrophones and Streamers datablocks for this time period. Can then use + * the snapshot geomentry classes to do the rest from the array manager ? + */ + boolean ok = true; + ok &= addPositionData(deployment.getDeploymentDetails()); + ok &= addPositionData(deployment.getRecoveryDetails()); + + } + + /** + * Add position data to DeploymentRecoveryDetails. + * @param drd + * @return + */ + public static boolean addPositionData(DeploymentRecoveryDetails drd) { + long timeMillis = TethysTimeFuncs.millisFromGregorianXML(drd.getAudioTimeStamp()); + LatLong pos = getLatLongData(timeMillis); + if (pos == null) { + return false; + } + drd.setLongitude(PamUtils.constrainedAngle(pos.getLongitude(), 360)); + drd.setLatitude(pos.getLatitude()); + drd.setElevationInstrumentM(pos.getHeight()); + drd.setDepthInstrumentM(-pos.getHeight()); + return true; + } + + public static LatLong getLatLongData(long timeMillis) { + // check the array time. + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + Streamer aStreamer = array.getStreamer(0); + GPSControl gpsControl = GPSControl.getGpsControl(); + PamConnection con = DBControlUnit.findConnection(); + if (gpsControl != null) { +// check GPS data are loaded for times around this. + GpsDataUnit gpsData = (GpsDataUnit) gpsControl.getGpsDataBlock().getLogging().findClosestDataPoint(con, timeMillis); + if (gpsData != null) { + return gpsData.getGpsData(); + } + } + HydrophoneLocator hydrophoneLocator = aStreamer.getHydrophoneLocator(); + if (hydrophoneLocator == null) { + return null; + } + return hydrophoneLocator.getStreamerLatLong(timeMillis); } } From 799c7b218e214c600dd3ded26ce330e5b0f0f6ff Mon Sep 17 00:00:00 2001 From: kbolaughlin Date: Mon, 13 Mar 2023 17:27:39 -0700 Subject: [PATCH 09/65] Update TethysExporter.java file importer --- src/tethys/output/TethysExporter.java | 524 ++++++++++++++------------ 1 file changed, 292 insertions(+), 232 deletions(-) diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 993e4dec..7542be23 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -2,6 +2,15 @@ package tethys.output; import java.util.ArrayList; import java.util.List; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.FileAttribute; +import java.io.StringWriter; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; import org.w3c.dom.Document; @@ -34,12 +43,19 @@ import tethys.dbxml.DBXMLConnect; import tethys.pamdata.TethysDataProvider; import tethys.pamdata.TethysSchema; +import dbxml.uploader.Importer; +import nilus.Deployment; +import nilus.MarshalXML; + /** - * Class sitting at the centre of all operations. It will talk to PAMGuard objects to get schemas and data - * and talk to the database connection to move data out (and possibly in). Eventually, a lot of the functionality - * in here will be moved to worker threads (SwingWorker?) so that it's easy to keep dialogs alive, show - * progress for big export jobs, etc. For now though, it's a relatively simple set of function which - * we can use to a) open the database, b) check everything such as schemas, etc. c) export data and d) clean up. + * Class sitting at the centre of all operations. It will talk to PAMGuard + * objects to get schemas and data and talk to the database connection to move + * data out (and possibly in). Eventually, a lot of the functionality in here + * will be moved to worker threads (SwingWorker?) so that it's easy to keep + * dialogs alive, show progress for big export jobs, etc. For now though, it's a + * relatively simple set of function which we can use to a) open the database, + * b) check everything such as schemas, etc. c) export data and d) clean up. + * * @author dg50 * */ @@ -47,7 +63,7 @@ public class TethysExporter { private TethysControl tethysControl; private TethysExportParams tethysExportParams; - + private DBXMLConnect dbxmlConnect; public TethysExporter(TethysControl tethysControl, TethysExportParams tethysExportParams) { @@ -55,248 +71,288 @@ public class TethysExporter { this.tethysExportParams = tethysExportParams; dbxmlConnect = new DBXMLConnect(tethysControl); } - + /** - * Does the work. In reality this will need an awful lot of changing, for instance - * to provide feedback to an observer class to show progress on the display. - * @return OK if success. + * Does the work. In reality this will need an awful lot of changing, for + * instance to provide feedback to an observer class to show progress on the + * display. + * + * @return OK if success. */ public boolean doExport() { - - boolean dbOK = dbxmlConnect.openDatabase(); - if (!dbOK) { - /* - * should we set up some exceptions to throw ? Can be a lot - * more informative than a simple 'false' - */ - return false; + + // boolean dbOK = dbxmlConnect.openDatabase(); + // if (!dbOK) { + /* + * should we set up some exceptions to throw ? Can be a lot more informative + * than a simple 'false' + */ + // return false; + // } + + Deployment deployment = new Deployment(); + deployment.setId("1"); + + Path tempFile = null; + try { + + JAXBContext jaxB = JAXBContext.newInstance(Deployment.class); + Marshaller marshall = jaxB.createMarshaller(); + marshall.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + StringWriter sw = new StringWriter(); + marshall.marshal(deployment, sw); + tempFile = Files.createTempFile("pamGuardToTethys", ".xml"); + Files.write(tempFile, sw.toString().getBytes()); + + String fileText = Importer.ImportFiles("http://localhost:9779", "Deployment", + new String[] { tempFile.toString() }, "", "", false); + + tempFile.toFile().deleteOnExit(); + + } catch(IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (JAXBException e) { + // TODO Auto-generated catch block + e.printStackTrace(); } - - + SnapshotGeometry arrayGeometry = findArrayGeometrey(); - + /** - * Doug populate instrument fields - may need to add a few things. Marie - * to define what we mean by instrument. - * Instrument names probably need to be added to the PAMGuard Array dialog and can - * then be extraced from there. We had some useful discussion about what constitutes - * an instrumnet in Tinas dataset where there was a deployment of 10 MARU's, but the - * files from all of them were merged into a single 10 channel wav file dataset and - * processed together for detection and localisation. Clearly this goes into a single - * Tethys database, but do we put 'MARU Array' as the instrument and then include - * serial numbers of individual MARU's with the hydrophone data, or what ? + * Doug populate instrument fields - may need to add a few things. Marie to + * define what we mean by instrument. Instrument names probably need to be added + * to the PAMGuard Array dialog and can then be extraced from there. We had some + * useful discussion about what constitutes an instrumnet in Tinas dataset where + * there was a deployment of 10 MARU's, but the files from all of them were + * merged into a single 10 channel wav file dataset and processed together for + * detection and localisation. Clearly this goes into a single Tethys database, + * but do we put 'MARU Array' as the instrument and then include serial numbers + * of individual MARU's with the hydrophone data, or what ? */ - - + /** - * Doug write something here to get most of the 'samplingdetails' schema. - * This all comes out of the Sound Acquisition module. Code below shows how - * to find this and iterate through various bits of information ... - * (I've put it in a separate function. Currently returning void,but could - * presumably return a Tethys samplingdetails document?) + * Doug write something here to get most of the 'samplingdetails' schema. This + * all comes out of the Sound Acquisition module. Code below shows how to find + * this and iterate through various bits of information ... (I've put it in a + * separate function. Currently returning void,but could presumably return a + * Tethys samplingdetails document?) */ - + /* * A load of notes Katie put in ....654654654 */ - //1. grab DeploymentRecoveryPair that has deployment details and recovery details - //a. this is based on start and end times - //Douglas calculates out dutycycles to only grab the - - //2. loop through the pairs to populate the extra information - //one pair is one deployment - //see below for matching - - - //id => unique - //project => project in pamguard - //deploymentId == id - //deploymentAlias => blank - //site => UI addition in pamguard, not done, can be blank - //siteAlias => blank - //cruise => UI addition, optional - //Platform=> UI addition in pamguard - //region => UI addition - //Instrument/Type => UI, array manager details (hydrophone names area) - //Instrument/Id => UI, array manager details - //Instrument/Geometry => in pamguard array manager - //SamplingDetails/Channel - //ChannelNumber => in pamguard, hyrdrophone array - //SensorNumber => in pamguard, - //Start => same as timestamp deployment detail - //End => same as timestamp recovery detail - //Sampling/Regimen (change sample rate, pamgauard doesnt handle, only on, get channel info in that loop) - //TimeStamp => start time - //SampleRate_kHz => - //SampleBits => - //Gain (another func call to get gain info) - //DutyCycles => needs to be calculated, not fields in pamguard, have fun Douglas - //QualityAssurance => not in pamguard, UI, maybe deployment notes, optional - //Data/Audio (static) - //URI => folder where audio is saved - //Data/Tracks - //Track => GPS datatable (granularity filter) - //TrackId => not unique between deployments, - //TrackEffort - //OnPath => scattered throughout pamguard - //URI => option, check with Shannon on how they are doing deployments - //Sensors/Audio (per hydrophone not quad array) streamer info + individual hydrophone data together - //pamguard hydrophone data - //number => hydrophoneId - //sensorId => sensor serial number - //Geometry => array geometry field goes to - //Sensors/Depth - //optional - //Sensors/Sensor - //Number => hydrophoneId in pamguard - //SensorId => addition to UI - //Geometry => array geometry fields - //Type => Hydrophone Type - - - - - - //get list of deployment recovery details (start, stop times and lat/long) - //deployment details and recovery details are same structure - //per pair, go through a loop to fill in each deployment - ArrayList deployRecover = getSamplingDetails(); - if (deployRecover == null) { - return false; - } - - for (DeploymentRecoveryPair drd : deployRecover) { - - - - - } - + // 1. grab DeploymentRecoveryPair that has deployment details and recovery + // details + // a. this is based on start and end times + // Douglas calculates out dutycycles to only grab the + + // 2. loop through the pairs to populate the extra information + // one pair is one deployment + // see below for matching + + // id => unique + // project => project in pamguard + // deploymentId == id + // deploymentAlias => blank + // site => UI addition in pamguard, not done, can be blank + // siteAlias => blank + // cruise => UI addition, optional + // Platform=> UI addition in pamguard + // region => UI addition + // Instrument/Type => UI, array manager details (hydrophone names area) + // Instrument/Id => UI, array manager details + // Instrument/Geometry => in pamguard array manager + // SamplingDetails/Channel + // ChannelNumber => in pamguard, hyrdrophone array + // SensorNumber => in pamguard, + // Start => same as timestamp deployment detail + // End => same as timestamp recovery detail + // Sampling/Regimen (change sample rate, pamgauard doesnt handle, only on, get + // channel info in that loop) + // TimeStamp => start time + // SampleRate_kHz => + // SampleBits => + // Gain (another func call to get gain info) + // DutyCycles => needs to be calculated, not fields in pamguard, have fun + // Douglas + // QualityAssurance => not in pamguard, UI, maybe deployment notes, optional + // Data/Audio (static) + // URI => folder where audio is saved + // Data/Tracks + // Track => GPS datatable (granularity filter) + // TrackId => not unique between deployments, + // TrackEffort + // OnPath => scattered throughout pamguard + // URI => option, check with Shannon on how they are doing deployments + // Sensors/Audio (per hydrophone not quad array) streamer info + individual + // hydrophone data together + // pamguard hydrophone data + // number => hydrophoneId + // sensorId => sensor serial number + // Geometry => array geometry field goes to + // Sensors/Depth + // optional + // Sensors/Sensor + // Number => hydrophoneId in pamguard + // SensorId => addition to UI + // Geometry => array geometry fields + // Type => Hydrophone Type + + // get list of deployment recovery details (start, stop times and lat/long) + // deployment details and recovery details are same structure + // per pair, go through a loop to fill in each deployment +// ArrayList deployRecover = getSamplingDetails(); +// if (deployRecover == null) { +// return false; +// } +// +// for (DeploymentRecoveryPair drd : deployRecover) { +// +// +// +// +// } + /* * Call some general export function */ exportGeneralData(tethysExportParams); /* - * go through the export params and call something for every - * data block that's enabled. + * go through the export params and call something for every data block that's + * enabled. */ ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); for (PamDataBlock aDataBlock : allDataBlocks) { StreamExportParams streamExportParams = tethysExportParams.getStreamParams(aDataBlock); if (streamExportParams == null || streamExportParams.selected == false) { - continue; // not interested in this one. + continue; // not interested in this one. } exportDataStream(aDataBlock, tethysExportParams, streamExportParams); } /* - * Then do whatever else is needed to complete the document. + * Then do whatever else is needed to complete the document. */ - + dbxmlConnect.closeDatabase(); - + return true; } - + /** - * find Deployment data. This is stored in a separate PAMGuard module, which may not - * be present. - * @return + * find Deployment data. This is stored in a separate PAMGuard module, which may + * not be present. + * + * @return */ public DeploymentData findDeploymentData() { /** - * What to do if this isn't present or is incomplete ? Should we be showing this in the - * main export dialog at some point ? More a Q for when we make a nicer UI later in the project. + * What to do if this isn't present or is incomplete ? Should we be showing this + * in the main export dialog at some point ? More a Q for when we make a nicer + * UI later in the project. */ - MetaDataContol metaControl = (MetaDataContol) PamController.getInstance().findControlledUnit(MetaDataContol.unitType); + MetaDataContol metaControl = (MetaDataContol) PamController.getInstance() + .findControlledUnit(MetaDataContol.unitType); if (metaControl == null) { return null; - } - else { + } else { return metaControl.getDeploymentData(); } } - + public SnapshotGeometry findArrayGeometrey() { /* - * this should never be null, but again, we might want to put some warnings - * and exception handlers in here anyway. Really just an example to show how - * to find this. We'll need to dig a bit elsewhere to get more detailed - * hydrophone information. + * this should never be null, but again, we might want to put some warnings and + * exception handlers in here anyway. Really just an example to show how to find + * this. We'll need to dig a bit elsewhere to get more detailed hydrophone + * information. */ /* - * In PAMGuard hydrophones are assigned to streamers, which can have different methods for estimating - * their positions from GPS. The geometry is a sum of xyz in the streamer and xyz in the hydrophone object - * Within a streamer, hydrophones are considered rigid relative to each other. The stremer will floow - * a choice of modesl (rigid, threading, etc) to estimate it's position relative to the GPS track. Different - * errors are used when estimating localisation errors within and between streamers. - * The Snapshot geometry sorts a lot of this out for a point in time and will give back a single object - * which is most of what we'll be wanting. + * In PAMGuard hydrophones are assigned to streamers, which can have different + * methods for estimating their positions from GPS. The geometry is a sum of xyz + * in the streamer and xyz in the hydrophone object Within a streamer, + * hydrophones are considered rigid relative to each other. The stremer will + * floow a choice of modesl (rigid, threading, etc) to estimate it's position + * relative to the GPS track. Different errors are used when estimating + * localisation errors within and between streamers. The Snapshot geometry sorts + * a lot of this out for a point in time and will give back a single object + * which is most of what we'll be wanting. */ PamArray currentArray = ArrayManager.getArrayManager().getCurrentArray(); SnapshotGeometry currentGeometry = currentArray.getSnapshotGeometry(PamCalendar.getTimeInMillis()); /* - * The following lines of code show how to get more detailed calibration info for each - * hydrophone, but we'll have to think about the easiest way to repackage this for Tethys. - * e.g. this function could be modified to return the correct Tethys object in one go. + * The following lines of code show how to get more detailed calibration info + * for each hydrophone, but we'll have to think about the easiest way to + * repackage this for Tethys. e.g. this function could be modified to return the + * correct Tethys object in one go. */ ArrayList hydrophones = currentArray.getHydrophoneArray(); /* - * each object in the list will have more detailed cal information for each phone. But - * for the full system calibration we'd need to go to the Acquisition module. + * each object in the list will have more detailed cal information for each + * phone. But for the full system calibration we'd need to go to the Acquisition + * module. */ - AcquisitionControl daqControl = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.unitType); + AcquisitionControl daqControl = (AcquisitionControl) PamController.getInstance() + .findControlledUnit(AcquisitionControl.unitType); if (daqControl != null) { AcquisitionProcess daqProcess = daqControl.getAcquisitionProcess(); for (int iPhone = 0; iPhone < hydrophones.size(); iPhone++) { - Hydrophone aPhone = hydrophones.get(iPhone); + Hydrophone aPhone = hydrophones.get(iPhone); double totalCal = -daqProcess.rawAmplitude2dB(1, iPhone, false); - System.out.printf("hydrophone %d has sensitivity %3.1fdB + gain %3.1fdB. Total calibration is %3.1fdB re1U/uPa\n", + System.out.printf( + "hydrophone %d has sensitivity %3.1fdB + gain %3.1fdB. Total calibration is %3.1fdB re1U/uPa\n", iPhone, aPhone.getSensitivity(), aPhone.getPreampGain(), totalCal); } } - - + return currentGeometry; } - - //in each channel + + // in each channel private ArrayList getSamplingDetails() { - // first find an acquisition module. + // first find an acquisition module. PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); if (aModule instanceof AcquisitionControl == false) { - // will return if it's null. Impossible for it to be the wrong type. - // but it's good practice to check anyway before casting. + // 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. + // 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 ! + * 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); + // 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. + * 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 + // just load everything. Probably OK for the acqusition, but will bring down daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null); ArrayList allStatusData = daqInfoDataBlock.getDataCopy(); if (allStatusData != null && allStatusData.size() > 0) { long dataStart = Long.MAX_VALUE; long dataEnd = Long.MIN_VALUE; // find the number of times it started and stopped .... - int nStart = 0, nStop = 0, nFile=0; + int nStart = 0, nStop = 0, nFile = 0; for (DaqStatusDataUnit daqStatus : allStatusData) { switch (daqStatus.getStatus()) { case "Start": @@ -312,12 +368,14 @@ public class TethysExporter { 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); - + + 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) { @@ -333,24 +391,24 @@ public class TethysExporter { // */ //// for () // } - + return null; - + } - /** - * No idea if we need this or not. May want to return something different to void, e.g. - * a reference to the main object for a tethys export. I've no idea ! + * No idea if we need this or not. May want to return something different to + * void, e.g. a reference to the main object for a tethys export. I've no idea ! + * * @param tethysExportParams2 */ private void exportGeneralData(TethysExportParams tethysExportParams) { // TODO Auto-generated method stub - + } /** - * Here is where we export data for a specific data stream to Tethys. + * Here is where we export data for a specific data stream to Tethys. * * @param aDataBlock * @param tethysExportParams @@ -359,100 +417,102 @@ public class TethysExporter { private void exportDataStream(PamDataBlock aDataBlock, TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { /** - * This will probably need to be passed additional parameters and may also want to return something - * other than void in order to build a bigger Tethys document. + * This will probably need to be passed additional parameters and may also want + * to return something other than void in order to build a bigger Tethys + * document. */ /* - * Some examples of how to do whatever is needed to get schema and data out of PAMGuard. + * Some examples of how to do whatever is needed to get schema and data out of + * PAMGuard. */ /* - * first we'll probably want a reference to the module containing the data. - * in principle this can't get null, since the datablock was found be searching in - * the other direction. + * first we'll probably want a reference to the module containing the data. in + * principle this can't get null, since the datablock was found be searching in + * the other direction. */ PamControlledUnit pamControlledUnit = aDataBlock.getParentProcess().getPamControlledUnit(); - + TethysDataProvider dataProvider = aDataBlock.getTethysDataProvider(); PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); - + if (dataProvider == null) { return; } - + TethysSchema tethysSchema = dataProvider.getSchema(); - /* - * the schema should have a Document object in it. If we wanted to turn - * that into an XML string we can ... - * (though I'd assume that having the Document is more useful) + /* + * the schema should have a Document object in it. If we wanted to turn that + * into an XML string we can ... (though I'd assume that having the Document is + * more useful) */ String schemaXMLString = pamXMLWriter.getAsString(tethysSchema.getXsd(), false); System.out.printf("Schema for %s is %s\n", aDataBlock.getDataName(), schemaXMLString); - - + /* - * Get the XML settings for that datablock. This is (or should be - * the parameters that were controlling that module, with adequate data about - * upstream modules). I think this has to go somewhere into the Detections document. + * Get the XML settings for that datablock. This is (or should be the parameters + * that were controlling that module, with adequate data about upstream + * modules). I think this has to go somewhere into the Detections document. */ Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); String moduleXML = null; if (doc != null) { - // this string should be XML of all the settings for the module controlling this datablock. + // this string should be XML of all the settings for the module controlling this + // datablock. moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml System.out.printf("Module settings for datablock %s are:\n", moduleXML); System.out.println(moduleXML); } - - - /** - * Now can go through the data. Probably, we'll want to go through all the data in - * the project, but we can hold off on that for now and just go for data that - * are in memory. We'll also have to think a lot about updating parts of the - * database which have been reprocessed - what we want to do, should eventually all - * be options set in the dialog and available within TethysExportParams - * For now though, we're just going to export data that are in memory. - * Once basic export is working, I can easily enough write something which will go - * through an entire data set, go through between two times, etc. + * Now can go through the data. Probably, we'll want to go through all the data + * in the project, but we can hold off on that for now and just go for data that + * are in memory. We'll also have to think a lot about updating parts of the + * database which have been reprocessed - what we want to do, should eventually + * all be options set in the dialog and available within TethysExportParams For + * now though, we're just going to export data that are in memory. Once basic + * export is working, I can easily enough write something which will go through + * an entire data set, go through between two times, etc. */ - // so this is a way of iterating through the data that are in memory, which will do for now .. - // do it with a data copy which can avoid synchronising the entire block for what may be a long time - // the copy function is itself synched, and is quite fast, so easier and safe this way + // so this is a way of iterating through the data that are in memory, which will + // do for now .. + // do it with a data copy which can avoid synchronising the entire block for + // what may be a long time + // the copy function is itself synched, and is quite fast, so easier and safe + // this way ArrayList dataCopy = aDataBlock.getDataCopy(); DataSelector dataSelector = aDataBlock.getDataSelector(tethysControl.getDataSelectName(), false); int nSkipped = 0; int nExport = 0; - + for (PamDataUnit aData : dataCopy) { /* - * see if we want this data unit. PAMGuard has a complicated system of data selectors specific to - * each data type. These are centrally managed so you don't need to worry too much about them. They - * are identified by name for each data stream and the behaviour here should follow the selections you - * made in the dialog. - * the data selection system allows different displays to show different data, so a stream can have many differently - * named selectors active at any one time, all doing different things in different parts of PAMGuard. + * see if we want this data unit. PAMGuard has a complicated system of data + * selectors specific to each data type. These are centrally managed so you + * don't need to worry too much about them. They are identified by name for each + * data stream and the behaviour here should follow the selections you made in + * the dialog. the data selection system allows different displays to show + * different data, so a stream can have many differently named selectors active + * at any one time, all doing different things in different parts of PAMGuard. */ if (dataSelector != null) { if (dataSelector.scoreData(aData) <= 0) { nSkipped++; - continue; // don't want this one. + continue; // don't want this one. } } - + /* - * then we do whatever we need to do to convert this into something for Tethys - * this might happen in the tethysSchema object for each data stream ???? + * then we do whatever we need to do to convert this into something for Tethys + * this might happen in the tethysSchema object for each data stream ???? */ - - - nExport ++; + + nExport++; } - - System.out.printf("Exported %d data units and skipped %d in %s", nExport, nSkipped, aDataBlock.getLongDataName()); - - + + System.out.printf("Exported %d data units and skipped %d in %s", nExport, nSkipped, + aDataBlock.getLongDataName()); + } } From 2dd870956ac9ef2da180f4e83f81e8158d53d0e6 Mon Sep 17 00:00:00 2001 From: kbolaughlin Date: Tue, 14 Mar 2023 08:40:49 -0700 Subject: [PATCH 10/65] Update TethysExporter.java small fix --- src/tethys/output/TethysExporter.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 96dc5c71..7a419c1e 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -93,8 +93,8 @@ public class TethysExporter { // return false; // } - Deployment deployment = new Deployment(); - deployment.setId("1"); + Deployment deployment1 = new Deployment(); + deployment1.setId("1"); Path tempFile = null; try { @@ -103,7 +103,7 @@ public class TethysExporter { Marshaller marshall = jaxB.createMarshaller(); marshall.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); StringWriter sw = new StringWriter(); - marshall.marshal(deployment, sw); + marshall.marshal(deployment1, sw); tempFile = Files.createTempFile("pamGuardToTethys", ".xml"); Files.write(tempFile, sw.toString().getBytes()); From 103c050d0c659970a355bb595ec91b414596470a Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 14 Mar 2023 17:28:13 +0000 Subject: [PATCH 11/65] Trying to Merge with KB Changes --- src/Array/Hydrophone.java | 2 +- src/tethys/deployment/DeploymentHandler.java | 429 +++++++++++++++++ src/tethys/deployment/DeploymentOverview.java | 50 ++ .../deployment/DeploymentRecoveryPair.java | 11 + src/tethys/deployment/DutyCycleInfo.java | 5 + src/tethys/deployment/RecordingPeriod.java | 36 ++ src/tethys/output/TethysExporter.java | 453 ++++++------------ 7 files changed, 680 insertions(+), 306 deletions(-) create mode 100644 src/tethys/deployment/DeploymentHandler.java create mode 100644 src/tethys/deployment/DeploymentOverview.java create mode 100644 src/tethys/deployment/DeploymentRecoveryPair.java create mode 100644 src/tethys/deployment/DutyCycleInfo.java create mode 100644 src/tethys/deployment/RecordingPeriod.java diff --git a/src/Array/Hydrophone.java b/src/Array/Hydrophone.java index ffbda1ac..d726f0b8 100644 --- a/src/Array/Hydrophone.java +++ b/src/Array/Hydrophone.java @@ -333,7 +333,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters { /** * @return Returns the coordinate. */ - protected double[] getCoordinates() { + public double[] getCoordinates() { return Arrays.copyOf(coordinate,3); } diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java new file mode 100644 index 00000000..9b1301f2 --- /dev/null +++ b/src/tethys/deployment/DeploymentHandler.java @@ -0,0 +1,429 @@ +package tethys.deployment; + +import java.io.Serializable; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import javax.xml.bind.JAXBException; + +import org.apache.commons.beanutils.converters.BigIntegerConverter; + +import Acquisition.AcquisitionControl; +import Acquisition.AcquisitionParameters; +import Acquisition.DaqStatusDataUnit; +import Acquisition.DaqSystem; +import Array.ArrayManager; +import Array.Hydrophone; +import Array.PamArray; +import PamController.PamControlledUnit; +import PamController.PamController; +import PamUtils.PamCalendar; +import PamUtils.PamUtils; +import PamguardMVC.PamDataBlock; +import nilus.Audio; +import nilus.ChannelInfo; +import nilus.ChannelInfo.Sampling; +import nilus.ChannelInfo.Sampling.Regimen; +import nilus.Deployment; +import nilus.Deployment.SamplingDetails; +import nilus.Deployment.Sensors; +import nilus.DeploymentRecoveryDetails; +import nilus.GeometryTypeM; +import nilus.Helper; +import nilus.MarshalXML; +import pamMaths.PamVector; +import tethys.TethysLocationFuncs; +import tethys.TethysTimeFuncs; + +/** + * Functions to gather data for the deployment document from all around PAMGuard. + * @author dg50 + * + */ +public class DeploymentHandler { + + + /** + * Get an overview of all the deployments. + * @return + */ + public DeploymentOverview createOverview() { + // first find an acquisition module. + PamControlledUnit aModule = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); + if (aModule instanceof AcquisitionControl == false) { + // 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(); + + ArrayList tempPeriods = null; + + if (allStatusData == null || allStatusData.size() == 0) { + System.out.println("Data appear to have no logged recording periods. Try to extract from raw audio ..."); + tempPeriods = extractTimesFromFiles(daqControl); + } + else { + tempPeriods = extractTimesFromStatus(allStatusData); + } + if (tempPeriods == null || tempPeriods.size() == 0) { + System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); + } + int nPeriods = tempPeriods.size(); + // now go through those and merge into longer periods where there is no gap between files. + ListIterator iterator = tempPeriods.listIterator(); + RecordingPeriod prevPeriod = null; + while (iterator.hasNext()) { + RecordingPeriod nextPeriod = iterator.next(); + long nextDur = nextPeriod.getRecordStop()-nextPeriod.getRecordStart(); + if (nextDur == 0) { + continue; + } + if (prevPeriod != null) { + long gap = nextPeriod.getRecordStart() - prevPeriod.getRecordStop(); + long prevDur = prevPeriod.getRecordStop()-prevPeriod.getRecordStart(); + if (gap < 3 || 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(); + nextPeriod = prevPeriod; + } + } + prevPeriod = nextPeriod; + } + 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); + return deploymentOverview; + // find the number of times it started and stopped .... +// System.out.printf("Input map of sound data indicates data from %s to %s with %d starts and %d stops over %d files\n", +// PamCalendar.formatDateTime(dataStart), PamCalendar.formatDateTime(dataEnd), nStart, nStop, nFile+1); + // now work out where there are genuine gaps and make up a revised list of recording periods. + + + } + + /** + * Work out whether or not the data are evenly duty cycled by testing the + * distributions of on and off times. + * @param tempPeriods + * @return + */ + private DutyCycleInfo assessDutyCycle(ArrayList tempPeriods) { + int n = tempPeriods.size(); + if (n < 2) { + return null; + } + 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; i++) { + ons[i] = tempPeriods.get(i).getDuration(); + } + return null; + } + + + private ArrayList extractTimesFromStatus(ArrayList allStatusData) { + ArrayList tempPeriods = new ArrayList<>(); + long dataStart = Long.MAX_VALUE; + long dataEnd = Long.MIN_VALUE; + Long lastStart = null; + int nStart = 0; + int nStop = 0; + int nFile = 0; + for (DaqStatusDataUnit daqStatus : allStatusData) { + switch (daqStatus.getStatus()) { + case "Start": + nStart++; + dataStart = Math.min(dataStart, daqStatus.getTimeMilliseconds()); + lastStart = daqStatus.getTimeMilliseconds(); + break; + case "Stop": + nStop++; + dataEnd = Math.max(dataEnd, daqStatus.getEndTimeInMilliseconds()); + long lastEnd = daqStatus.getEndTimeInMilliseconds(); + if (lastStart != null) { + tempPeriods.add(new RecordingPeriod(lastStart, lastEnd)); + } + lastStart = null; + break; + case "NextFile": + nFile++; + break; + } + } + return tempPeriods; + } + + private ArrayList extractTimesFromFiles(AcquisitionControl daqControl) { + // 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 == false) { + // 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; + + } + + public Deployment createDeploymentDocument(int i, DeploymentRecoveryPair drd) { + Deployment deployment = new Deployment(); + String id = String.format("%d", i); + deployment.setId(id); + deployment.setDeploymentId(i); + deployment.setDeploymentDetails(drd.deploymentDetails); + deployment.setRecoveryDetails(drd.recoveryDetails); + + TethysLocationFuncs.getTrackAndPositionData(deployment); + + getSamplingDetails(deployment); + + getSensorDetails(deployment); + + /** + * Stuff that may need to be put into the UI: + * Audio: can easily get current loc of raw and binary data, but may need to override these. I think + * this may be for the export UI ? + * Tracks: trackline information. General problem in PAMGUard. + */ + + + + return deployment; + } + + private boolean getSensorDetails(Deployment deployment) { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + Sensors sensors = new Sensors(); + List - + + diff --git a/src/PamController/settings/output/xml/PamguardXMLWriter.java b/src/PamController/settings/output/xml/PamguardXMLWriter.java index 7058241b..b943f2d6 100644 --- a/src/PamController/settings/output/xml/PamguardXMLWriter.java +++ b/src/PamController/settings/output/xml/PamguardXMLWriter.java @@ -29,6 +29,7 @@ import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; import com.sun.javafx.runtime.VersionInfo; @@ -48,6 +49,7 @@ import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamProcess; import binaryFileStorage.BinaryStore; +import tethys.TethysControl; /** * Class for writing XML configuration output to a file. @@ -62,6 +64,7 @@ public class PamguardXMLWriter implements PamSettings { private static final Set> WRAPPER_TYPES = getWrapperTypes(); private XMLWriterSettings writerSettings = new XMLWriterSettings(); +// private String xmlNameSpace; private static PamguardXMLWriter singleInstance; @@ -83,6 +86,19 @@ public class PamguardXMLWriter implements PamSettings { } return singleInstance; } + + /** + * Recursively walk the tree and add a namespace to every + * single element. + * @param doc + * @param nameSpace + * @return + */ + public boolean addNameSpaceToElements(Document doc, Element el, String nameSpace) { +// el.setAttributeNS(nameSpace, nameSpace, nameSpace); + NamedNodeMap attributes = el.getAttributes(); + return true; + } /** * Make a document with the options specified in writerSettings. @@ -967,5 +983,9 @@ public class PamguardXMLWriter implements PamSettings { return true; } +// public void setStaticNameSpace(String xmlNameSpace) { +// this.xmlNameSpace = xmlNameSpace; +// } + } diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 2c88e3d5..2e584354 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -30,6 +30,7 @@ public class TethysControl extends PamControlledUnit { 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(); diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 3f752d55..8ba36fdd 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -27,6 +27,7 @@ import nilus.Deployment; import nilus.DescriptionType; import nilus.Detection; import nilus.SpeciesIDType; +import tethys.TethysControl; import tethys.TethysTimeFuncs; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; @@ -105,10 +106,12 @@ public class AutoTethysProvider implements TethysDataProvider { if (settingsObjs == null) { return null; } +// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); if (settingsEl == null) { return null; } + pamXMLWriter.addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); dummyEl.appendChild(settingsEl); NodeList childs = settingsEl.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { From 7f680f3e20deb0107a3c89f3b485391032fa2f50 Mon Sep 17 00:00:00 2001 From: kbolaughlin Date: Thu, 16 Mar 2023 09:13:42 -0700 Subject: [PATCH 22/65] fixed some deploymentData defaulting fixed some deploymentData defaulting --- src/tethys/TethysControl.java | 30 +++++++++++++++++ src/tethys/deployment/DeploymentHandler.java | 34 ++++++++++++-------- src/tethys/detection/DetectionsHandler.java | 7 ++-- src/tethys/output/TethysExporter.java | 2 +- src/tethys/pamdata/AutoTethysProvider.java | 14 +++++++- 5 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 2c88e3d5..fb22e7a1 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -11,6 +11,9 @@ import javax.swing.JMenuItem; import PamController.PamControlledUnit; import PamController.PamController; import PamguardMVC.PamDataBlock; +import metadata.MetaDataContol; +import metadata.deployment.DeploymentData; +import nilus.Deployment.Instrument; import tethys.dbxml.DBXMLConnect; //import nilus.Deployment; //import nilus.Deployment.Instrument; @@ -94,6 +97,33 @@ public class TethysControl extends PamControlledUnit { tethysExporter.doExport(); } + public DeploymentData getGlobalDeplopymentData() { + PamControlledUnit aUnit = PamController.getInstance().findControlledUnit(MetaDataContol.class, null); +// if (aUnit instanceof MetaDataContol == false || true) { +// deployment.setProject("thisIsAProject"); +// deployment.setPlatform("Yay a platform"); +// Instrument instrument = new Instrument(); +// instrument.setType("machiney"); +// instrument.setInstrumentId("12345555"); +// deployment.setInstrument(instrument); +// return false; +// } + + MetaDataContol metaControl = (MetaDataContol) aUnit; + DeploymentData deploymentData = metaControl != null ? metaControl.getDeploymentData() : new DeploymentData(); + + 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; + } + /** * A name for any deta selectors. * @return diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index fdda96e1..2bcdf6b2 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -37,6 +37,7 @@ import nilus.DeploymentRecoveryDetails; import nilus.GeometryTypeM; import nilus.Helper; import pamMaths.PamVector; +import tethys.TethysControl; import tethys.TethysLocationFuncs; import tethys.TethysTimeFuncs; @@ -46,8 +47,15 @@ import tethys.TethysTimeFuncs; * */ public class DeploymentHandler { + + private TethysControl tethysControl; + public DeploymentHandler(TethysControl tethysControl) { + super(); + this.tethysControl = tethysControl; + } + /** * Get an overview of all the deployments. * @return @@ -329,19 +337,19 @@ public class DeploymentHandler { * @param deployment */ private boolean getProjectData(Deployment deployment) { - PamControlledUnit aUnit = PamController.getInstance().findControlledUnit(MetaDataContol.class, null); - if (aUnit instanceof MetaDataContol == false || true) { - deployment.setProject("thisIsAProject"); - deployment.setPlatform("Yay a platform"); - Instrument instrument = new Instrument(); - instrument.setType("machiney"); - instrument.setInstrumentId("12345555"); - deployment.setInstrument(instrument); - return false; - } - - MetaDataContol metaControl = (MetaDataContol) aUnit; - DeploymentData deploymentData = metaControl.getDeploymentData(); +// PamControlledUnit aUnit = PamController.getInstance().findControlledUnit(MetaDataContol.class, null); +// if (aUnit instanceof MetaDataContol == false || true) { +// deployment.setProject("thisIsAProject"); +// deployment.setPlatform("Yay a platform"); +// Instrument instrument = new Instrument(); +// instrument.setType("machiney"); +// instrument.setInstrumentId("12345555"); +// deployment.setInstrument(instrument); +// return false; +// } +// +// MetaDataContol metaControl = (MetaDataContol) aUnit; + DeploymentData deploymentData = tethysControl.getGlobalDeplopymentData(); deployment.setProject(deploymentData.getProject()); deployment.setDeploymentAlias(deploymentData.getDeploymentAlias()); deployment.setSite(deploymentData.getSite()); diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 002f920d..0b6d117b 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -29,6 +29,9 @@ public class DetectionsHandler { private TethysControl tethysControl; + public int uniqueDetectionsId; + public int uniqueDetectionId; + public DetectionsHandler(TethysControl tethysControl) { super(); this.tethysControl = tethysControl; @@ -142,7 +145,7 @@ public class DetectionsHandler { */ TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); Detections detections = new Detections(); - detections.setId(deployment.getId()); + detections.setId(String.format("%d", uniqueDetectionsId++)); detections.setDescription(dataProvider.getDescription(deployment, tethysExportParams)); DataSourceType dataSource = new DataSourceType(); dataSource.setDeploymentId(deployment.getId()); @@ -156,7 +159,7 @@ public class DetectionsHandler { List detectionList = detectionGroup.getDetection(); for (int i = 0; i < data.size(); i++) { PamDataUnit dataUnit = data.get(i); - Detection detection = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); + Detection detection = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); if (detection != null) { detectionList.add(detection); } diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 52554f0a..2760099c 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -168,7 +168,7 @@ 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(); + DeploymentHandler deploymentHandler = new DeploymentHandler(tethysControl); ArrayList deployRecover = deploymentHandler.getDeployments(); if (deployRecover == null) { diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 82952665..88dd6266 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -82,10 +82,22 @@ public class AutoTethysProvider implements TethysDataProvider { @Override public AlgorithmType getAlgorithm() { AlgorithmType algorithm = new AlgorithmType(); + try { + nilus.Helper.createRequiredElements(algorithm); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } algorithm.setMethod(this.getAlgorithmMethod()); algorithm.setSoftware("PAMGuard"); algorithm.setVersion(PamguardVersionInfo.version); - algorithm.setParameters(this.getAlgorithmParameters()); + //algorithm.setParameters(this.getAlgorithmParameters()); return algorithm; } From cff6f71881550da29bda0334c997e423e6440a5d Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:34:03 +0000 Subject: [PATCH 23/65] update autoprovider --- src/tethys/dbxml/DMXMLQueryTest.java | 22 +++ src/tethys/pamdata/AutoTethysProvider.java | 157 ++++++++++++++++----- 2 files changed, 140 insertions(+), 39 deletions(-) create mode 100644 src/tethys/dbxml/DMXMLQueryTest.java diff --git a/src/tethys/dbxml/DMXMLQueryTest.java b/src/tethys/dbxml/DMXMLQueryTest.java new file mode 100644 index 00000000..27ce0d06 --- /dev/null +++ b/src/tethys/dbxml/DMXMLQueryTest.java @@ -0,0 +1,22 @@ +package tethys.dbxml; + +import dbxml.JerseyClient; +import tethys.output.TethysExportParams; + +public class DMXMLQueryTest { + + public static void main(String[] args) { + new DMXMLQueryTest().runTest(); + } + + private void runTest() { + TethysExportParams params = new TethysExportParams(); + + JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); + + // web browse to http://localhost:9779/Client + + + } + +} diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 51eb9c1f..681a8913 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -32,6 +32,15 @@ import tethys.TethysTimeFuncs; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import whistleClassifier.WhistleContour; +import javax.xml.transform.*; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.IOException; +import java.io.StringReader; +import java.net.URISyntaxException; /** * Automatically provides Tethys data based on the SQL database interface @@ -76,7 +85,7 @@ public class AutoTethysProvider implements TethysDataProvider { description.setAbstract(fullUnitName); description.setObjectives(fullUnitName); description.setMethod(pamControlledUnit.getUnitType()); - + return description; } @@ -87,7 +96,7 @@ public class AutoTethysProvider implements TethysDataProvider { algorithm.setSoftware("PAMGuard"); algorithm.setVersion(PamguardVersionInfo.version); algorithm.setParameters(this.getAlgorithmParameters()); - + return algorithm; } @@ -106,59 +115,129 @@ public class AutoTethysProvider implements TethysDataProvider { if (settingsObjs == null) { return null; } -// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); + // pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); if (settingsEl == null) { return null; } - pamXMLWriter.addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); + + settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); + + dummyEl.appendChild(settingsEl); NodeList childs = settingsEl.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node el = childs.item(i); -// System.out.println(el.getNodeName()); + // System.out.println(el.getNodeName()); if (el instanceof Element) { paramList.add((Element) el); } } -// Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); -// String moduleXML = null; + // Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); + // String moduleXML = null; if (doc != null) { // this string should be XML of all the settings for the module controlling this // datablock. -// moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml -// System.out.printf("Module settings for datablock %s are:\n", moduleXML); -// System.out.println(moduleXML); -// Element pamguard = doc.get("PAMGUARD"); -// Element modules = (Element) pamguard.getElementsByTagName("MODULES"); -// doc.get -// NodeList childs = doc.getChildNodes(); -// for (int i = 0; i < childs.getLength(); i++) { -// Node el = childs.item(i); -// System.out.println(el.getNodeName()); -// if (el instanceof Element) { -// paramList.add((Element) el); -// } -// } -// String moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml -// System.out.printf("Module settings for datablock %s are:\n%s", this.pamDataBlock.getDataName(), moduleXML); + // moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml + // System.out.printf("Module settings for datablock %s are:\n", moduleXML); + // System.out.println(moduleXML); + // Element pamguard = doc.get("PAMGUARD"); + // Element modules = (Element) pamguard.getElementsByTagName("MODULES"); + // doc.get + // NodeList childs = doc.getChildNodes(); + // for (int i = 0; i < childs.getLength(); i++) { + // Node el = childs.item(i); + // System.out.println(el.getNodeName()); + // if (el instanceof Element) { + // paramList.add((Element) el); + // } + // } + // String moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml + // System.out.printf("Module settings for datablock %s are:\n%s", this.pamDataBlock.getDataName(), moduleXML); } - -// // try the old say -// Document doc2 = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); -// String moduleXML = null; -// if (doc2 != null) { -// // this string should be XML of all the settings for the module controlling this -// // datablock. -// moduleXML = pamXMLWriter.getAsString(doc2, true); // change to false to get smaller xml -// System.out.printf("Module settings for datablock %s are:\n%s", pamDataBlock.getDataName(),moduleXML); -// } -// - + + // // try the old say + // Document doc2 = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); + // String moduleXML = null; + // if (doc2 != null) { + // // this string should be XML of all the settings for the module controlling this + // // datablock. + // moduleXML = pamXMLWriter.getAsString(doc2, true); // change to false to get smaller xml + // System.out.printf("Module settings for datablock %s are:\n%s", pamDataBlock.getDataName(),moduleXML); + // } + // + return parameters; } - + + private Element addNameSpaceToElements(Document doc, Element settingsEl, String xmlNameSpace) { + + +// String xsltString = "\r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + " \r\n" +// + "\r\n"; + String xsltString = "\n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + " \n" + + "\n"; + try { + TransformerFactory factory = TransformerFactory.newInstance(); +// Source xslt = new StreamSource(new File("transform.xslt")); + StringReader reader = new StringReader(xmlNameSpace); + Source xslt = new StreamSource(reader); + + Transformer transformer = factory.newTransformer(xslt); + + DOMSource source = new DOMSource(doc); + +// Result +// Source text = new StreamSource(new File("input.xml")); + DOMResult result = new DOMResult(); + transformer.transform(source, result); + + System.out.println(result.toString()); + } + catch (Exception e) { + e.printStackTrace(); + } + + return null; + } + private PamSettings[] getSettingsObjects() { if (pamControlledUnit instanceof PamSettings) { PamSettings[] settings = new PamSettings[1]; @@ -188,7 +267,7 @@ public class AutoTethysProvider implements TethysDataProvider { * NOTE: I use channel bitmaps throughout since detections are often made on multiple channels. */ detection.setChannel(BigInteger.valueOf(dataUnit.getChannelBitmap())); - + nilus.Detection.Parameters detParams = new nilus.Detection.Parameters(); detection.setParameters(detParams); double[] freqs = dataUnit.getFrequency(); @@ -198,9 +277,9 @@ public class AutoTethysProvider implements TethysDataProvider { } double ampli = dataUnit.getAmplitudeDB(); detParams.setReceivedLevelDB(ampli); -// DataUnitBaseData basicData = dataUnit.getBasicData(); + // DataUnitBaseData basicData = dataUnit.getBasicData(); gotTonalContour(dataUnit, detParams); - + return detection; } From f51a519d82003b3d4d1893f88ff64c9a2bcada51 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 17 Mar 2023 21:26:13 +0000 Subject: [PATCH 24/65] Start Tethys GUI --- src/tethys/TethysControl.java | 84 ++++++++++++- src/tethys/TethysState.java | 19 +++ src/tethys/TethysStateObserver.java | 13 ++ src/tethys/dbxml/DBXMLConnect.java | 17 +++ src/tethys/dbxml/DBXMLQueries.java | 119 ++++++++++++++++++ src/tethys/dbxml/DMXMLQueryTest.java | 67 ++++++++++ src/tethys/dbxml/ServerStatus.java | 35 ++++++ src/tethys/output/TethysExportParams.java | 2 +- src/tethys/swing/SelectServerdDialog.java | 89 ++++++++++++++ src/tethys/swing/TethysConnectionPanel.java | 128 ++++++++++++++++++++ src/tethys/swing/TethysGUIPanel.java | 31 +++++ src/tethys/swing/TethysMainPanel.java | 36 ++++++ src/tethys/swing/TethysTabPanel.java | 40 ++++++ 13 files changed, 678 insertions(+), 2 deletions(-) create mode 100644 src/tethys/TethysState.java create mode 100644 src/tethys/TethysStateObserver.java create mode 100644 src/tethys/dbxml/DBXMLQueries.java create mode 100644 src/tethys/dbxml/ServerStatus.java create mode 100644 src/tethys/swing/SelectServerdDialog.java create mode 100644 src/tethys/swing/TethysConnectionPanel.java create mode 100644 src/tethys/swing/TethysGUIPanel.java create mode 100644 src/tethys/swing/TethysMainPanel.java create mode 100644 src/tethys/swing/TethysTabPanel.java diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 76f7a057..de485f53 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -1,7 +1,12 @@ package tethys; +import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; import java.util.ArrayList; import javax.swing.JFrame; @@ -10,17 +15,20 @@ import javax.swing.JMenuItem; import PamController.PamControlledUnit; import PamController.PamController; +import PamView.PamTabPanel; import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; import nilus.Deployment.Instrument; import tethys.dbxml.DBXMLConnect; +import tethys.dbxml.DBXMLQueries; //import nilus.Deployment; //import nilus.Deployment.Instrument; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.output.TethysExporter; import tethys.output.swing.TethysExportDialog; +import tethys.swing.TethysTabPanel; /** * Quick play with a simple system for outputting data to Tethys. At it's start @@ -39,10 +47,18 @@ public class TethysControl extends PamControlledUnit { private TethysExportParams tethysExportParams = new TethysExportParams(); private DBXMLConnect dbxmlConnect; + + private TethysTabPanel tethysTabPanel; + + private DBXMLQueries dbxmlQueries; + + private ArrayList stateObservers; public TethysControl(String unitName) { super(unitType, unitName); + stateObservers = new ArrayList(); dbxmlConnect = new DBXMLConnect(this); + dbxmlQueries = new DBXMLQueries(this); } /** @@ -60,15 +76,51 @@ public class TethysControl extends PamControlledUnit { JMenuItem tethysExport = new JMenuItem("Export ..."); tethysMenu.add(tethysExport); tethysExport.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { tethysExport(parentFrame); } }); + JMenuItem openClient = new JMenuItem("Open client in browser"); + openClient.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTethysClient(); + } + }); + tethysMenu.add(openClient); return tethysMenu; } + protected void openTethysClient() { + String urlString = tethysExportParams.getFullServerName() + "/Client"; + System.out.println("Opening url " + urlString); + URL url = null; + try { + url = new URL(urlString); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + if (url == null) { + return; + } + try { + Desktop.getDesktop().browse(url.toURI()); + } catch (IOException e) { + e.printStackTrace(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } + + @Override + public PamTabPanel getTabPanel() { + if (tethysTabPanel == null) { + tethysTabPanel = new TethysTabPanel(this); + } + return tethysTabPanel; + } + /** * @return the tethysExportParams */ @@ -124,7 +176,33 @@ public class TethysControl extends PamControlledUnit { return deploymentData; } + + /** + * Add a new state observer. + * @param stateObserver + */ + public void addStateObserver(TethysStateObserver stateObserver) { + stateObservers.add(stateObserver); + } + + /** + * Remove a state observer. + * @param stateObserver + * @return true if it existed. + */ + public boolean removeStateObserver(TethysStateObserver stateObserver) { + return stateObservers.remove(stateObserver); + } + /** + * Send state updates around to all state observers. + * @param tethysState + */ + public void sendStateUpdate(TethysState tethysState) { + for (TethysStateObserver stateObserver : this.stateObservers) { + stateObserver.updateState(tethysState); + } + } /** * A name for any deta selectors. * @return @@ -133,4 +211,8 @@ public class TethysControl extends PamControlledUnit { return getUnitName(); } + public DBXMLQueries getDbxmlQueries() { + return dbxmlQueries; + } + } diff --git a/src/tethys/TethysState.java b/src/tethys/TethysState.java new file mode 100644 index 00000000..1fa7d4b4 --- /dev/null +++ b/src/tethys/TethysState.java @@ -0,0 +1,19 @@ +package tethys; + +/** + * Basis for a message system which will get passed around whenever something happens in + * Tethys, whether it be a new connection, progress during data output, etc. + * @author dg50 + * + */ +public class TethysState { + + public enum StateType {UPDATESERVER, TRANSFERDATA}; + + public StateType stateType; + + public TethysState(StateType stateType) { + super(); + this.stateType = stateType; + } +} diff --git a/src/tethys/TethysStateObserver.java b/src/tethys/TethysStateObserver.java new file mode 100644 index 00000000..8738beda --- /dev/null +++ b/src/tethys/TethysStateObserver.java @@ -0,0 +1,13 @@ +package tethys; + +public interface TethysStateObserver { + + /** + * Receive state updates when Tethys has done something (made a connection, moved some data, etc.)
+ * Note that this is for RECEIVING state updates, not for sending them. To avoid infinite notifications + * loops, use tethysControl.sendStateUpdate(TethysState) if this component knows something. + * @param tethysState + */ + public void updateState(TethysState tethysState); + +} diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index 88eb042a..ba061e9a 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -9,6 +9,7 @@ import javax.xml.bind.JAXBException; import java.nio.file.Files; import java.nio.file.Path; +import dbxml.JerseyClient; import dbxml.uploader.Importer; import nilus.Deployment; import nilus.MarshalXML; @@ -113,6 +114,22 @@ public class DBXMLConnect { public void closeDatabase() { } + + /** + * Get the server state via a ping ? + * @return String descritption of state ? + */ + public ServerStatus pingServer() { + JerseyClient jerseyClient = new JerseyClient(tethysControl.getTethysExportParams().getFullServerName()); + boolean ok = false; + try { + ok = jerseyClient.ping(); + } + catch (Exception ex) { + return new ServerStatus(false, ex); + } + return new ServerStatus(ok, null); + } // add whatever calls are necessary ... diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java new file mode 100644 index 00000000..cf3c17c0 --- /dev/null +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -0,0 +1,119 @@ +package tethys.dbxml; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import dbxml.JerseyClient; +import tethys.TethysControl; +import tethys.output.TethysExportParams; + +/** + * 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 + * + */ +public class DBXMLQueries { + + private TethysControl tethysControl; + + public DBXMLQueries(TethysControl tethysControl) { + super(); + this.tethysControl = tethysControl; + } + + public ArrayList getProjectNames() { + DBXMLConnect dbxmlConnect = tethysControl.getDbxmlConnect(); + ServerStatus serverStatus = dbxmlConnect.pingServer(); + if (serverStatus.ok == false) { + return null; + } + Document doc = null; + + TethysExportParams params = tethysControl.getTethysExportParams(); + + try { + JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); + + String testJson = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; + // web browse to http://localhost:9779/Client + + String testResult = jerseyClient.queryJSON(testJson); + + doc = convertStringToXMLDocument(testResult); + } + catch (Exception e) { + e.printStackTrace(); + } + if (doc == null) { + return null; + } + + ArrayList projectNames = new ArrayList<>(); + // iterate through the document and make a list of names, then make them unique. + /* looking for elements like this: + * + * check out the jaxb unmarshaller ... + + + LJ + + + */ + 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) { + Node depEl = ((Element) aNode).getFirstChild(); + if (depEl == null) { + continue; + } + if (depEl instanceof Element) { + Element projEl = (Element) ((Element) depEl).getFirstChild(); + String projName = projEl.getTextContent(); + if (projName != null) { + if (projectNames.contains(projName) == false) { + projectNames.add(projName); + } + } + } + } + } + + Collections.sort(projectNames); + + return projectNames; + } + + private Document convertStringToXMLDocument(String xmlString) { + //Parser that produces DOM object trees from XML content + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + //API to obtain DOM Document instance + DocumentBuilder builder = null; + try { + //Create DocumentBuilder with default configuration + builder = factory.newDocumentBuilder(); + + //Parse the content to Document object + Document doc = builder.parse(new InputSource(new StringReader(xmlString))); + return doc; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/tethys/dbxml/DMXMLQueryTest.java b/src/tethys/dbxml/DMXMLQueryTest.java index 27ce0d06..f6691f64 100644 --- a/src/tethys/dbxml/DMXMLQueryTest.java +++ b/src/tethys/dbxml/DMXMLQueryTest.java @@ -1,5 +1,24 @@ package tethys.dbxml; +import java.io.ByteArrayOutputStream; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import PamController.settings.output.xml.PamguardXMLWriter; +import PamUtils.XMLUtils; import dbxml.JerseyClient; import tethys.output.TethysExportParams; @@ -14,9 +33,57 @@ public class DMXMLQueryTest { JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); +// String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/DeploymentId\",\"Deployment/Site\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\"],\"select\":[],\"enclose\":1}"; +// String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/Region\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/DeploymentId\",\"2\"],\"optype\":\"binary\"}],\"enclose\":1}"; + //String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/Region\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/DeploymentId\",\"2\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; +// String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/Region\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\",\"Deployment/DeploymentId\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/DeploymentId\",\"2\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String testJson = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; // web browse to http://localhost:9779/Client + String testResult = jerseyClient.queryJSON(testJson); + + Document doc = convertStringToXMLDocument(testResult); + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + String formettedXML = pamXMLWriter.getAsString(doc, true); + + System.out.println(testResult); + System.out.println(formettedXML); +// try { +// Transformer serializer = SAXTransformerFactory.newInstance() +// .newTransformer(); +// Source source = new StreamSource(testResult); +// ByteArrayOutputStream bytes = new ByteArrayOutputStream(); +// StreamResult res = new StreamResult(bytes); +// serializer.transform(source, res); +// System.out.println(bytes.toString()); +// } catch (TransformerConfigurationException | TransformerFactoryConfigurationError e) { +// e.printStackTrace(); +// } +// // System.err.println(testResult); +// catch (TransformerException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + } + private Document convertStringToXMLDocument(String xmlString) { + //Parser that produces DOM object trees from XML content + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + //API to obtain DOM Document instance + DocumentBuilder builder = null; + try { + //Create DocumentBuilder with default configuration + builder = factory.newDocumentBuilder(); + + //Parse the content to Document object + Document doc = builder.parse(new InputSource(new StringReader(xmlString))); + return doc; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } } diff --git a/src/tethys/dbxml/ServerStatus.java b/src/tethys/dbxml/ServerStatus.java new file mode 100644 index 00000000..d79f6095 --- /dev/null +++ b/src/tethys/dbxml/ServerStatus.java @@ -0,0 +1,35 @@ +package tethys.dbxml; + +public class ServerStatus { + + public boolean ok; + + public Exception error; + + public ServerStatus(boolean ok, Exception error) { + super(); + this.ok = ok; + this.error = error; + } + + public String getFormatted() { + if (ok) { + return "Server OK"; + } + if (error == null) { + return "Unknown error"; + } + String msg = error.getLocalizedMessage(); + if (msg.startsWith("Exception")) { + msg.substring(9); + } + return msg; + } + + @Override + public String toString() { + return getFormatted(); + } + + +} diff --git a/src/tethys/output/TethysExportParams.java b/src/tethys/output/TethysExportParams.java index c9d848e0..b8ddfd40 100644 --- a/src/tethys/output/TethysExportParams.java +++ b/src/tethys/output/TethysExportParams.java @@ -20,7 +20,7 @@ public class TethysExportParams implements Serializable, Cloneable{ */ public String serverName = "http://localhost"; - public String port = "9779"; + public int port = 9779; public String getFullServerName() { return serverName + ":" + port; diff --git a/src/tethys/swing/SelectServerdDialog.java b/src/tethys/swing/SelectServerdDialog.java new file mode 100644 index 00000000..dbeb7dd1 --- /dev/null +++ b/src/tethys/swing/SelectServerdDialog.java @@ -0,0 +1,89 @@ +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.PamDialog; +import PamView.dialog.PamGridBagContraints; +import tethys.TethysControl; +import tethys.output.TethysExportParams; + +public class SelectServerdDialog extends PamDialog { + + private static final long serialVersionUID = 1L; + + private JTextField serverHost, serverPort; + + private TethysExportParams exportParams; + + private static SelectServerdDialog singleInstance; + + private SelectServerdDialog(TethysControl tethysControl, Window parentFrame) { + super(parentFrame, "Tethys Server", true); + JPanel mainPanel = new JPanel(new GridBagLayout()); + mainPanel.setBorder(new TitledBorder("Tethys Server")); + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(new JLabel("Host: ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(serverHost = new JTextField(20), c); + c.gridx = 0; + c.gridy++; + c.fill = GridBagConstraints.NONE; + mainPanel.add(new JLabel("Port: ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(serverPort = new JTextField(4), c); + + setDialogComponent(mainPanel); + + } + + public static final TethysExportParams showDialog(TethysControl tethysControl, Window parentFrame, TethysExportParams exportParams) { + if (singleInstance == null) { + singleInstance = new SelectServerdDialog(tethysControl, parentFrame); + } + singleInstance.setParams(exportParams); + singleInstance.setVisible(true); + return singleInstance.exportParams; + } + + private void setParams(TethysExportParams exportParams) { + this.exportParams = exportParams; + serverHost.setText(exportParams.serverName); + serverPort.setText(String.format("%d", exportParams.port)); + } + + @Override + public boolean getParams() { + String newHost = serverHost.getText(); + int newPort = 0; + try { + newPort = Integer.valueOf(serverPort.getText()); + } + catch (NumberFormatException e) { + return showWarning("Server port must be a valid integer number"); + } + exportParams.serverName = newHost; + exportParams.port = newPort; + return true; + } + + @Override + public void cancelButtonPressed() { + exportParams = null; + } + + @Override + public void restoreDefaultSettings() { + TethysExportParams defaultParams = new TethysExportParams(); + exportParams.serverName = defaultParams.serverName; + exportParams.port = defaultParams.port; + setParams(exportParams); + } + +} diff --git a/src/tethys/swing/TethysConnectionPanel.java b/src/tethys/swing/TethysConnectionPanel.java new file mode 100644 index 00000000..5b0d2dc6 --- /dev/null +++ b/src/tethys/swing/TethysConnectionPanel.java @@ -0,0 +1,128 @@ +package tethys.swing; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JComboBox; +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.dialog.ScrollingPamLabel; +import PamView.dialog.SettingsButton; +import PamView.panel.PamPanel; +import PamView.panel.WestAlignedPanel; +import pamViewFX.fxNodes.PamComboBox; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysState.StateType; +import tethys.dbxml.ServerStatus; +import tethys.output.TethysExportParams; + +/** + * Top strip of main Tethys GUI for connection and project information + * @author dg50 + * + */ +public class TethysConnectionPanel extends TethysGUIPanel { + + private static final int SERVERNAMELENGTH = 30; + private static final int SERVERSTATUSLENGTH = 20; + + private JPanel mainPanel; + + private JTextField serverName; + + private SettingsButton serverSelButton; + + private ScrollingPamLabel serverStatus; + + private JComboBox projectList; + + public TethysConnectionPanel(TethysControl tethysControl) { + super(tethysControl); + mainPanel = new WestAlignedPanel(new GridBagLayout()); + mainPanel.setBorder(new TitledBorder("Connection and project details")); + serverName = new JTextField(SERVERNAMELENGTH); + serverSelButton = new SettingsButton(); + serverSelButton.setToolTipText("Select server"); + serverStatus = new ScrollingPamLabel(SERVERSTATUSLENGTH); + serverName.setEditable(false); +// serverStatus.setEditable(false); + serverSelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectServer(); + } + }); + + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(new JLabel("Tethys Server "), c); + c.gridx++; + mainPanel.add(serverName, c); + c.gridx++; + mainPanel.add(serverSelButton, c); + c.gridx++; + mainPanel.add(serverStatus, c); + c.gridx++; + + c.gridx =0; + c.gridy++; + mainPanel.add(new JLabel("Projects "), c); + c.gridx++; + mainPanel.add(projectList = new JComboBox<>(), c); + + fillServerControl(); + } + + 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)); + } + } + + private void fillServerControl() { + TethysExportParams exportParams = getTethysControl().getTethysExportParams(); + serverName.setText(exportParams.getFullServerName()); + ServerStatus status = getTethysControl().getDbxmlConnect().pingServer(); + serverStatus.setText(status.toString()); + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + + @Override + public void updateState(TethysState tethysState) { + super.updateState(tethysState); + if (tethysState.stateType == StateType.UPDATESERVER) { + fillServerControl(); + updateProjectList(); + } + + } + + private void updateProjectList() { + projectList.removeAllItems(); + ArrayList dbNames = getTethysControl().getDbxmlQueries().getProjectNames(); + if (dbNames == null || dbNames.size() == 0) { + System.out.println("No existing projects"); + return; + } + for (int i = 0; i < dbNames.size(); i++) { + projectList.addItem(dbNames.get(i)); + } + } + + + +} diff --git a/src/tethys/swing/TethysGUIPanel.java b/src/tethys/swing/TethysGUIPanel.java new file mode 100644 index 00000000..a9263b85 --- /dev/null +++ b/src/tethys/swing/TethysGUIPanel.java @@ -0,0 +1,31 @@ +package tethys.swing; + +import javax.swing.JComponent; + +import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysStateObserver; + +public abstract class TethysGUIPanel implements TethysStateObserver { + + private TethysControl tethysControl; + + public TethysGUIPanel(TethysControl tethysControl) { + super(); + this.tethysControl = tethysControl; + tethysControl.addStateObserver(this); + } + + public TethysControl getTethysControl() { + return tethysControl; + } + + public abstract JComponent getComponent(); + + @Override + public void updateState(TethysState tethysState) { + + } + + +} diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java new file mode 100644 index 00000000..d03929eb --- /dev/null +++ b/src/tethys/swing/TethysMainPanel.java @@ -0,0 +1,36 @@ +package tethys.swing; + +import java.awt.BorderLayout; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import tethys.TethysControl; + +public class TethysMainPanel extends TethysGUIPanel { + + private TethysControl tethysControl; + + private JPanel mainPanel; + + private TethysConnectionPanel connectionPanel; + + public TethysMainPanel(TethysControl tethysControl) { + super(tethysControl); + this.tethysControl = tethysControl; + mainPanel = new JPanel(new BorderLayout()); + connectionPanel = new TethysConnectionPanel(tethysControl); + mainPanel.add(BorderLayout.NORTH, connectionPanel.getComponent()); + } + + public JPanel getMainPanel() { + return mainPanel; + } + + @Override + public JComponent getComponent() { + return getMainPanel(); + } + + +} diff --git a/src/tethys/swing/TethysTabPanel.java b/src/tethys/swing/TethysTabPanel.java new file mode 100644 index 00000000..92aa457a --- /dev/null +++ b/src/tethys/swing/TethysTabPanel.java @@ -0,0 +1,40 @@ +package tethys.swing; + +import java.awt.Frame; + +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JToolBar; + +import PamView.PamTabPanel; +import tethys.TethysControl; + +public class TethysTabPanel implements PamTabPanel { + + private TethysControl tethysContol; + + private TethysMainPanel tethysMainPanel; + + @Override + public JMenu createMenu(Frame parentFrame) { + return null; + } + + public TethysTabPanel(TethysControl tethysContol) { + super(); + this.tethysContol = tethysContol; + tethysMainPanel = new TethysMainPanel(tethysContol); + } + + @Override + public JComponent getPanel() { + return tethysMainPanel.getMainPanel(); + } + + @Override + public JToolBar getToolBar() { + // TODO Auto-generated method stub + return null; + } + +} From c7ceba1604d573c412522a2eb028de811e10a5e6 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 21 Mar 2023 20:05:38 +0000 Subject: [PATCH 25/65] GUI Work. working on some GUI / control options. Semi functional. Seem to have a problem with time zones converting from XML time to millis which need sorting. --- src/Array/ArrayDialog.java | 7 + src/Array/InstrumentIdentityPanel.java | 72 +++ src/Array/PamArray.java | 26 + src/metadata/deployment/DeploymentData.java | 106 ++-- src/tethys/TethysControl.java | 230 +++++++- src/tethys/TethysMenuActions.java | 61 +++ src/tethys/TethysState.java | 7 +- src/tethys/TethysTimeFuncs.java | 46 ++ src/tethys/dbxml/DBQueryResult.java | 26 + src/tethys/dbxml/DBXMLConnect.java | 47 +- src/tethys/dbxml/DBXMLQueries.java | 364 ++++++++++++- src/tethys/deployment/DeploymentHandler.java | 499 ++++++++++++++---- src/tethys/deployment/DeploymentOverview.java | 43 +- .../deployment/DeploymentRecoveryPair.java | 6 +- src/tethys/deployment/DutyCycleInfo.java | 28 + src/tethys/deployment/PInstrument.java | 49 ++ src/tethys/deployment/RecordingPeriod.java | 16 + src/tethys/detection/DetectionsHandler.java | 17 +- src/tethys/niluswraps/PDeployment.java | 55 ++ src/tethys/output/DatablockSynchInfo.java | 48 ++ src/tethys/output/TethysExportParams.java | 10 + src/tethys/output/TethysExporter.java | 30 +- src/tethys/pamdata/AutoTethysProvider.java | 2 +- src/tethys/swing/DatablockSynchPanel.java | 112 ++++ src/tethys/swing/DeploymentsPanel.java | 48 ++ src/tethys/swing/NewProjectDialog.java | 91 ++++ .../swing/PAMGuardDeploymentsTable.java | 215 ++++++++ src/tethys/swing/TethysConnectionPanel.java | 200 ++++++- src/tethys/swing/TethysDeploymentsTable.java | 151 ++++++ src/tethys/swing/TethysGUIPanel.java | 27 + src/tethys/swing/TethysMainPanel.java | 21 + 31 files changed, 2423 insertions(+), 237 deletions(-) create mode 100644 src/Array/InstrumentIdentityPanel.java create mode 100644 src/tethys/TethysMenuActions.java create mode 100644 src/tethys/dbxml/DBQueryResult.java create mode 100644 src/tethys/deployment/PInstrument.java create mode 100644 src/tethys/niluswraps/PDeployment.java create mode 100644 src/tethys/output/DatablockSynchInfo.java create mode 100644 src/tethys/swing/DatablockSynchPanel.java create mode 100644 src/tethys/swing/DeploymentsPanel.java create mode 100644 src/tethys/swing/NewProjectDialog.java create mode 100644 src/tethys/swing/PAMGuardDeploymentsTable.java create mode 100644 src/tethys/swing/TethysDeploymentsTable.java 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() { From 642f1da873808dd1648a17576076977b10a59771 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 22 Mar 2023 20:07:17 +0000 Subject: [PATCH 26/65] updated doc names Dog names now match their id so it's easier to delete them from Tethys --- src/tethys/dbxml/DBXMLConnect.java | 126 ++++++++++++++++---- src/tethys/dbxml/DBXMLQueries.java | 7 +- src/tethys/detection/DetectionsHandler.java | 2 +- src/tethys/output/TethysExporter.java | 2 +- 4 files changed, 111 insertions(+), 26 deletions(-) diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index 4508ebec..55884926 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -1,6 +1,9 @@ package tethys.dbxml; +import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -11,6 +14,7 @@ import java.nio.file.Files; import java.nio.file.Path; import dbxml.JerseyClient; +import dbxml.Queries; import dbxml.uploader.Importer; import nilus.Deployment; import nilus.MarshalXML; @@ -28,9 +32,13 @@ import PamguardMVC.PamDataBlock; public class DBXMLConnect { private TethysControl tethysControl; - + private File tempDirectory; + public DBXMLConnect(TethysControl tethysControl) { this.tethysControl = tethysControl; + + checkTempFolder(); + } @@ -42,27 +50,27 @@ public class DBXMLConnect { * @param pamGuardObjs all nilus objects loaded with PamGuard data * @return error string, null string means there are no errors */ - public String postToTethys(List pamGuardObjs) + public String postToTethys(Object nilusObject) { - Class objClass = pamGuardObjs.get(0).getClass(); + Class objClass = nilusObject.getClass(); String collection = getTethysCollection(objClass.getName()); PamDataBlock defaultPamBlock = null; TethysExportParams params = new TethysExportParams(); String fileError = null; + String tempName = getTempFileName(nilusObject); + tempName = tempDirectory.getAbsolutePath() + File.separator + tempName + ".xml"; + File tempFile = new File(tempName); try { MarshalXML marshal = new MarshalXML(); - marshal.createInstance(objClass); - for (Object obj : pamGuardObjs ) - { - Path tempFile = Files.createTempFile("pamGuardToTethys", ".xml"); - marshal.marshal(obj, tempFile.toString()); + marshal.createInstance(objClass); +// Path tempFile = Files.createTempFile("pamGuardToTethys", ".xml"); + marshal.marshal(nilusObject, tempFile.toString()); fileError = Importer.ImportFiles(params.getFullServerName(), collection, new String[] { tempFile.toString() }, "", "", false); // System.out.println(fileError); - tempFile.toFile().deleteOnExit(); - } + tempFile.deleteOnExit(); } catch(IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -77,6 +85,74 @@ public class DBXMLConnect { return fileError; } + /** + * Get a temp folder to hold xml output. This will be the standard + * temp folder + /PAMGuardTethys. Files will be left here until PAMGUard + * exits then should delete automatically + */ + private void checkTempFolder() { + String javaTmpDirs = System.getProperty("java.io.tmpdir") + File.separator + "PAMGuardTethys"; + + File tempDir = new File(javaTmpDirs); + if (tempDir.exists() == false) { + if (tempDir.mkdirs()) { + tempDirectory = tempDir; + }; + } + if (tempDirectory == null) { + tempDirectory = new File(System.getProperty("java.io.tmpdir")); + } + + } + + + /** + * needs to be based on the document id, but the getter for this can vary by type, so time + * to start casting ! + * @param nilusObject + * @return + */ + private String getTempFileName(Object nilusObject) { + /** + * While all nilus objects should have a getId function, they have no + * common root, so try to get the function via the class declared methods. + */ + String tempName = "PamguardTethys"; + Class nilusClass = nilusObject.getClass(); + try { + Method getId = nilusClass.getDeclaredMethod("getId", null); + Object[] inputs = new Object[0]; + Object res = getId.invoke(nilusObject, inputs); + if (res instanceof String) { + tempName = (String) res; + return tempName; + } + } catch (NoSuchMethodException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (nilusObject instanceof nilus.Deployment) { + tempName = ((Deployment) nilusObject).getId(); + } + else if (nilusObject instanceof nilus.Detections) { + tempName = ((nilus.Detections) nilusObject).getId(); + } + return tempName; + } + + /** * get Tethys collection name from nilus collection objects * @param className nilus object Class Name @@ -115,30 +191,34 @@ public class DBXMLConnect { public boolean deleteDeployment(String deploymentId) { ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocsIds(deploymentId); JerseyClient jerseyClient = null; + Queries queries = null; try { jerseyClient = new JerseyClient(tethysControl.getTethysExportParams().getFullServerName()); + queries = new Queries(jerseyClient); } 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; - } - } +// 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); + String doc = queries.getDocument("Deployments", deploymentId); +// queries. + result = jerseyClient.removeDocument("Deployments", deploymentId ); } catch (Exception e) { - e.printStackTrace(); +// e.printStackTrace(); return false; } return true; diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 440763d2..6eb81359 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -17,6 +17,7 @@ import org.xml.sax.InputSource; import PamController.settings.output.xml.PamguardXMLWriter; import PamguardMVC.PamDataBlock; import dbxml.JerseyClient; +import dbxml.Queries; import nilus.Deployment; import nilus.Deployment.Instrument; import nilus.DeploymentRecoveryDetails; @@ -28,7 +29,7 @@ import tethys.output.TethysExportParams; /** * Some standard queries we're going to want to make from various - * parts of the system as the user interracts with the GUI. + * parts of the system as the user interacts with the GUI. * @author dg50 * */ @@ -65,6 +66,8 @@ public class DBXMLQueries { try { JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); + Queries queries = new Queries(jerseyClient); + queryResult = jerseyClient.queryJSON(jsonQueryString, 0); schemaPlan = jerseyClient.queryJSON(jsonQueryString, 1); @@ -159,10 +162,12 @@ public class DBXMLQueries { NodeList returns = doc.getElementsByTagName("Return"); // System.out.println("N projects = " + returns.getLength()); int n = returns.getLength(); +// Queries queries = new Queries(null) 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"); diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index eda7c061..6240defb 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -177,7 +177,7 @@ public class DetectionsHandler { ArrayList detectionDocuments = new ArrayList(); detectionDocuments.add(detections); - tethysControl.getDbxmlConnect().postToTethys(detectionDocuments); // call whatever you need to call in here to write the Detections. +// tethysControl.getDbxmlConnect().postToTethys(detectionDocuments); // call whatever you need to call in here to write the Detections. return true; diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index f944c9cc..e2bcb980 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -189,10 +189,10 @@ public class TethysExporter { Deployment deployment = deploymentHandler.createDeploymentDocument(i++, recordingPeriod); // System.out.println(deployment.toString()); deploymentDocs.add(deployment); + tethysControl.getDbxmlConnect().postToTethys(deployment); } - tethysControl.getDbxmlConnect().postToTethys(deploymentDocs); /* * go through the export params and call something for every data block that's From 4be259b76bea6c59fb1b2d4e1b39eb63564af100 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 29 Mar 2023 20:57:54 +0100 Subject: [PATCH 27/65] GUI Play Experimenting with GUI components --- .../output/xml/PamguardXMLWriter.java | 14 +- .../RawDataUnavailableException.java | 2 +- .../dataSelector/DataSelector.java | 56 +++- src/tethys/TethysControl.java | 19 +- src/tethys/TethysTimeFuncs.java | 4 + src/tethys/dbxml/DBXMLConnect.java | 185 ++++++++---- src/tethys/dbxml/DBXMLQueries.java | 43 ++- src/tethys/images/Tethys-200.png | Bin 0 -> 100644 bytes src/tethys/niluswraps/PDeployment.java | 4 + src/tethys/niluswraps/PGranularityType.java | 58 ++++ src/tethys/output/StreamExportParams.java | 14 + src/tethys/output/TethysExporter.java | 1 - .../swing/DatablockDetectionsPanel.java | 89 ++++++ src/tethys/swing/DatablockSynchPanel.java | 48 ++++ src/tethys/swing/DeploymentExportPanel.java | 268 ++++++++++++++++++ src/tethys/swing/DeploymentTableObserver.java | 7 + src/tethys/swing/DeploymentsPanel.java | 31 +- src/tethys/swing/DetectionsExportPanel.java | 64 +++++ src/tethys/swing/Images.java | 24 ++ src/tethys/swing/NewProjectDialog.java | 2 +- .../swing/PAMGuardDeploymentsTable.java | 70 ++++- .../swing/ProjectDeploymentsDialog.java | 57 ++++ src/tethys/swing/StreamTableObserver.java | 9 + src/tethys/swing/TethysConnectionPanel.java | 13 + src/tethys/swing/TethysImagePanel.java | 50 ++++ src/tethys/swing/TethysMainPanel.java | 22 +- src/tethys/swing/export/DescriptionCard.java | 30 ++ .../swing/export/DescriptionTypePanel.java | 104 +++++++ .../swing/export/DetectionsExportWizard.java | 146 ++++++++++ src/tethys/swing/export/ExportWizardCard.java | 43 +++ src/tethys/swing/export/GranularityCard.java | 144 ++++++++++ 31 files changed, 1522 insertions(+), 99 deletions(-) create mode 100644 src/tethys/images/Tethys-200.png create mode 100644 src/tethys/niluswraps/PGranularityType.java create mode 100644 src/tethys/swing/DatablockDetectionsPanel.java create mode 100644 src/tethys/swing/DeploymentExportPanel.java create mode 100644 src/tethys/swing/DeploymentTableObserver.java create mode 100644 src/tethys/swing/DetectionsExportPanel.java create mode 100644 src/tethys/swing/Images.java create mode 100644 src/tethys/swing/ProjectDeploymentsDialog.java create mode 100644 src/tethys/swing/StreamTableObserver.java create mode 100644 src/tethys/swing/TethysImagePanel.java create mode 100644 src/tethys/swing/export/DescriptionCard.java create mode 100644 src/tethys/swing/export/DescriptionTypePanel.java create mode 100644 src/tethys/swing/export/DetectionsExportWizard.java create mode 100644 src/tethys/swing/export/ExportWizardCard.java create mode 100644 src/tethys/swing/export/GranularityCard.java diff --git a/src/PamController/settings/output/xml/PamguardXMLWriter.java b/src/PamController/settings/output/xml/PamguardXMLWriter.java index b943f2d6..ec656589 100644 --- a/src/PamController/settings/output/xml/PamguardXMLWriter.java +++ b/src/PamController/settings/output/xml/PamguardXMLWriter.java @@ -520,6 +520,14 @@ public class PamguardXMLWriter implements PamSettings { private Element writeSettings(Document doc, PamSettings pamSettings, ArrayList objectHierarchy) { return writeSettings(doc, pamSettings, pamSettings.getSettingsReference(), objectHierarchy); } + + public Document writeOneObject(Object data) { + Document doc = XMLUtils.createBlankDoc(); + Element el = doc.createElement("Settings"); + Element newel = writeObjectData(doc, el, data, new ArrayList()); + doc.appendChild(newel); + return doc; + } /** * Write settings using an object of choice instead of the standard one from PamSettings. @@ -567,8 +575,10 @@ public class PamguardXMLWriter implements PamSettings { if (parameterSet == null) { return null; } - - objectHierarchy.add(data); + + if (objectHierarchy != null) { + objectHierarchy.add(data); + } for (PamParameterData pamParam:parameterSet.getParameterCollection()) { try { Object paramData = pamParam.getData(); diff --git a/src/PamguardMVC/RawDataUnavailableException.java b/src/PamguardMVC/RawDataUnavailableException.java index 581f981d..37e4275a 100644 --- a/src/PamguardMVC/RawDataUnavailableException.java +++ b/src/PamguardMVC/RawDataUnavailableException.java @@ -61,7 +61,7 @@ public class RawDataUnavailableException extends Exception { return String.format("Samples %d length %d requested from %s have not yet arrived", startSample, duration, rawDataBlock.getDataName()); case INVALID_CHANNEL_LIST: - return String.format("Samples %d length %d requested from %s do not contain the reqeusted channels %s", + return String.format("Samples %d length %d requested from %s do not contain the reqeusted channels", startSample, duration, rawDataBlock.getDataName()); case NEGATIVE_DURATION: return String.format("Negative data duration request for %d samples" , duration); diff --git a/src/PamguardMVC/dataSelector/DataSelector.java b/src/PamguardMVC/dataSelector/DataSelector.java index 0503479f..a91712b5 100644 --- a/src/PamguardMVC/dataSelector/DataSelector.java +++ b/src/PamguardMVC/dataSelector/DataSelector.java @@ -9,7 +9,10 @@ import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JMenuItem; +import org.w3c.dom.Document; + import PamController.PamController; +import PamController.settings.output.xml.PamguardXMLWriter; import PamView.dialog.PamDialogPanel; import PamView.dialog.SettingsButton; import PamguardMVC.PamDataBlock; @@ -39,7 +42,7 @@ public abstract class DataSelector { private String selectorTitle; private boolean allowScores; - + /** * Create a data selector for a DataBlock. If allowScores is * true, then the selector MAY (but may not) offer a more complicated @@ -104,18 +107,8 @@ public abstract class DataSelector { if (parentFrame == null) { parentFrame = PamController.getMainFrame(); } - Window localWin = parentFrame; - DataSelectorChangeListener localChangeListener = changeListener; JMenuItem menuItem = new JMenuItem("Data selection ..."); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - boolean ok = showSelectDialog(localWin); - if (ok && changeListener != null) { - changeListener.selectorChange(DataSelector.this); - } - } - }); + menuItem.addActionListener(new ShowSettingsButton(parentFrame, changeListener)); return menuItem; } @@ -129,6 +122,24 @@ public abstract class DataSelector { return ok; } + /** + * Get descriptive text about the data selector which can be + * added to dialogs and other information panels. + * @return descriptive text. Default is a xml dump of params. + */ + public String getDescription() { + if (getParams() == null) { + return null; + } + PamguardXMLWriter xmlWriter = PamguardXMLWriter.getXMLWriter(); + Document doc = xmlWriter.writeOneObject(getParams()); + if (doc != null) { + String str = xmlWriter.getAsString(doc, true); + return str; + } + return null; + } + /** * Score a PAMDataUnit. this is used in preference * to a boolean select function so that the user can add different @@ -228,25 +239,40 @@ public abstract class DataSelector { * @param parentWindow */ public JButton getDialogButton(Window parentWindow) { + return getDialogButton(parentWindow, null); + } + /** + * Create a settings type button that can be inserted into a + * larger dialog. + * @param parentWindow + */ + + public JButton getDialogButton(Window parentWindow, DataSelectorChangeListener changeListener) { JButton button = new SettingsButton(); - button.addActionListener(new ShowSettingsButton(parentWindow)); + button.addActionListener(new ShowSettingsButton(parentWindow, changeListener)); button.setToolTipText("Data selection options for " + getSelectorTitle()); return button; } private class ShowSettingsButton implements ActionListener { private Window parentWindow; + private DataSelectorChangeListener changeListener; /** * @param parentWindow + * @param changeListener */ - public ShowSettingsButton(Window parentWindow) { + public ShowSettingsButton(Window parentWindow, DataSelectorChangeListener changeListener) { super(); this.parentWindow = parentWindow; + this.changeListener = changeListener; } @Override public void actionPerformed(ActionEvent e) { - showSelectDialog(parentWindow); + boolean ok = showSelectDialog(parentWindow); + if (ok && changeListener != null) { + changeListener.selectorChange(DataSelector.this); + } } } diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index b5ef662d..7fe79466 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -38,6 +38,7 @@ import tethys.output.DatablockSynchInfo; import tethys.output.TethysExportParams; import tethys.output.TethysExporter; import tethys.output.swing.TethysExportDialog; +import tethys.swing.ProjectDeploymentsDialog; import tethys.swing.TethysTabPanel; /** @@ -75,7 +76,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet super(unitType, unitName); stateObservers = new ArrayList(); dbxmlConnect = new DBXMLConnect(this); - dbxmlQueries = new DBXMLQueries(this); + dbxmlQueries = new DBXMLQueries(this, dbxmlConnect); deploymentHandler = new DeploymentHandler(this); serverCheckTimer = new Timer(10000, new ActionListener() { @Override @@ -126,9 +127,21 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } }); tethysMenu.add(openClient); + JMenuItem showDeps = new JMenuItem("Show project deployments"); + showDeps.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showProjectDeploymentsDialog(); + } + }); + tethysMenu.add(showDeps); return tethysMenu; } + public void showProjectDeploymentsDialog() { + ProjectDeploymentsDialog.showDialog(getGuiFrame(), this); + } + public ArrayList getExportableDataBlocks() { ArrayList sets = new ArrayList<>(); ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); @@ -182,7 +195,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet /** * open client in the default web browser */ - protected void openTethysClient() { + public void openTethysClient() { String urlString = tethysExportParams.getFullServerName() + "/Client"; System.out.println("Opening url " + urlString); URL url = null; @@ -340,7 +353,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet private void initializationStuff() { deploymentHandler.createPamguardOverview(); serverCheckTimer.start(); - updateState(new TethysState(StateType.NEWPAMGUARDSELECTION)); + sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION)); } /** diff --git a/src/tethys/TethysTimeFuncs.java b/src/tethys/TethysTimeFuncs.java index 922ebd1f..569f6f3d 100644 --- a/src/tethys/TethysTimeFuncs.java +++ b/src/tethys/TethysTimeFuncs.java @@ -14,6 +14,8 @@ import javax.xml.datatype.XMLGregorianCalendar; import PamUtils.PamCalendar; public class TethysTimeFuncs { + + private static TimeZone timeZone = TimeZone.getTimeZone("UTC"); /* * Copied from http://www.java2s.com/Code/Java/Development-Class/ConvertsagiventimeinmillisecondsintoaXMLGregorianCalendarobject.htm @@ -21,6 +23,7 @@ public class TethysTimeFuncs { public static XMLGregorianCalendar xmlGregCalFromMillis(long millis) { try { final GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTimeZone(timeZone); calendar.setTimeInMillis(millis); return DatatypeFactory.newInstance().newXMLGregorianCalendar( calendar); @@ -41,6 +44,7 @@ public class TethysTimeFuncs { return null; } GregorianCalendar gc2 = xmlGregorian.toGregorianCalendar(); + gc2.setTimeZone(timeZone); return gc2.getTimeInMillis(); } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index 55884926..f1347d99 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -33,6 +33,12 @@ public class DBXMLConnect { private TethysControl tethysControl; private File tempDirectory; + + private JerseyClient jerseyClient; + + private Queries queries; + + private String currentSiteURL; public DBXMLConnect(TethysControl tethysControl) { this.tethysControl = tethysControl; @@ -41,20 +47,97 @@ public class DBXMLConnect { } + /** + * Check the jersey client and the queries. Need to recreate + * if the url has changed. + * @return + */ + private boolean checkClient() { + if (jerseyClient == null || queries == null || currentSiteURL == null) { + return false; + } + TethysExportParams params = tethysControl.getTethysExportParams(); + if (currentSiteURL.equalsIgnoreCase(params.getFullServerName()) == false) { + return false; + } + return true; + } /** - * take list of nilus objects loaded with PamGuard data and post them to the Tethys database - * all objects must be of the same nilus object - * TethysExportParams obj used from UI inputs + * Get the client. The client will only be recreated if the url changes + * @return Jersy client + */ + public synchronized JerseyClient getJerseyClient() { + if (checkClient() == false) { + openConnections(); + } + return jerseyClient; + } + + /** + * Get the Queries object. This will only be recreated if the client changes. + * @return + */ + public synchronized Queries getTethysQueries() { + if (checkClient() == false) { + openConnections(); + } + return queries; + } + + /** + * Update a document within Tethys. We're assuming that a + * document with the same name in the same collection already + * exists. If it doesn't / has a different name, then use + * the removedocument function + * @param nilusDocument + * @return + */ + public String updateDocument(Object nilusDocument) { + deleteDocument(nilusDocument); + return postToTethys(nilusDocument); + } + + /** + * Delete a nilus document from the database. The only field which + * needs to be populated here is the Id. The code also uses the object + * class to identify the correct collection. + * @param nilusDocument + * @return + */ + public boolean deleteDocument(Object nilusDocument) { + + Class objClass = nilusDocument.getClass(); + String collection = getTethysCollection(objClass.getName()); + String docId = getDocumentId(nilusDocument); + String result = null; + try { + result = jerseyClient.removeDocument(collection, docId ); + /** + * Return from a sucessful delete is something like + * + deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId++, recordPeriod); + + ['ECoastNARW0'] + + */ + } + catch (Exception e) { + System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); + } + return result == null; + } + + /** + * take a nilus object loaded with PamGuard data and post it to the Tethys database * - * @param pamGuardObjs all nilus objects loaded with PamGuard data + * @param pamGuardObjs a nilus object loaded with PamGuard data * @return error string, null string means there are no errors */ public String postToTethys(Object nilusObject) { Class objClass = nilusObject.getClass(); String collection = getTethysCollection(objClass.getName()); - PamDataBlock defaultPamBlock = null; TethysExportParams params = new TethysExportParams(); String fileError = null; String tempName = getTempFileName(nilusObject); @@ -95,63 +178,57 @@ public class DBXMLConnect { File tempDir = new File(javaTmpDirs); if (tempDir.exists() == false) { - if (tempDir.mkdirs()) { - tempDirectory = tempDir; - }; + tempDir.mkdirs(); } + if (tempDir.exists()) { + tempDirectory = tempDir; + }; if (tempDirectory == null) { tempDirectory = new File(System.getProperty("java.io.tmpdir")); } } - /** - * needs to be based on the document id, but the getter for this can vary by type, so time - * to start casting ! + * Get a document Id string. All Document objects should have a getId() function + * however they do not have a type hierarchy, so it can't be accessed directly. + * instead go via the class.getDeclaredMethod function and it should be possible to find + * it. * @param nilusObject - * @return + * @return document Id for any type of document, or null if the document doesn't have a getID function */ - private String getTempFileName(Object nilusObject) { - /** - * While all nilus objects should have a getId function, they have no - * common root, so try to get the function via the class declared methods. - */ - String tempName = "PamguardTethys"; + private String getDocumentId(Object nilusObject) { + String tempName = null; Class nilusClass = nilusObject.getClass(); + Method getId; try { - Method getId = nilusClass.getDeclaredMethod("getId", null); + getId = nilusClass.getDeclaredMethod("getId", null); Object[] inputs = new Object[0]; Object res = getId.invoke(nilusObject, inputs); if (res instanceof String) { tempName = (String) res; return tempName; } - } catch (NoSuchMethodException e) { - // TODO Auto-generated catch block + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); - } catch (SecurityException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InvocationTargetException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - if (nilusObject instanceof nilus.Deployment) { - tempName = ((Deployment) nilusObject).getId(); - } - else if (nilusObject instanceof nilus.Detections) { - tempName = ((nilus.Detections) nilusObject).getId(); } return tempName; } + /** + * needs to be based on the document id, + * @param nilusObject + * @return + */ + private String getTempFileName(Object nilusObject) { + + String docId = getDocumentId(nilusObject); + if (docId == null || docId.length() == 0) { + docId = "PamguardTethys"; + } + return docId; + } + /** * get Tethys collection name from nilus collection objects @@ -190,16 +267,8 @@ public class DBXMLConnect { */ public boolean deleteDeployment(String deploymentId) { ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocsIds(deploymentId); - JerseyClient jerseyClient = null; + JerseyClient jerseyClient = getJerseyClient(); Queries queries = null; - try { - jerseyClient = new JerseyClient(tethysControl.getTethysExportParams().getFullServerName()); - queries = new Queries(jerseyClient); - } - catch (Exception e) { - e.printStackTrace(); - return false; - } String result; // for (int i = 0; i < detDocNames.size(); i++) { // try { @@ -225,13 +294,19 @@ public class DBXMLConnect { } - public boolean openDatabase() { - - return true; + public synchronized boolean openConnections() { + TethysExportParams params = tethysControl.getTethysExportParams(); + currentSiteURL = params.getFullServerName(); + jerseyClient = new JerseyClient(currentSiteURL); + queries = new Queries(jerseyClient); + ServerStatus state = pingServer(); + return state.ok; } - public void closeDatabase() { - + public synchronized void closeConnections() { + jerseyClient = null; + queries = null; + currentSiteURL = null; } /** @@ -239,10 +314,10 @@ public class DBXMLConnect { * @return Server state ? */ public ServerStatus pingServer() { - JerseyClient jerseyClient = new JerseyClient(tethysControl.getTethysExportParams().getFullServerName()); + boolean ok = false; try { - ok = jerseyClient.ping(); + ok = getJerseyClient().ping(); } catch (Exception ex) { return new ServerStatus(false, ex); diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 6eb81359..76b236a0 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -36,10 +36,12 @@ import tethys.output.TethysExportParams; public class DBXMLQueries { private TethysControl tethysControl; - - public DBXMLQueries(TethysControl tethysControl) { + private DBXMLConnect dbXMLConnect; + + public DBXMLQueries(TethysControl tethysControl, DBXMLConnect dbXMLConnect) { super(); this.tethysControl = tethysControl; + this.dbXMLConnect = dbXMLConnect; } /** @@ -64,7 +66,8 @@ public class DBXMLQueries { TethysExportParams params = tethysControl.getTethysExportParams(); try { - JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); + JerseyClient jerseyClient = dbxmlConnect.getJerseyClient(); +// String url = jerseyClient.getURL(); Queries queries = new Queries(jerseyClient); @@ -210,6 +213,9 @@ public class DBXMLQueries { } public int countData(PamDataBlock dataBlock, String deploymentId) { + /** + * first query for Detections documents associated with this deployment and datablock. + */ 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; @@ -235,11 +241,40 @@ public class DBXMLQueries { Node aNode = returns.item(i); String docName = aNode.getTextContent(); // System.out.println(aNode.getTextContent()); - count += countDetecionsData(docName); + int count2 = countDetections2(docName); + count += count2; //countDetecionsData(docName); + } return count; } + private int countDetections2(String docName) { + TethysExportParams params = tethysControl.getTethysExportParams(); + String queryBase = "count(collection(\"Detections\")/Detections[Id=\"ReplaceDocumentId\"]/OnEffort/Detection)"; + String query = queryBase.replace("ReplaceDocumentId", docName); + + String result = null; + try { + Queries queries = dbXMLConnect.getTethysQueries(); + result = queries.QueryTethys(query); +// System.out.println(result); + } + catch (Exception e) { + System.out.println("Error executing " + query); +// e.printStackTrace(); + return -1; + } + int count = 0; + try { + count = Integer.valueOf(result); + } + catch (NumberFormatException e) { + System.out.println("Unable to interpret count data " + result); + return 0; + } + return count; + } + /** * Count the data in a detections document. * @param detectionDocId diff --git a/src/tethys/images/Tethys-200.png b/src/tethys/images/Tethys-200.png new file mode 100644 index 0000000000000000000000000000000000000000..19e3c48fc825cbfb723b51f983c21a978946c8d9 GIT binary patch literal 100644 zcmXt-}o*9dr`G7 zQDKlxfnkRF?Z%4_M>#S&>DBJ7%fy=`+$jF&bO5VlVI&DqFC&x==koh!JG~`W``ZXo7jwNp`$e@;NKfWwKAAY`;<>6oB$VUJ%ZEdgYaRhAw(OnZU6+^gOZI-j8;(2GJ{lzp5fsLL%SPR8@}a@E ze=MoZAAox>=AQ0dr#y}c*NDkQT}@1k)^|U<>L`fLuxJU!?CX0^d0O-$DdiwqmUX|H zRf$*h!=I$;k-`{PfOM}&T_BggI6+R(rRb84V^S3HHY0F4V_r!l$m$bw&UufOem1omJeDL zdf%0^MI$-hO}9YQc}+XLCU0&jx_wMc^YWfEmsGqn10xHcvHW;`{O2sG45RgIE zLVWaW$Of)lt(#hzJm(Hs-1X%tsmfATY(lxDZAQEeg{h>RyK(Mk$-RB0Lbd|)+&{OR zZ|V8dh;JoRP`{uU5v>$e?yQ#*tutW~mnHw41crdCBj`I$GF$)ty;3o#Q7zwGf64gf z!K*01-crpz`POLlLP0vq)s0YHDYdwiBGyrHT6BK&#zxFlterj56(l4}`kr)C|{|+6I zbIGoe{}Cnq@twEUS&=oI`@3g@D3-?A978kj;&TWc~*7tj#`01!m$gVk-YYzes@L!rB#ZTc8f zlRTKkTJS~o;3%fGABXGTr*Pr44*5GQ33p0VBtxVZx$OD3n@|MJk_SnQ65z=%Ppo43 zY$#u!LMW#NhDV!r_PzZZxY=<~vR`;5L3%l9wn+1OqaioOx=kxD`XTdK&q2GCl6OgE zPK$wX!swUkdgS`s8q|ZUZ?k#xA-(dIHAg0S`IR@xfLQbnqGG&)n<7Y??gfsGB#A|q zk`x)+^k#A6di&wgfOq>7{|$j3-Bs1#4xI6Db<3nEs~1)nLasj`c8J+? z^FlDZKSkxex8}HN+xLZRPxj8*H`o4NQm2?BK?(B460)WZP)i*GXxpB=nGo@jiXVD) z^b!D_XlSbx_nhRy0OWbdb_2VlK&9wRTUolta=vh`BF@OKR!Y$YO9#h}{9!*@f`Wtn zn%58sr{AFPA!gScq*g|0>EF+$da8c)ZEV2mNwvA`emvf}BzAaMm0m9K7agTm^UA`k z7ue#{2q*7qS8z}j1xS>6Pm^)d)-u!+`{uecRarsFXm;2f;v9TQ)^W3OebmqS=l7Be zV5sr3>Qe$T6g%s+^u~IYyi?fmg+vq5uE37^RVmw`{;7Gh(PNZvn=CZr?U#>lE9&TfJnEFHh=7lxb+VIp9-$Z zgx1v5dhFhB{O{AN@8~a*2<|4&>F=TMC{F~{Qm7Ts!&}*Lc$4Tn{Fb{&K5xW-?0Bx@ z%s2uW>L}@@W??g>bOMEz_NYeuIQQ$y3Wmq56zH`RVGAuXFKqYSuUgVD>&7S6mMY=g zab1B|4~~GvqobFXjxDD^=`lgxClT+cw9aID`UV5dlf}|i-+0)5^YoXO!4xuSJS}G) zCk$f=gKioTVwSpwBkWlD*yZwsbOIVgv!ihR=HiT{RucXl0ruJHbsEpzelRs4REJaD zZ2CL{gZ-*=s}fGP65F>f_p{p#$5p{vpWcX!H_-&bhArcg=CuqDAP=%)U!YM19eOgR z4)>az_u5ob6qQXFy-~_XM~8xmy!cV07hgM(bDnl*B%9d{HIm=u?k zRA6>6bo;^RJGHN9hJ+0j?@|-~J!=Z~UyYIuh1Ekn=QKc-6X9BIZdX=c#;86U*E0F3 zx(eszaE1f!L&owCea3bNFP906OHUyRX@KIaUv`CFQV)#pV=0lN!nRCNf@o}J?yi(351j;HxhsRxaeX>`}!d$QX(dtZ9Lus zdEAw(WnWB9iy-^7zU;csGHm?nSi-^&3|_ey`aEbc$1vBvGM>}Z;@6;iyTKIf@(fDO zJ6w!!{Cnw_%7(r4M5~1Oc+l3vRgq7!Dh_xjZJkyuH<9UQ)yqrpi~>q_H-EJm>C}5o%nQL zAJ4X~1>YPLBK)BhZ2`dulRQ&?8@^d-4AxCWv2AtLG$(fp_3feR{7fA7a%H8{WBu>m ztQDg3@=x+N*Y=gpfa^mmuWy1A&aL<4U0I>}im1c6{?R?HfTi>EgA84?whOtNtbYto z?V4~=;_y?_k=-o@u#wQj!h(Cs_6r)}4}S^5xMMDmxZ_ zKa*vorqW+wP=-2Tt&1aNtFO7@0%?+5TOH(6SxEbCuIf;0!`wM4YGlyLxK;yEittO% ziugsB=gef!l;+6^aLl8P-@?k?3Ax^!QUR$qHm@b`2>CE6JC~R2s;P)vd2>y@9Pmpq zF*8OWv37bSLB#D%)C=U#gZfipY0i<9(Ut9`@%_q*OE=q`-U-$@(({Oxp4IPu@y59YFV`GNO47&quil)Ft77-cezj*z zP=s7<8s%)s={-$q_LFW|ddWO`>S>NK>UKO0lm+uEV__fEhUdmE1M{{n?b04_nREwd zY@@X2;}#HVS}J|D-M3y6^h!<=fqzt!)v?0-L2=A@PGBBkRPy3#CBXI*#ZU!ASA5@Ae;a6(-7xi?A#A%FX zqFx?VLRr|@$p3Jwg9g)*L+>f0V=0Nd9QF^!aSMC0r_zQZraUFzQU}xIrF9qykjC?C zAz!RPQntD8C!be0r&K@^r+9c8zLjLi<8XyiGbxbMypn+hboqIz6 zeK*hq!!dc{mN%?9hYyC2_Gc=+$}y=@)71-lq5dn5?IqaSWwnD>{=p|BNYo%(T;Jxf zxr`JzPEU$V58S)BGJG7cX$eG9Rh$a4qp`dBe#lW^++znwQOn_Yq`Kr+&@hW%;&K& zXNW=>wcbF-bWKKixfU3q zDXAA{XIc(75e0guB^6P^WoheW|8W%8&!ymbtlXL_^BE5*8#N))Bz#C^PjlrsC7XA~ zB=zAX)ls)UHvM=WQsDYT5ogGqEX}h^?3Ct+O=_h@AiI@cpntN3YiCRTT;* z7f;dFf_v{ZyQ+nHfsY73+axPZ1n4EU13Gfy^ntC3DM4ob*Cm%M`bSzZ3$xYjul}Wq zPYsSLO1>f24^EFIFQ;y`dE{2SQCR`DE$!zw{cX!mP$}cPN3R=Fw_?&;jrCqk(iJgU zqI1%2Cdy4sjJtw5g5bIjwMhO|SW)}&GLI?se3{5pD{7KQmJzM#{J&?H_Pdzw{@~OH zIrYZE26%aC(t3@XO^@~}=*)WdT9omD-h!pnglPywjAn|5+Tb_Mtp6^eX6@Vb^LR5} z%nBr4NOs>GH@_?SR9PXRHuObj?AZUVst=wVzAR2*uxPpPv^7x^;wr#~R;F_U7@2kt zCz+V!O{%Gt(kPGa+LPrRT?=9!eR?SGud7nkntks5u@wTde-b@v0e=gU+98V6t0YW* z^yy~&ijOHCP&ThOKY}~QYJ;KC_?CDM+^6(kdrs^xC3VH$on0P&S?q*3WI63tpU=s1 zKjb23=`hMYNL1jtv=f|FzId3AIgNi6whudj_berD&CV{tVti{G*+?zEH{C2ztZ7Dy z_Z6kOtlmC<(U5VD+d6;$=w? za$V)38I84H)_;X`ZEk$8$bS`6i#R?utj$MB!CiF?S9=2y#FRl_I$tYL)_))A5}2d@ zCJYMOr!d{x{iES>)$+)n1}KZJDb;w1`0q8%vlVD@CL_gu3xdu37xz=*+0zDYhfn7= zofKjA@X4hv`mK8dJ&Fs>3N)`m2cV9!D2B!&;g|9v$Da=dM^9NmuEshj_zSiT^+`$rY5)FnN)6T(cnN4Z8oTj+^8IJ+-Q^mP@=^au;@zK%sGHZB zK;&ociSJfzCQPZCzYh=2y*q1KpS&#n*21LRyehJllp+5hs(x4_R^tViXic?rrfyV< z77*M}cW<#-71t4?U+^9Yx7%;47*E|2R_DM5QpwJvC7$g4IyoHQ${x3Dj+%7Nca9gN ztzDb&lY zRhEH0Wpn(_Iqs(O9H#o}KR%bO+4I(@Q~g8N0X!sQ^9X@@x zYpktki8;+&PojvQ4RZ4r(N~A!G@ebgQZx8W4^Mojj@UjUB5?Bla`h%t%azY(BfwnS}&s|H&jA(nmJc#UXkZj#RYPW={Z3&j#Kt*!`#YdP-x` zsrhGwDP=^&ciU|}zbvPgCnA}`mbGhL6Ggke2>&nL*So`##$En5XH8;`*|X2DccT(- zHhle>tNfVCQ)}pAPxRd$7lmq>wf)v(>Z%`NN=RJbbjcp`(=R!3>;5*DG1Tdg*G2gt zs>EJY-c0HfQIhw8nZE+gWSSUxPG_(%RFLnt0v`P%&FoNw1<< z6?-~ZweZ`tm;VV-%Z;bL;;kHYBv><|PW>bhE%Y>O)GCj$QZ$~<{rBEqEwS_QVWx6? zDocI<#Z_DGHcGE`&K*#t737ELy1bgVo`vqkKWR&F_~uoL2s-!fOF73|GFTUT>VCZ+ z*XzlTmdP5RWL7~LXAEPpdP55>I_mqw7e{M?I**9oHU{JWI5yFQQ}u65T+bi$(8fI; zq(;H%;>TMwwSuoMgS#@#fZw@C%f1cgOWOF^vz^5n9Exfi61gQ&!0pBraO{Sowc|@3xZ_)G&mAQhLCcju^_98 zLQ51WnN^Dzzr@0u9%yof@W+9#lrDJSnliyz}xYCJvYX48Oi)f z8vG~0+w-W}ekeNYx391r^BL5)w!pgw%3G_pQ}HL?;3xWlV7#!gN(!92a>YV0dy>Wt zVZXK5sZk_od!zQ zs7dn-y^x0WdwciWGkN|u6V=Ml!9`B$4e{VWD>7}&qSB~)4 z4IOB5Amt9h_VbcYx&=&DDUbamVv8N&jBDejxFlyiAacqr<0Czo)D;^=4sdUfuwOx9>DCBTWHAHXTN=^z3`D{+JI7* z0ETKQ0trGhzyHwZOp^lu2P`+MH~^xnF)iuVD5tqZ z)kZh(5gZ%&F!pi~JD&RMp{sX8W<4j|fQceX*nIV488=^%R)vWtZ~Hb(3OlNlxmjz{ z$rI}aE{}DfK**|b)%&4*oUpA2(;gJYh8gl-OzfNpJSXiMmzbKTw|=d16@KKQ|Y|9)@WiCutiFZI?X zHFGGXh-9}jy<5X)FcPt{CcYwxH#sMAxtbKtXXiSu`P7nX!7!`@A)rsPQb2mkUOuf;{t!3*dh9#-*7g!g({5qyS{ZwlZTgN5?cCCR^ozvesmm{RgD`P z&v_Zw2u&dsK}p}n{NqUDa6O{vT-`Cd>P{adi1}X03?X4|2+AeFtGLt+FmHtKrMG+; zI!A9QVEu#OHN>;WZ!>2eKnZ}ICcLAz==;|$$S4m>7rSOuQAhc$z|?Q>vA7?KdOiY0 zK%$*^!yN9nc+;*xHUhQNuzN}Yj-AIGja#107Me57#$Y+E7u&|hK2A$daOzW&3OF~g z!ZdEwPVhr*iN^w4uF5ZS2&Sxf_EIN3;of%Ys3V`G_WiV|!@Yxc&IwKygZ)Jjp5x3a#Oc#p~&rr?{c@%4yxQwB%7z99R@M zV!yn-2sDgE(U&<5^F6p~3GfRp0vh7PnTtm=g^w`JkiuZQ_vuD(8IGkE9-HEtql7KZ zifA*I((lvE$|x{bVPs)zmLY2)Nl!|2sdTj8c1rX+j7n^WqKxhO(3xJX=>+=&+4XU6 zF-P)Y8PX2t-~AeK(ie;=gZ5nSqQlpHmrGIdkN}jVsM&snrxu@b2^CI*L;< z-0pAxM5m5|qLW1XpWpM{@ycaSVwQa^V{7^ zbASFaOf%6k(;>LWYxbecyyrt?4;?L)!TaGt+BxPSP>%+IDnK4&Nm%M8GfNfO=ejBW zk>WAHIftF>5sB37X?Q9OgoQE;>py%RrUH>>DXRYT99o(Zcs5a7x5vd0VZn}T$QNv%9n(%#Mscll<43wd|Ir>4sQ6>n^(Q@ z(jD=_`A~xH;ke~fSkszwTp$wiIAz!z02}GEyZNm2lTha5K=ShN^HSX0&7M&AbDCXq z)1bSHyOYZe+x;KEPVmzVpA}66b-=*=@>8L;m!y>WA-AiLCWE~0wm|HI$F>WOB6qjt zl3&a56XU=1Yh>uPsp(k>0K@XiZUz|+e6I9P#eqf3^_IBL3y=;3f9w~|ar=|VR9NLbksCDg09iJx)^Bx3!VDrq_r z#K&L}MS09w02DnaZ0X^Ec)wi!?|R~7DuZ@>?RGXojx}s&rCd^I!Uq1K%il{58$|TwDz%FL$Bxn||Iu`CVgjflYZhV7QMcHv{-IofDJ8}1_e={_{ z<&B_=SFofkHTsS3IEMcHi!(JjJUqEOle}}mFu$BVpWWx4f<_G4(@gJ{KtUX6S+2dd zVE){M26%xIcvyO@gwMq!NFaae@bE~XcyxC`M2d|J--zF@8ex`JT|4j|hK-CjysUKJ z36R(C$^2eTZ4u0`8uy0F!@Erd2a7>6Zv_hGt0?5O%7_6-2zp%BI*XO0hy+DG+#^^x zN=Wio2@~!iXb8E&Q@C_rw*3QO@Z26-+YHb(n_44$0{@vS>cr}NZ$*qogL!G<>XQ@s z)D*>eJ%1hx2GbxT>1&(wJ8s4vCt1?2{lIAZ#)~ zj*D$TX~Jii%4`7q6i5|IPLx=Tm>4cr`W5QuEAn5WfK45DWg(8>e4Yaj*0$v~Z_@ME z&~(yy89}Ew~{^38h z#eH<|K?7$(=T^w&?R8S5U%KlzqRoGtfmf*8&Um~dJ&VTWwkDHg!L%lLa6mRBg4Jh1};U6WPLl=BXaO` z9?VKDH=?i8+NtYpXK4KwJ3hHo@X2xIgj==B6D*y$JpBg33glx2pw^niY4ei6QpbO^ z;)ifsX_I)@EOr8K>Hx>5EsD?MNUgY8*piVdwsM2bo?VB3RxEeH6R*Yq{NEY($NUt) z1Js_XyH%rciXRi8RQ1#J=YKsPBxk*(U9gHpLq8)J6DAjSJ?BX2N_YQm4siB^HjLX* zk`K6;Wmi7}uxz6xKb6UTyykc&Br|OIpY*(du{JA*-+ZeR$NMy+tX@%4#yrM=Z!+aL zPGE${izdb#w>fZ)BQ|ZUX?$yU`oqLka-0OHzpJ*g4HG`?izCp|>b$#>;mabF`9JR2 zaryV%g~iV0^k~=aQX4_ijMvJWB}DvpM7(~cwOm@_dso$V^&zsmbJLr+!N2@d@{7tx z>;G~DjWeRO^5#@>6s=Pz362P7(-v?+*^5s6s(JC*ZJkJZyhp|U{)Bf3{*re60M~zT zvy##8eU!FTEwoAyZ6}Kgs`}TuZMV1QlwAJH-v4LZ>4T%(WcysZYcb(Ii9*Bu^|d;g z5fl`y>a7=zm%F2T2&Q3ku*f_-bp! zkk822S?1F1pRHazyu?dE;SfO-{T6P7s>EQCfJFUJ=7v>2kkM?Du}l1rR&mD7w1=4F z2devsP&AdyP6=Hfh(hb!BGdLBAb~}JIc11Ws$Y1c9~{YT#!g`#p*RmGu=ZLIP(4C$ z#^x{^L~PinO{M~+fyvLN%_w9TD0i}?gZ+#slXJ7M%NwIsz|cvB-VF8pGLGVw%DH`k;~o5PqTYOE8JYvP^*M7n@DednvG|lS zCaXm^)-rYwrjhOsDWVS?8Uu9$nun)*|yL{1x zMt>lABewA1EdimfTB@gp79A-85bDJPhtR%`%+V`FrV+@(2(;*>Vq6Ic71PiG*75O# zFl7R3C0mBRfAXNR;^6AgLd1=n@{Mm4EeBU9I8_$4jd)9JCIgaXx;rk9tBgaZLm3Q? zXc`|)lEf4E*niYj>Cx3aM`)OJ!i;ocMLaw%rI-&*St3=C18_6#KPG!WJS`IZk% z*^607SxXI~cPq!={rhO04F-y|_dFusj5{nm9C@z@29rhq=dPB?eKBZcC)7MWGwaL6MstFwbE$^>Uf58 zqgUYg(|xmaSLXbCX~jImu9QCwTxdPtWUT#~xmr)B`Eloe0gS`uC|ylz1?Gcx!5G5r zKilfvV1_1Rv24|C4(*7SJwqa_?augy$3bfh$&Ne^Yrb>k_GE^Yq z%W!0tp`esNsS(5N3!QFp#kk`0U)I!7sh$YBZAQC$kpQ}g4k3*vIKZ`gdkcUoQT{TG z61-jSHFsaGgD$b3TOJ2av_OJG8J}|%dXH5=K}aew61aD4z)ql)IV5F)P0g}oCX~;K z#GF7SW!Yed8Oog|?=Au=%o#kGmXs9NB#hT7x3x9KGLbt(dx7`(-epE`&87&cUi421^%Y#& z>tL{L)nPE@1~?`&rSrXoXg=Dq>FwQrtsyed#p)x_U<4&I*|uP;7TK|*Xm`l+(8k9F zCKt&yVz@1}lec>9T1RJB*XhvN)BJoRPN&Z>lwcMn<>r=4Esp!e;@%JM+48CUaL%_8 zXF052G2yFX80lGtaozW-l}~NhmL2CoSeDmOUFa;q7#0doTX@XRmV3I_N_p>qIbS_O zbECL<-78h~T6z|2cAhxrljj6bmw$lO(Aj?z@}qL7?(jm<$nnWW%WQJur@utsL#g$# zsl>vfQIJ&V8EHJUhumPh&4r?xUFtV3_cv$b)(6PB+UQA-gnPpi>w9B+Iuxa4!x?SJ$8MRa)TmR}xCdFAESzFy#hPcJMvAFPcS2uDHF>5#$ZV+z_l->r%c+*zI%glf;)~ESP_^;PlJB5JLtfG} zax=NiwfEbwimV+a2<3ZElV}|fwd^E^dUTXtXurs(5YGI#cru6Y=#cB0s zh&%vbuPqN_8#l;Cu5T=)qiqmhh1ZHXziHfA8Jyj}WYKFNNNBZw7D-^f)?{tW^6m0}3Qj}U%eYww<AA5dxt8CFju7^9dD zmM_p*wJLUPZuI1Y@WqKo2Ng4^Mu*PO9^S ztDV-n7jViG6zLMJo|{MK_!J#wU+v^}x8J2~_F72{3nLE6f&D_YwZ);b1VeY{Yqy3f zNeTeU8LzGBSuIZ|V;v`~w|MEu^Ami2$iG@L={LrS%;Xz`#9IiS6>*cOkV^mO*i8TU z6Lq+oKRhO?&^f!y>GOJrC`0aBgZ>se*!_x@rGo_3=8(2)&*hFc#up6>Y3E&^D0G@} z9RVh_M~6ox4B#2{i;%Zn^F>5MEI=G*pkHv;=Z>!pn_DNu*Xw70xxemi^9tcc`IU>i zDkXxh;LmB?(oPEdfmF!)SJdIo>o+}kxb(C<<{l)smS&Z%@J&)(y3khPeLJ%bhc6%^ z>L?Whx%w9=(GG@lTaJQRDmFbL^5TOdc4Bn4phC9MQ?`>K&_5MG5k)M8goQx@09Cz< z$RlXz3=wy;H-vzMa{xj~LK(>jcnStTb?VU$FnW~>a0XZb^y zD``WgYAKKuFkX9-YjWxIvqY84RpF7ah^~Ow2L8bRx}kXyU`Kg2mvy~VUiPVxTMj~l zMT6gU2VN@ngC8l=m1g>`L*JtFkQ7lZBnCb@tnW8uYICcyh9I!~d<8e@;`Ni0IhhxO z8i#Wkw3V;~NFfrGVs&EEDJIg+mdS82r8{!XJ50gU72t91wKAW1B~6A-4*qlO@ae4w zjLCLV&dK1 z3sqHL$J`&X@U48*nT7JbC9t1aAtHvd1+#!MiFD-JrXm9lyY>^-Yy*hTh2{(l-fG>4 z62p*vq}G;_RuQDkbkex#&>bGzhhdUo>;TE<8fmXaN#zUL8Muo1BC*)xKfABwrO(CL zX(zG&*v@05F!AZ>v2U;+%DB4X((&#T{(*TSWpMVef0$w_KX_7B`NQ-O8`46tx?YQI zRZM|EMI-N(GVVvzY~TR`!9dcd|DgxPvv9Zwq*7^>5;m|18UW`^_#B2#4nxU_ zH+jVM$D+CG%gS2-vl$A-qI|a&@<##LTtN|sJhf=~VLZ!wfV-wp8giMNPn%Y2{XxJv z9VjAcswT72T>glR=GoHaIXfX6ZyG6v51q{eO9a+bSi=Q3V(`-HS2s}?==3O1H3hJd z)8p$SDOcB2`N>*cy!p;zSMJ1%UOxdtiRGjAf0jNA7kb4wOS?Ub_nnmF%^8js<0mO0 zF7edVL<%^0jd-lK3l1Z(XPAMjuC{^C9KUQv=a46Y63C>}Ns(l-$EA$0azWAB=AIrk z%Lo{7zv=O%vMURWkC8j0vgs?h&!zjeqH6|(?GiinE8&(5wtJ=?m-gN?Xn`@GMBu|G zLHnu-kv_x#B~*~?xolnL<1l7wC4({PUH>-(R7j*ODI1H_qPLT=kWr7{P}c91XA71J zB8m?v=g=pI6J9(ZkQslK_DTcy%=E%eyLKu&7pq0`mfUD}+v?9~b|=mufN(UwbTF4N6FV{=~_X0oQftmdXEtH8V}5 zLS2D+qkspa1u`>Cggj6gg{e@V-rNT65kGeq`*e3Z5vrOM&U+Zt>m7E+>Cbz1_mZ3KeG`07^7U(tR}q3y*;()Jz?3hEFrJq;Ls8^sedm9+RuA*gJ=Wb_U!~Nafg*)nQ@=bw)@u*ZR&tuKuS30HyRRj` z)hYb#KWrvv*$YG^P@~+Or~b6worLJp%wlhFKjJ4NR}NHH&bD9IzR%WsAZs`8X>Ia8 zZehchg}TCgI=qxx^p%>uo%;30GP4vx6Jx}Bnow$^TIHx1Q?>NBa7fNqPr+<3;C;=Mj8vc1A6@%uL9gucp{OlFa1)c$tB7ycTP*K^ zFlOZOYPd|08Ae%|G|tIIJn0`io1{ut^lYyYE=^;f8*t-47Zw4&&xS_!gx6yyMfa=c zg0eIK9qSDJ(dO_WzH};C78sft+XRi4k7gj78!CLYZBPv2D+O3uz9a;p*<8b5kNH`~ zNa8rRU_F4s{5R=rnVJI4=UWT_740!9Vnk|$Hx-c#RG0i=S95S0IvZcv~EcZC4*46Dw>o1tyo zIzAW+O|rEP?HZ{>Gu$0Vt*v!+F`gx*a}_OTyNXEYjZ=ZSBcy~)*1FWRF8`jP%&OXY zKWdhh&i)IScB#weOzB}>BYib#{5^v_KE+ZKScQw_s}0Z#z1VgVN{`d|Vk8ii+W6;i zC4&;kouq;`DM{mJb~9M#e%-+cr)Rb}3b}=_HejU`&j3x^JdZ{)Z<-Y}qvT5-=M@q}~xXKeGK-vqPSXs0*h9K-;4PN3sE!2KJ>OI~F zJd*%z`sm1u$S3`f>a@jFOmOSJ<_5~E1HWl<(L1_EYBK}pahpcb60jaRcqbi8L;pwG zMs7;091SE9tD914AdlfIOfk=CkrQ0n{tJpKedp67B%rqWuu!F`(MSs{Jd^@+M+gzf zZdZS8nDt(QublsSKALh9d>4494Sas(E0XJz?If8ncX=83F2&n ze0)ENlz@3ShjyG!-fGtIYzA!!E7M} zo(VOR1z!Sm>M{okIAYC#--t%uDW=Zc%j6Y=o1R*Ef6Vrju(Tficcojs3grT5x`v0! zk;&0ZNwG;$-{7&^IJfRa6JFidrP=@bZ&CCNZ#nLkEoFcR1S5zJi%#+oB;M|4c^{_| z0U`Io?$_zR@f&NRZ+2UZ)Vx?V3|lxU6?xk3A^!E3+;h7u8cNyT(9JY0VuDSTP;k}B zjTyVRD^HY~&~{hOgx7Rhxcxsv7nq#NZ!C#Akayba!b?4&W&H_=OtT&-j(7k%{>Z*> zkPhPcezlB#Vv@zkC$AkVd`5{eCS$a;f5m8%B4dF^J8lho6 z-jMevtAM}mr*T_?69tUy#b7|R7NAImlKPUc_s^fY?oK}v=;rYpDk0Jx*+Kn^9Jfv5-IB5z}gPqHq5fHjtlKVYagPz4K zT#nOzMJWbE3`OoEXCyWdqgEK0KY&&Fqbz4`S-~{}uHWBBC`Gx=KrN`8^U44Zp2=jp`NI0XMa5sT=t=n`jAmow2~CJ~6Gexyu*oSNbY z8+DWr;(>-WE)cRr-fiD)g&4c&T5B?xlKa%IQp@S+Ze7`nnew(SJ+y4%e$C|(0S!C; zecr6NlOKE`=7Nd)wgBQAj4%&gRx`*YDbzZa`_HI zjzMD4rDjH2lgx4vVEQn_VA2m%61lk3{B?1XE6{RJ)7MQ&Pw~Y zd_^kIBw-={T1sp3(Di=@20dCh&o{x!#5Ry^;KHZmBZ+iIa|TJ`ZpCY+2R> zEngCT7g96`F`nGE?^Djk-qHCHh5>TLqLQkf>F3x@sghkX(dLiS zZAo4LjsRwsayEveZpr04_!mXRx18^2%=ezUekmAOf}zAS;g;+w;hK4rD&h472Wgtl z3<6;hk5qu1HG=%_DgBQK;(-bejY1ArN}c1lO%LXZwTHf}!&uUML42zb#|AMNXpF1Z8Y@u2Uh{~+|rYgu%7b;j@1&=Sch^k14dX>7m;uRw{wI5TaA9~ zY`wl;R+Z$LF{!ytTG3)E5p*of-_Fi$z8IJUiJ-byt8H|wtFMo5%k9$*ol@9Vj9g#} zBfaK(U6uoi3mo56r+r&A-{}NXQJ!r?O_>J8R~Bj6f1d%#?Hl(Pdn{- zlzHqa_X!JRz9DxA!a|cg46^~Sf3(-fBzDph(T2W`0Jg zU3-g78>6LG6}3m~`OlmGb>&UoC0Fu&o^$SV-=7ogUb#WO#~xFrX8K9MK4tu{fY*UW zdGGhm?#1pJS)gD-qG0AK-Z@!W4K`?dq&>$}o>?RYq$VX!wxP9)q1WSQRLj=pb+m|1nX+>xQZbYwWcm{HsOv|=c z|20rA%AH?-u%^Y&&98ChBN)J@Nr70t+PCiCooW#eX!8FZx*#VPa(cO!UVA6`r}?bS zi|nZ>S1%W|={qrA_0)>L(h|=cDT(%J0-=Z(xYyft=A(Ev@iBFt@?VNeMQwE~3IL(7 zv-9(FeXDO;M)6Y>Bcyb=iQtQUk}w+RE)i6aa8N5dRBR|UnYXpKJ(^`#J`nG?Nx17= z<52zk22b~~n7Hsw0*_<-sPA#lX~#oVQGfjIc*iVndipR?e&1}w?(bac^j~G~<}~YO zn+KFN0yMPThu8!wjNYO>q6jlneAyF_5~3@=hvI%elCt0=Xz=hiISE&^G$+el1t^u- z9Nfu~ME5B~@=5Xz_M~`t{o#|0W6M{OZ8c-R0D^3tq-veZ6N0M7!|#MSm9=>8Dm--& ze63Z4E&;17Nzh3*Q`qVXwZ8XC*IYPR*J*s9Obj6Vx@T4MyF{OjBaFN&TKyrX!M33F zyCw@uOa7H6T}Rr&e=t+Nb)-JigokvfbOYv~hUXiY*oM*$sNVl~gpn?Cd_6^TNd_4H zW;LQ-%Y9IK8G-kao2;H$vCg%MvSFg&BZU#EXr=nbP|-wPIU91K;4WFa(mItm$xJ$G z<`W=!9JJo|$hAO`cxNB3$m!;4#kZ#UcBg1|)PYVW3PpxMXjz_&>m;4B%DyMmT*cF3 z%Y(x`1~Iz=x0_W-*cB7M8RJHNe^#wtZhruMYFxglK4dZVv_8VKAXUD)Cuo}$P1}2-E1gudJ0!d~!h7_bwoS)1rS`+1nMPsg4KVv*Z`y;|Q zi10Pcfe!s*aCpFr*ReuWFC>wiE4iMwIhn>EIC|mp3C?k2&ABApsk|wKgzU-czBi-R z1CwV@X$Ngz<5Yy=Dl(ao$U8$eM}X&&Ou??tW7)|8f>G*F>nsKiGRg5Fh&rNI>fPjD z-u0AFkH#T7BwH4-B8S$dL`HQ79IW>`Sn+8D9RsW+MKxcmqq%)(=+IO0vIYu?T$&rU zVe&EMP13!FpByg-7`+@mXUj?NtN5C_b%|%#Yk!1!Ao8uHZHJ#h{)HF(QEn~*Jo}_u z#X`KxROg$uI$KJ4`ApxRxUV3Zk4*!3Ao1bFN%?pZYnAeU*p-@=JA2Md%tW@5M(x%?T6?i#}C#UD=RlW zy6A_Fo(1UV?;YU@;nZskZ{F1U@Xfj4QwFS;3z0RY@AX#`z8^3D+byW)FN@Rb z1ZfOQ+pfP{M7IEbo~-qgd)=N#;>c9hK8Q}&DG8(?1viz#04rf(NwEHWHz)bA3!8uP zl+zz#nzYsd_FyH%BM?OC?<0wn!7Dxzt!Bujhnq^qAlcd_uTup>ohR+pOW0MN__@O zGAB3Z9z;kv6a21mVJ!V^GG=)7iGagwM#0c0m-1?5Jm8rUO+BVLkN>aRZJ*otI?lKLn>p3+ zXd3kF%H5>G&)oWX_vn^s7KRA98q~00VW60hJ9O^MB+3xfliKI$;p2r*x;;wj`h1y` zhrBBci|!!;GRYulww!1cMO_T&w zwt?%hpzk9-Ph@d?h-D@G`Pe-{)m1_Cm20Ym(kxv_)1|Jnn9)9B`ze9>bSJ`t->tDKl3Et-`0i~42L)9nciB=aRtNNiBadlC zHRgl#U4x`nnRW}uL8~zq7C8=NX~WYLw#@q|eK%WiZ%BBF>|-F~Lkhq;_8NORo&h71 zw>s6>aISZ0&J|)qe4P%!U_$_UGni&s8BoJsSLc&!eUvba54BLx@#e9o zf%DsjmPadRPNAV*_#sj+pEk`DEsl*DLVZ6~-qPpU6e|0B;Wz(u{QU7$MV3_}hFb2* zTVe*YlDzb@0n_l1t;oqTV#xyt(NEbgs`ybr4AC|laiRFhR)hf?muQPXe+2?6~n%vz>4db3r{t;ZYrBH8?dE)mhenS65k%40ocC6$YKNw zns|S3RILrKIt=50>>m$%o78}r#>30zJrJ4?Fv-0j%h##9bCo(jdQC`RH7qYignGfS z{!A3xC-ux?eb2Cu3GF)%yP8fRuz#&lAb4Ry!_EYgqfpEO(-!e#^MuqNV@^qh7Q8=Z z++3>~jN@%(H(c941`uyg2xtDb8Vfx;|14vOvo737I2_Xnj1z)}QT}nNS1_dleSlp! z)oJiOIB)T@TC{B2+0wKuf!RjbNQq*H16tnuNVC{?#|hu=wk&d$!ew$M1*b5$4fl#Q z&2?Df+5Q!q1hr5@dL_3p!hS&s(sR6%_(C#4QnQQzzwimRcrZ1-c)M}1tVB5t7fIB3 z`p`Tm&>wyD(?}S?^!G8tn}GUM^(a!pgLC_5CbTEukHs4unXoH= zNy|j8XIg<-I+(&!8b!$?GYy(H5j(u@?SANDXy9vvnHwwX=|VK5&`;8zS`R5pwmw%f zhWBR1mblbEGxwCaS0*76r?aE*entA~XhRmB#E$+H-{+aWmMOMUlrzAfY=j>ASMNi& zGGdCEeEIruSLa5oo{qf@+kqZ71{v=%Cw}Z6d`hY~{|R?J9|W}IKjr!$X&luO%zOr`vnW-Hxh z?4sV=$pLy}_a=-Rw*m}>;vfw>8yRfwZ6l-~3a6uM18?1@W3rau zsubkAiTY-F+u54j~mvTS5?A* zu^dX`SNkhTuqg4W4wl*7srjA278q*OczAv{&ZZ(NL@aL*_1@Zeu6}@4F~5OC_nX+P zO5?(=Eixck2st|}F@EOxUMAbkH051GxwHU!!#G<-zXr`#Bv+1QvahH2LFITW&Yu?3>kNX2&<$U=%8jDF+ zb2pF^8};`J#`5TJu>UM?Z*=I^vK2d(XQK>z_+=y5z2CXtWYHIi&)++Z#^tr&N+=^~ zL<5g>ber9D9RIyYd5pS4564od#;CJ+AfY;`Ff9jrm00A$qPfeYplI~Us{iDy-N=zB zhj3oHAx?~}x5BKV$`!zA+*qZYE3Y_oZd-4Dp`NL}UQ3D?ctZmAogIxk%$B?LQcP7h zC0BA!w1&`p?;@Wo?xh3s5J6`@^HD&f08ov1q$VfDht$-UKuXel!7xz%G~FE<5(Jz3 z0&0qq2w^wr?B1Lz{GU?|i%i~SGxf?}bz!ZiywQzn?oLoZ|oSPmAz*nHI!Uf0^fuDN zGMfe0bSFJMUCjJ?He-{V^xe?MdUUv;GA>IthDS>O}5XFBI-oT0#VyoE8w!&C6p=v}{>9 zxH-T(s+#JR1ir(xQ;#K-Ic0kR7vQPIP8O_yNdhro&d7$hzx{Uq`f~pGsn&jA6Zi06 zJeD=P-O--r#pHDu5zSz0h8utrH@R7Q-Y3vOOFXqxy|Edi$Eg{6W^0tV*)5@hte6pb z`^hs=p1DoRx9#rkv1^;9TeN1a$(|^%a2hQf7cl=H2n2BE=q?UM-T&WI?blRjMS+fuoG zz4;i?zRM-rUGli+bAKL-B})=}5l_m?S3QE?!ziir`&+B&Rn(z}EdFu=wV+_Lmk3QY z>l7~tCFT81sUXsG`>#O?R~#qKkVEmPKM=vB`_!0o~HCBcEA&!Ns1-{6!U-CXVqWbe|xfc zb6P7s)w6$bvNko}u*E<1PQ2U!v@+$n>az9dqSoq$jM$Ac-Cl&VcjdY;N9z^!<+YyL z`EI;Gi>s|NnhG;~?(ZLv8x;#X-`!!bf3_7BQf(#@N05rU+yg_J+6kl+5)T@nTFT+c z@vAYF+-f!$bMMN*0sdOofq71o+ybTbT4lH3A-3J=k`PspT+PAx+Gdon4ad_H8JFnm z(&cOY0<&eSQWvsD^^>nX3KIf4*6>>Jrw5ml*+ctV$`iy=jmUs!-f-S3*NS*>x3p$3 zW%>oN4N6NML|$mm+4OmQTwrdrjGJx<9^UFkP&=^2ek5_I3h9;Foe4%@EsT-Tt&RZHcH~%=;k_Iehe?WK3IO)IJ#;Zjle z!aWrUvu?R*ZD~5DsyjYASXr;g=ouKtkloIN-kwfty95(;u_H=gLDxG~4*1>-iv0h4 zL=#6IY~1Vz`5c#-83tr(Ih$9^%ruG@>a}dS0K)H5DEtLdS=3ro6=~JllIigZ@1iAT zg2fsxug5C{p zC~K%fzCs}Pd6(@atVsS|=JO9J4!njC;HY?l(A@tV*uS(*?_iqC8S9s9I|8Mh5J9SW zqZRu_226`a6kPAa^ESJf=CCV^6?g zrb!`Gf?YdqPIivV1vVWhiLh#k0$9S$hUi7!Z7U6s!nM>sb?5(!}Kg)T|zgy`gS)-?&ij?L+TOs*x9~yCrd~` zgmq^Ha|-SK<(UAA>*6arJl_z~yxZ-5RM{dLSm#pMuTE&tF2^se0n3N_r*4ZOZ7pr( z**d~36wQIPbSWp-OOcU+zZXO4&FO2M+bspejQ#xF@>t~J5wt^f(<^WQ823_ROrZ|g zk9wgFi+Z75c$((ntoSCo(xv&@DX8=nGXF1y{Z9$}=9+Jdh2w{ypyrvv0#sF+ah{wU z-ka zl}B@|kC|b(?hxnG!yrnX2zF(f7*)RrJox+TFUM4LS`?Rr0zM#%TZT{URTvTCg>G;b z!3KpC)$9{J33PVEKL-5U56WBRS*9%XmtdYaqw zWZKQs7xTNViSQgtx3vCVm$*Y?r6ZtX#B~4pGb-m!w^o`Xmw($8?)y<*Y#g5yp7oZ0}j4LoR4)Brfcp3lXX}Z$l@#u>@0bvFU zan&TNL~YBo#p|0ExAt?D`k&SSvf>BBQ=d`723fHu-Zj&fI|N=X3qb+&^1NNf!~ZMb zjT816%NlHNd5M|?~b*8cGQBU{P^ z-{b%4j#aW~lt1sLpCgxIV1E!8Hi<{MG#`K-VapDFBH4Vy&QlFsnVcKEdJZV09>P54 z<38)nk&OMs_?@wTZa0Tjp@i!7n~L5;-04ac0jL1Bk-NRQ9L7~=#N%!p`G23j7>zO;96{INY^TxNuS7NFVQT-qkQ3PZ6H6&r5-XVWO|;g%G8F z0OGJ59sQ8noVW4rnH$SB7REUrD4tR{#PSLe$-a?tqLe2nIxk;p`$;A|uvz|qN;Qhm zQ?G%rDSA&%D{V&VnhM~8e!NIx2!oWUP@bIQQk#$V+OG%^!rYNd){w7b?|d7BMa))* z1L}YBMffQBc`sO5>Ja|xjFGIY*_EZZx&n~KhRY(F>WfEmEG3YS)FbU#LXGnw?VWyq zHi2M+`b?I4Updu#Cz7rwgqCsW{MSUFq+U)_Vu1EPhQ;dxzs0Lxje$ST_HK?l6k<-4 zM!6pYQKknw0-<_*59a0*J?INCD0LnQCDkN|0!)JL>UiGbi3A5!-D_)UYNy;YxA%>5 z+G`b6`1II#*^hvEvFDB>qB_H?w6@-otrZVMG{T`HWu;mi_9L6>euYe^)7_(>SvkTb z*S_e|=<7-Gl)Il|U06NJ2H6aYAl#pq1EI|{$Hou8NM1 zBtPM~Ir5}czqpulLwhAr>mqHxr~<=B;)BE%E$24}<2V!sX3{l6I{fO+AkUF$H zuW8w?_Hmax4)25CM?-g-PmZ(2Gwu|Q2I+mO{h*a9s#)b`sSc|}Tp!MahWNC}u&_jz z6f4UsS5PB8?66zLjiQ4iq6yAlVahRIaw{8#g{yR;djdrS)(xM%LMICJwF1CSn0GI` zHkM>Q1!Y9_Ij60Y-W*$9-Di7wPv+>~!Hq)8#J$mLT&fUmT{n*a{@XwMCYP5uoygsf zxlE$BLpr57E|5Atg8ft(XUq;fKENdHY+POM-FC=bFSD}d%C&D>f&YM0DtD+Z4UAB@ zqTEvfXHg4r+9&W-XMGnO3$?&HKA@$AKkjgM*TQhEAITr(`Rw+BVQ~mSl{#8BRgZ79NhEb6n8w>~GZrrpf}f5)bh%Yd)^`1rm$P?rM<%SgV$G?2M6n2FNX`EizRN1Z&y1i%1F~|_KtQp zjn)PUUINeRc^AQMV*;S1rLne5a5m`d+|GdD13mLvEY%F2WtH8{+sm$;mt)P#qsrhE zud>>50*&1qj)!~_Q~>ii4twf(>W~!8h*yCrqtjOeLXNlQNAKD5$}ffWLAZp4WY=nG zZ9W=*$m*|COO;;b-PycZwGVw?JFMRv8h`*rLnzk4)-rFNP43=uZRi9(o1-P+s|xut zzgH!7E#Xlx`hKM($4LrjU1W>k<_6n#B506rquy|EUWp*I^twIM5^_%0TneRVFy|NZ zduyhC62sjEB~R+57JxOA2HN2D)#dD)ks?83J~4V$&N{p)>Q;)%mD(7 zcwteOJ-+qQnzaRQvsS8UJ@iK$x++R8e=I^%mba+0|B3T`(cWkTP~TgY`S;4zmY=)XjfdM z&Us%EM~+Ou1Q@%4Dlu^Y8Fmit)c{T8|GfbBzO0&asN0}=q44sO>L0$!9EPcP%${A6 z9KAtV|9iFWBBv377H0u)Vic6Q6aSk5nP@kCqW?wabu>i=BrQYx8@Z1!H+JS+6N5fPv`1mHf-y8Np?#PPL6ArXwHoN7*t5mJ{7nQF<{lnl~Y0`%xNU0uEVUE*S$85eH+OaO{^mt2@um{@h6G2^wvn&6BlQqJ%- z{G`M^d9XSqjM|m9lUnuR-RksaxhL}vrA*0%0Asr9zLHk<$Oqbdf)nwk8ky@(=x^?f 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 literal 0 HcmV?d00001 diff --git a/src/tethys/niluswraps/PDeployment.java b/src/tethys/niluswraps/PDeployment.java index 364a9a46..4652329c 100644 --- a/src/tethys/niluswraps/PDeployment.java +++ b/src/tethys/niluswraps/PDeployment.java @@ -50,6 +50,10 @@ public class PDeployment { public void setMatchedPAMGaurdPeriod(RecordingPeriod matchedPAMGaurdPeriod) { this.matchedPAMGaurdPeriod = matchedPAMGaurdPeriod; } + + public String getShortDescription() { + return String.format("%s %s", deployment.getId(), PamCalendar.formatDBDate(getAudioStart())); + } } diff --git a/src/tethys/niluswraps/PGranularityType.java b/src/tethys/niluswraps/PGranularityType.java new file mode 100644 index 00000000..ee898d64 --- /dev/null +++ b/src/tethys/niluswraps/PGranularityType.java @@ -0,0 +1,58 @@ +package tethys.niluswraps; + +import nilus.GranularityEnumType; +import nilus.GranularityType; + +/** + * Decorate GranularityType + * @author dg50 + * + */ +public class PGranularityType { + + /** + * Nicer formatted name for a dialog. + * @param granularity + * @return + */ + public static String prettyString(GranularityEnumType granularity) { + switch (granularity) { + case BINNED: + return "Binned"; + case CALL: + return "Call"; + case ENCOUNTER: + return "Encounter"; + case GROUPED: + return "Grouped"; + default: + break; + + } + return granularity.toString(); + } + + /** + * Tool tip for display in a dialog. + * @param granularity + * @return + */ + public static String toolTip(GranularityEnumType granularity) { + switch (granularity) { + case BINNED: + return "Output of counts in regular time periods"; + case CALL: + return "Call level output"; + case ENCOUNTER: + return "Encounter level output"; + case GROUPED: + return "Grouped output (whatever that is?)"; + default: + break; + + } + return prettyString(granularity); + } + + +} diff --git a/src/tethys/output/StreamExportParams.java b/src/tethys/output/StreamExportParams.java index d4842e64..2ba4f2fd 100644 --- a/src/tethys/output/StreamExportParams.java +++ b/src/tethys/output/StreamExportParams.java @@ -2,6 +2,9 @@ package tethys.output; import java.io.Serializable; +import nilus.DescriptionType; +import nilus.GranularityEnumType; + /** * Parameters controlling export of a single stream. * Starts just with a boolean 'selected', but may grow. @@ -23,5 +26,16 @@ public class StreamExportParams implements Serializable { public String longDataName; public boolean selected; + + public GranularityEnumType granularity = GranularityEnumType.CALL; + + public nilus.DescriptionType detectionDescription; + + public DescriptionType getDetectionDescription() { + if (detectionDescription == null) { + detectionDescription = new DescriptionType(); + } + return detectionDescription; + } } diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index e2bcb980..35733a2d 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -218,7 +218,6 @@ public class TethysExporter { * Then do whatever else is needed to complete the document. */ - dbxmlConnect.closeDatabase(); return true; } diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java new file mode 100644 index 00000000..bf2ee42e --- /dev/null +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -0,0 +1,89 @@ +package tethys.swing; + +import java.awt.BorderLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.border.TitledBorder; +import javax.swing.table.AbstractTableModel; + +import PamView.tables.SwingTableColumnWidths; +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; + +/** + * Table of Detections documents for a single PAMGuard datablock. + * Generally, this should only have a smallish number of entries in it + * so may not need too much real estate on the display. + * @author dg50 + * + */ +public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTableObserver { + + private JPanel mainPanel; + + private JLabel dataBlockName; + + private TableModel tableModel; + + private JTable table; + + private PamDataBlock dataBlock; + + public DatablockDetectionsPanel(TethysControl tethysControl) { + super(tethysControl); + mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(BorderLayout.NORTH, dataBlockName = new JLabel("PAMGUard data stream", JLabel.LEFT)); + mainPanel.setBorder(new TitledBorder("Data stream Tethys Detections documents")); + + tableModel = new TableModel(); + table = new JTable(tableModel); + JScrollPane scrollPane = new JScrollPane(table); + mainPanel.add(BorderLayout.CENTER, scrollPane); + + new SwingTableColumnWidths(tethysControl.getUnitName() + getClass().getName(), table); + + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + + @Override + public void selectDataBlock(PamDataBlock dataBlock) { + this.dataBlock = dataBlock; + dataBlockName.setText(dataBlock.getLongDataName()); + } + + private class TableModel extends AbstractTableModel { + + private String[] colNames = {"Person", "Name", "Abstract"}; + + @Override + public int getRowCount() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public int getColumnCount() { + return colNames.length; + } + + @Override + public String getColumnName(int column) { + return colNames[column]; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + // TODO Auto-generated method stub + return null; + } + + } +} diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java index 0049587b..8da66a90 100644 --- a/src/tethys/swing/DatablockSynchPanel.java +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -1,6 +1,12 @@ package tethys.swing; import java.awt.BorderLayout; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.ArrayList; import javax.swing.JComponent; @@ -10,9 +16,11 @@ 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 PamguardMVC.PamDataBlock; import dataMap.OfflineDataMap; import tethys.TethysControl; import tethys.TethysState; @@ -27,6 +35,8 @@ public class DatablockSynchPanel extends TethysGUIPanel { private SynchTableModel synchTableModel; private ArrayList dataBlockSynchInfo; + + private ArrayList tableObservers = new ArrayList<>(); public DatablockSynchPanel(TethysControl tethysControl) { super(tethysControl); @@ -37,6 +47,8 @@ public class DatablockSynchPanel extends TethysGUIPanel { new SwingTableColumnWidths(tethysControl.getUnitName()+"SynchTable", synchTable); JScrollPane scrollPane = new JScrollPane(synchTable); mainPanel.add(BorderLayout.CENTER, scrollPane); + synchTable.addMouseListener(new MouseActions()); + synchTable.addKeyListener(new KeyActions()); } @Override @@ -44,11 +56,47 @@ public class DatablockSynchPanel extends TethysGUIPanel { return mainPanel; } + private class KeyActions extends KeyAdapter { + @Override + public void keyReleased(KeyEvent e) { + if(e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) { + selectRow(); + } + } + + } + private class MouseActions extends MouseAdapter { + @Override + public void mouseClicked(MouseEvent e) { + selectRow(); + } + + } + + private void selectRow() { + int row = synchTable.getSelectedRow(); + if (row < 0) { + return; + } + DatablockSynchInfo synchInfo = dataBlockSynchInfo.get(row); +// datablockDetectionsPanel.setDataBlock(synchInfo.getDataBlock()); + notifyObservers(synchInfo.getDataBlock()); + } @Override public void updateState(TethysState tethysState) { synchTableModel.fireTableDataChanged(); } + + public void addTableObserver(StreamTableObserver observer) { + tableObservers.add(observer); + } + + public void notifyObservers(PamDataBlock dataBlock) { + for (StreamTableObserver obs : tableObservers) { + obs.selectDataBlock(dataBlock); + } + } private ArrayList getSychInfos() { if (dataBlockSynchInfo == null) { diff --git a/src/tethys/swing/DeploymentExportPanel.java b/src/tethys/swing/DeploymentExportPanel.java new file mode 100644 index 00000000..dd9a7ce0 --- /dev/null +++ b/src/tethys/swing/DeploymentExportPanel.java @@ -0,0 +1,268 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +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; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import Acquisition.AcquisitionControl; +import Acquisition.DaqSystem; +import Acquisition.FolderInputSystem; +import PamController.PamControlledUnit; +import PamController.PamController; +import PamUtils.PamCalendar; +import PamView.dialog.PamGridBagContraints; +import PamView.dialog.warn.WarnOnce; +import PamView.panel.PamAlignmentPanel; +import PamView.panel.WestAlignedPanel; +import binaryFileStorage.BinaryStore; +import generalDatabase.DBControlUnit; +import metadata.deployment.DeploymentData; +import nilus.Deployment; +import nilus.Deployment.Data; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.dbxml.DBXMLConnect; +import tethys.deployment.RecordingPeriod; +import tethys.niluswraps.PDeployment; + +public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentTableObserver { + + private JPanel mainPanel; + + private JButton showAllDeployments, bigExportButton; + + private JTextField site, cruise; +// , region; don't inlude region here - it's set with the NewProject along with the project name. + // the stuff here may vary within a project. + private JTextField rawURI, binaryURI, databaseURI; + private JTextField contact, date; + + private JComboBox projectDeployments; + + private ArrayList tethysDeploys; + + private PAMGuardDeploymentsTable pamDeploymentsTable; + + private ArrayList selectedDeployments; + + public DeploymentExportPanel(TethysControl tethysControl, PAMGuardDeploymentsTable pamDeploymentsTable) { + super(tethysControl); + this.pamDeploymentsTable = pamDeploymentsTable; + mainPanel = new PamAlignmentPanel(BorderLayout.NORTH); + mainPanel.setLayout(new GridBagLayout()); + mainPanel.setBorder(new TitledBorder("Deployment Detail")); + GridBagConstraints c = new PamGridBagContraints(); + showAllDeployments = new JButton("Show project deployments"); + showAllDeployments.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + tethysControl.showProjectDeploymentsDialog(); + } + }); + site = new JTextField(40); + cruise = new JTextField(20); + rawURI = new JTextField(20); + binaryURI = new JTextField(20); + databaseURI = new JTextField(20); + contact = new JTextField(20); + date = new JTextField(20); + date.setEditable(false); + projectDeployments = new JComboBox(); + projectDeployments.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectExistingDeployment(); + } + }); + +// c.gridx = 1; +// mainPanel.add(showAllDeployments, c); +// c.gridwidth = 1; + addPair("Site ", site, c); + addPair("Cruise ", cruise, c); + addPair("Raw data URI ", rawURI, c); + addPair("Binary data URI ", binaryURI, c); + addPair("Datbase URI ", databaseURI, c); + addPair("Contact ", contact, c); + addPair("Date ", date, c); + addPair("Set from ", projectDeployments, c); + + bigExportButton = new JButton("Export selection"); + bigExportButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportButtonPressed(); + } + }); + c.gridx = 1; + c.gridy++; + mainPanel.add(bigExportButton, c); + + + } + + protected void selectExistingDeployment() { + int row = projectDeployments.getSelectedIndex(); + if (row < 0 || tethysDeploys == null) { + return; + } + + if (row >= tethysDeploys.size()) { + return; + } + PDeployment deployment = tethysDeploys.get(row); + String msg = "Do you want to copy settings from deploymnet document " + deployment.deployment.getId(); + int ans = WarnOnce.showWarning("Deployment data", msg, WarnOnce.OK_CANCEL_OPTION); + if (ans == WarnOnce.OK_OPTION) { + copyDeploymentData(deployment.deployment); + } + } + + private void copyDeploymentData(Deployment deployment) { + DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); + globalMeta.setSite(deployment.getSite()); + globalMeta.setCruise(deployment.getCruise()); + globalMeta.setRegion(deployment.getRegion()); + setInternal(); + } + + @Override + public void updateState(TethysState tethysState) { + super.updateState(tethysState); + switch (tethysState.stateType) { + case NEWPAMGUARDSELECTION: + setInternal(); + setDefaultStores(); + enableControls(); + break; + case NEWPROJECTSELECTION: + updateDeployments(); + enableControls(); + } + } + + private void updateDeployments() { + tethysDeploys = null; + projectDeployments.removeAllItems(); + ArrayList deploys = getTethysControl().getDeploymentHandler().getProjectDeployments(); + if (deploys == null) { + return; + } + for (PDeployment aDep : deploys) { + projectDeployments.addItem(aDep.getShortDescription()); + } + tethysDeploys = deploys; + } + + private void addPair(String label, JComponent component, GridBagConstraints c) { + c.gridy++; + c.gridx = 0; + mainPanel.add(new JLabel(label, JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(component, c); + } + + @Override + public JComponent getComponent() { + // TODO Auto-generated method stub + return mainPanel; + } + + /** + * Set the parms from internally stored data. + */ + private void setInternal() { + DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); + site.setText(globalMeta.getSite()); + cruise.setText(globalMeta.getCruise()); +// region.setText(globalMeta.getRegion()); + date.setText(PamCalendar.formatDBDateTime(System.currentTimeMillis())); + } + + private void setDefaultStores() { + + + BinaryStore binStore = BinaryStore.findBinaryStoreControl(); + if (binStore != null) { + binaryURI.setText(binStore.getBinaryStoreSettings().getStoreLocation()); + } + + DBControlUnit databaseControl = DBControlUnit.findDatabaseControl(); + if (databaseControl != null) { + databaseURI.setText(databaseControl.getLongDatabaseName()); + } + + try { + PamControlledUnit daq = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); + if (daq instanceof AcquisitionControl) { + AcquisitionControl daqCtrl = (AcquisitionControl) daq; + DaqSystem system = daqCtrl.findDaqSystem(null);// getAcquisitionProcess().getRunningSystem(); + if (system instanceof FolderInputSystem) { + FolderInputSystem fip = (FolderInputSystem) system; + rawURI.setText(fip.getFolderInputParameters().recentFiles.get(0)); + } + } + } + catch (Exception e) { + rawURI.setText("unknown"); + } + + } + + @Override + public void selectionChanged() { + selectedDeployments = pamDeploymentsTable.getSelectedDeployments(); + enableControls(); + } + + protected void exportButtonPressed() { + if (selectedDeployments == null || selectedDeployments.size() == 0) { + return; + }; + int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId(); + for (int i = 0; i < selectedDeployments.size(); i++) { + RecordingPeriod recordPeriod = selectedDeployments.get(i); + PDeployment exDeploymnet = recordPeriod.getMatchedTethysDeployment(); + Deployment deployment = null; + if (exDeploymnet != null) { + deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId, recordPeriod); + deployment.setId(exDeploymnet.deployment.getId()); + } + if (deployment == null) { + deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId++, recordPeriod); + } + // fill in a few things from here + deployment.setCruise(cruise.getText()); + deployment.setSite(site.getText()); + // also need to sort out track data here, etc. +// Should really tidy this up a lot and move functionality to DeploymentHandler with all +// the metadata in a object ? +// Data data = new nilus.Deployment.Data(); +// d + DBXMLConnect dbxmlConnect = getTethysControl().getDbxmlConnect(); + if (exDeploymnet != null) { + dbxmlConnect.updateDocument(deployment); + } + else { + dbxmlConnect.postToTethys(deployment); + } + } + } + + private void enableControls() { + boolean enable = selectedDeployments != null && selectedDeployments.size() > 0; + bigExportButton.setEnabled(enable); + } + +} diff --git a/src/tethys/swing/DeploymentTableObserver.java b/src/tethys/swing/DeploymentTableObserver.java new file mode 100644 index 00000000..1eda9db4 --- /dev/null +++ b/src/tethys/swing/DeploymentTableObserver.java @@ -0,0 +1,7 @@ +package tethys.swing; + +public interface DeploymentTableObserver { + + public void selectionChanged(); + +} diff --git a/src/tethys/swing/DeploymentsPanel.java b/src/tethys/swing/DeploymentsPanel.java index 73e238fd..c41d7465 100644 --- a/src/tethys/swing/DeploymentsPanel.java +++ b/src/tethys/swing/DeploymentsPanel.java @@ -17,25 +17,30 @@ public class DeploymentsPanel extends TethysGUIPanel { private PAMGuardDeploymentsTable pamDeploymentsTable; - private TethysDeploymentsTable tethysDeploymentsTable; + DeploymentExportPanel exportPanel; +// private TethysDeploymentsTable tethysDeploymentsTable; public DeploymentsPanel(TethysControl tethysControl) { super(tethysControl); pamDeploymentsTable = new PAMGuardDeploymentsTable(tethysControl); - tethysDeploymentsTable = new TethysDeploymentsTable(tethysControl); + exportPanel = new DeploymentExportPanel(tethysControl, pamDeploymentsTable); + pamDeploymentsTable.addObserver(exportPanel); +// 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); - } - }); +// 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); +// } +// }); + mainPanel.add(BorderLayout.CENTER, pamDeploymentsTable.getComponent()); + mainPanel.add(BorderLayout.EAST, exportPanel.getComponent()); } @Override diff --git a/src/tethys/swing/DetectionsExportPanel.java b/src/tethys/swing/DetectionsExportPanel.java new file mode 100644 index 00000000..eda59aaa --- /dev/null +++ b/src/tethys/swing/DetectionsExportPanel.java @@ -0,0 +1,64 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamGridBagContraints; +import PamView.panel.PamAlignmentPanel; +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.swing.export.DetectionsExportWizard; + +public class DetectionsExportPanel extends TethysGUIPanel implements StreamTableObserver { + + private JPanel mainPanel; + + private JButton exportButton; + + private PamDataBlock selectedDataBlock; + + public DetectionsExportPanel(TethysControl tethysControl) { + super(tethysControl); + mainPanel = new PamAlignmentPanel(BorderLayout.NORTH); + mainPanel.setLayout(new GridBagLayout()); + mainPanel.setBorder(new TitledBorder("Export")); + exportButton = new JButton("Export"); + exportButton.setToolTipText("Export PAMGaurd data to Tethys"); + exportButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + doExport(); + } + }); + exportButton.setEnabled(false); + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(exportButton, c); + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + + private void doExport() { + if (selectedDataBlock == null) { + return; + } + DetectionsExportWizard.showDilaog(getTethysControl().getGuiFrame(), getTethysControl(), selectedDataBlock); + } + + @Override + public void selectDataBlock(PamDataBlock dataBlock) { + this.selectedDataBlock = dataBlock; + exportButton.setEnabled(selectedDataBlock != null); + } + +} diff --git a/src/tethys/swing/Images.java b/src/tethys/swing/Images.java new file mode 100644 index 00000000..1d73e710 --- /dev/null +++ b/src/tethys/swing/Images.java @@ -0,0 +1,24 @@ +package tethys.swing; + +import java.awt.Image; +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; + +public class Images { + + private static final String imageRes = "tethys/images/Tethys-200.png"; + public static ImageIcon getTethysImage() { + ImageIcon tethysImage = null; + try { + tethysImage = new ImageIcon(ClassLoader.getSystemResource(imageRes)); + } + catch (Exception e) { + System.out.println(e.getMessage()); +// System.out.println("Unable to load file " + file.getAbsolutePath()); + } + return tethysImage; + } +} diff --git a/src/tethys/swing/NewProjectDialog.java b/src/tethys/swing/NewProjectDialog.java index 353ad732..03f64318 100644 --- a/src/tethys/swing/NewProjectDialog.java +++ b/src/tethys/swing/NewProjectDialog.java @@ -22,7 +22,7 @@ public class NewProjectDialog extends PamView.dialog.PamDialog { private JTextField projectName; private JTextField projectRegion; - + private DeploymentData deploymentData; private NewProjectDialog(Window parentFrame, TethysControl tethysControl) { diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 25e93601..59a8d354 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -5,7 +5,9 @@ import java.awt.Color; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; +import java.util.Arrays; +import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; @@ -39,6 +41,10 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { private JPanel mainPanel; private DeploymentOverview deploymentOverview; + + private boolean[] selection = new boolean[0]; + + private ArrayList observers = new ArrayList<>(); public PAMGuardDeploymentsTable(TethysControl tethysControl) { super(tethysControl); @@ -47,7 +53,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { mainPanel.setBorder(new TitledBorder("PAMGuard recording periods")); tableModel = new TableModel(); table = new JTable(tableModel); - table.setRowSelectionAllowed(true); +// table.setRowSelectionAllowed(true); table.addMouseListener(new TableMouse()); JScrollPane scrollPane = new JScrollPane(table); mainPanel.add(BorderLayout.CENTER, scrollPane); @@ -76,6 +82,18 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } } + @Override + public void mouseClicked(MouseEvent e) { + int aRow = table.getSelectedRow(); + int col = table.getSelectedColumn(); + if (aRow >= 0 && aRow < selection.length && col == 6) { + selection[aRow] = !selection[aRow]; + for (DeploymentTableObserver obs : observers) { + obs.selectionChanged(); + } + } + } + } public void showPopup() { @@ -107,6 +125,24 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { */ } + + /** + * Get a list of selected recording periods. + * @return list of selected periods. + */ + public ArrayList getSelectedDeployments() { + if (deploymentOverview == null) { + return null; + } + ArrayList selDeps = new ArrayList<>(); + int n = Math.min(selection.length, deploymentOverview.getRecordingPeriods().size()); + for (int i = 0; i < n; i++) { + if (selection[i]) { + selDeps.add(deploymentOverview.getRecordingPeriods().get(i)); + } + } + return selDeps; + } @Override public void updateState(TethysState tethysState) { @@ -136,15 +172,28 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { private void updateDeployments() { DeploymentHandler deploymentHandler = getTethysControl().getDeploymentHandler(); deploymentOverview = deploymentHandler.getDeploymentOverview(); + int n = deploymentOverview.getRecordingPeriods().size(); + if (selection.length < n) { + selection = Arrays.copyOf(selection, n); +// for (int i = 0; i < setDefaultStores.length; i++) { +// if (selectBoxes[i] == null) { +// selectBoxes[i] = new JCheckBox(); +// } +// } + } tableModel.fireTableDataChanged(); // DeploymentData deplData = getTethysControl().getGlobalDeplopymentData(); // ArrayList projectDeployments = getTethysControl().getDbxmlQueries().getProjectDeployments(deplData.getProject()); // deploymentHandler.matchPamguard2Tethys(deploymentOverview, projectDeployments); } + + public void addObserver(DeploymentTableObserver observer) { + observers.add(observer); + } private class TableModel extends AbstractTableModel { - private String[] columnNames = {"Id", "Start", "Stop", "Duration", "Cycle", "Tethys Deployment"}; + private String[] columnNames = {"Id", "Start", "Stop", "Duration", "Cycle", "Tethys Deployment", "Select"}; @Override public int getRowCount() { @@ -165,6 +214,15 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { public int getColumnCount() { return columnNames.length; } + + @Override + public Class getColumnClass(int columnIndex) { + if (columnIndex == 6) { + return Boolean.class; +// return JCheckBox.class; + } + return super.getColumnClass(columnIndex); + } @Override public Object getValueAt(int rowIndex, int columnIndex) { @@ -176,6 +234,11 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { return getValueAt(period, rowIndex, columnIndex); } + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return columnIndex == 6; + } + private Object getValueAt(RecordingPeriod period, int rowIndex, int columnIndex) { switch (columnIndex) { case 0: @@ -193,6 +256,9 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { case 5: PDeployment deployment = period.getMatchedTethysDeployment(); return makeDeplString(period, deployment); + case 6: +// return selectBoxes[rowIndex]; + return selection[rowIndex]; } return null; diff --git a/src/tethys/swing/ProjectDeploymentsDialog.java b/src/tethys/swing/ProjectDeploymentsDialog.java new file mode 100644 index 00000000..fed3041e --- /dev/null +++ b/src/tethys/swing/ProjectDeploymentsDialog.java @@ -0,0 +1,57 @@ +package tethys.swing; + +import java.awt.Window; + +import PamView.dialog.PamDialog; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysState.StateType; + +/** + * Modeless dialog showing all project deployments. + * @author dg50 + * + */ +public class ProjectDeploymentsDialog extends PamDialog { + + private TethysDeploymentsTable deploymentsPanel; + private TethysControl tethysControl; + private static ProjectDeploymentsDialog singleInstance; + + private ProjectDeploymentsDialog(Window parentFrame, TethysControl tethysControl) { + super(parentFrame, "Project deployments", false); + this.tethysControl = tethysControl; + deploymentsPanel = new TethysDeploymentsTable(tethysControl); + setDialogComponent(deploymentsPanel.getComponent()); + setModal(false); + setResizable(true); + getCancelButton().setVisible(false); + } + + public static void showDialog(Window parentFrame, TethysControl tethysControl) { + if (singleInstance == null) { + singleInstance = new ProjectDeploymentsDialog(parentFrame, tethysControl); + } + singleInstance.setVisible(true); + singleInstance.deploymentsPanel.updateState(new TethysState(StateType.NEWPROJECTSELECTION)); +// if (singleInstance.is) + } + + @Override + public boolean getParams() { + // TODO Auto-generated method stub + return true; + } + + @Override + public void cancelButtonPressed() { + // TODO Auto-generated method stub + + } + + @Override + public void restoreDefaultSettings() { + // TODO Auto-generated method stub + + } +} diff --git a/src/tethys/swing/StreamTableObserver.java b/src/tethys/swing/StreamTableObserver.java new file mode 100644 index 00000000..cd0f3b13 --- /dev/null +++ b/src/tethys/swing/StreamTableObserver.java @@ -0,0 +1,9 @@ +package tethys.swing; + +import PamguardMVC.PamDataBlock; + +public interface StreamTableObserver { + + public void selectDataBlock(PamDataBlock dataBlock); + +} diff --git a/src/tethys/swing/TethysConnectionPanel.java b/src/tethys/swing/TethysConnectionPanel.java index c13efd54..95837d6a 100644 --- a/src/tethys/swing/TethysConnectionPanel.java +++ b/src/tethys/swing/TethysConnectionPanel.java @@ -62,6 +62,8 @@ public class TethysConnectionPanel extends TethysGUIPanel { private JButton newInstrument; + private JButton openClient; + public TethysConnectionPanel(TethysControl tethysControl) { super(tethysControl); mainPanel = new WestAlignedPanel(new GridBagLayout()); @@ -71,6 +73,8 @@ public class TethysConnectionPanel extends TethysGUIPanel { serverSelButton.setToolTipText("Select server"); serverStatus = new ScrollingPamLabel(SERVERSTATUSLENGTH); serverName.setEditable(false); + openClient = new JButton("Open Client"); + openClient.setToolTipText("Open Tethys client in web browser"); // serverStatus.setEditable(false); serverSelButton.addActionListener(new ActionListener() { @Override @@ -78,6 +82,12 @@ public class TethysConnectionPanel extends TethysGUIPanel { selectServer(); } }); + openClient.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + tethysControl.openTethysClient(); + } + }); newProjectButton = new JButton("New project"); newProjectButton.setToolTipText("Create new project information"); newProjectButton.addActionListener(new ActionListener() { @@ -121,6 +131,9 @@ public class TethysConnectionPanel extends TethysGUIPanel { c.gridx++; c.gridwidth = 2; mainPanel.add(serverStatus, c); + c.gridx += c.gridwidth; + c.gridwidth = 1; + mainPanel.add(openClient, c); c.gridx =0; c.gridy++; diff --git a/src/tethys/swing/TethysImagePanel.java b/src/tethys/swing/TethysImagePanel.java new file mode 100644 index 00000000..b0a74610 --- /dev/null +++ b/src/tethys/swing/TethysImagePanel.java @@ -0,0 +1,50 @@ +package tethys.swing; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; + +import javax.swing.ImageIcon; +import javax.swing.JPanel; +import javax.swing.border.BevelBorder; + + +public class TethysImagePanel extends JPanel { + + private static final long serialVersionUID = 1L; + private ImageIcon image; + private int prefWidth; + private int prefHeight; + private int size; + + public TethysImagePanel(int size) { + this.size = size; + image = Images.getTethysImage(); + if (image != null) { + prefWidth = image.getIconWidth(); + prefHeight = image.getIconHeight(); + } +// setBorder(new BevelBorder(BevelBorder.RAISED)); + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + if (image == null) { + return; + } + int inset = 2; + g.drawImage(image.getImage(), inset, inset, getWidth()-inset*2, getHeight()-inset*2, + 0, 0, prefWidth, prefHeight, null); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(size, size); + } + + + + + +} diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index 72e07590..49b01c0b 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -21,21 +21,39 @@ public class TethysMainPanel extends TethysGUIPanel { private DeploymentsPanel deploymentsPanel; + private DatablockDetectionsPanel datablockDetectionsPanel; + + private DetectionsExportPanel detectionsExportPanel; + public TethysMainPanel(TethysControl tethysControl) { super(tethysControl); this.tethysControl = tethysControl; mainPanel = new JPanel(new BorderLayout()); connectionPanel = new TethysConnectionPanel(tethysControl); + datablockDetectionsPanel = new DatablockDetectionsPanel(tethysControl); datablockSynchPanel = new DatablockSynchPanel(tethysControl); deploymentsPanel = new DeploymentsPanel(tethysControl); + detectionsExportPanel = new DetectionsExportPanel(tethysControl); + datablockSynchPanel.addTableObserver(detectionsExportPanel); + datablockSynchPanel.addTableObserver(datablockDetectionsPanel); - mainPanel.add(BorderLayout.NORTH, connectionPanel.getComponent()); + JSplitPane southwestSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + JPanel southEastPanel = new JPanel(new BorderLayout()); + + JPanel northPanel = new JPanel(new BorderLayout()); + northPanel.add(BorderLayout.CENTER, connectionPanel.getComponent()); + northPanel.add(BorderLayout.WEST, new TethysImagePanel(100)); + mainPanel.add(BorderLayout.NORTH, northPanel); 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()); + southwestSplit.add(datablockSynchPanel.getComponent()); + southwestSplit.add(southEastPanel); + southEastPanel.add(datablockDetectionsPanel.getComponent(), BorderLayout.CENTER); + southEastPanel.add(detectionsExportPanel.getComponent(), BorderLayout.EAST); + splitPane.add(southwestSplit); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { diff --git a/src/tethys/swing/export/DescriptionCard.java b/src/tethys/swing/export/DescriptionCard.java new file mode 100644 index 00000000..13b13ee3 --- /dev/null +++ b/src/tethys/swing/export/DescriptionCard.java @@ -0,0 +1,30 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; + +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.output.StreamExportParams; + +public class DescriptionCard extends ExportWizardCard { + + private DescriptionTypePanel descriptionPanel; + + public DescriptionCard(TethysControl tethysControl, PamDataBlock dataBlock) { + super(tethysControl, "Description", dataBlock); + this.setLayout(new BorderLayout()); + descriptionPanel = new DescriptionTypePanel("Description data", true, true, true); + this.add(BorderLayout.CENTER, descriptionPanel.getMainPanel()); + } + + @Override + public boolean getParams(StreamExportParams streamExportParams) { + return descriptionPanel.getParams(streamExportParams.getDetectionDescription()); + } + + @Override + public void setParams(StreamExportParams streamExportParams) { + descriptionPanel.setParams(streamExportParams.getDetectionDescription()); + } + +} diff --git a/src/tethys/swing/export/DescriptionTypePanel.java b/src/tethys/swing/export/DescriptionTypePanel.java new file mode 100644 index 00000000..2fc968cb --- /dev/null +++ b/src/tethys/swing/export/DescriptionTypePanel.java @@ -0,0 +1,104 @@ +package tethys.swing.export; + +import java.awt.Dimension; +import java.awt.Label; + +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamDialog; +import nilus.DescriptionType; + +/** + * Panel containing the three test entry fields for nilus.DescriptionType + * @author dg50 + * + */ +public class DescriptionTypePanel { + + private JTextArea tObjectives, tAbstract, tMethod; + + private JPanel mainPanel; + + private boolean requireObjective; + + private boolean requireAbstract; + + private boolean requireMethod; + + private static final int ctrlWidth = 40; + + public DescriptionTypePanel(String bordertitle, boolean requireObjective, boolean requireAbstract, boolean requireMethod) { + this.requireObjective = requireObjective; + this.requireAbstract = requireAbstract; + this.requireMethod = requireMethod; + + mainPanel = new JPanel(); + if (bordertitle != null) { + mainPanel.setBorder(new TitledBorder(bordertitle)); + } + tObjectives = new JTextArea(12, ctrlWidth); + tAbstract = new JTextArea(8, ctrlWidth); + tMethod = new JTextArea(9, ctrlWidth); + + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + addScrollablePanel(tObjectives, "Objectives"); + addScrollablePanel(tAbstract, "Abstract"); + addScrollablePanel(tMethod, "Method"); + } + + private void addScrollablePanel(JTextArea textArea, String title) { + // TODO Auto-generated method stub +// mainPanel.add(new Label(title, JLabel.LEFT)); +// textArea.setMinimumSize(new Dimension(200, 200)); + JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setBorder(new TitledBorder(title)); + scrollPane.setPreferredSize(new Dimension(scrollPane.getPreferredSize().height/2, 0)); + mainPanel.add(scrollPane); + } + + public JPanel getMainPanel() { + return mainPanel; + } + + public void setParams(DescriptionType description) { + if (description == null) { + tObjectives.setText(null); + tAbstract.setText(null); + tMethod.setText(null); + } + } + + public boolean getParams(DescriptionType description) { + if (checkField(requireObjective, tObjectives) == false) { + return PamDialog.showWarning(null, "Objectives", "The objectives field must be competed"); + } + if (checkField(requireAbstract, tAbstract) == false) { + return PamDialog.showWarning(null, "Abstract", "The abstract field must be competed"); + } + if (checkField(requireMethod, tMethod) == false) { + return PamDialog.showWarning(null, "Method", "The method field must be competed"); + } + + description.setObjectives(tObjectives.getText()); + description.setAbstract(tAbstract.getText()); + description.setMethod(tMethod.getText()); + + return true; + } + + private boolean checkField(boolean required, JTextArea field) { + if (required == false) { + return true; + } + String txt = field.getText(); + if (txt == null || txt.length() == 0) { + return false; + } + return true; + } +} diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java new file mode 100644 index 00000000..1e67d67d --- /dev/null +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -0,0 +1,146 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JTextArea; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamDialog; +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.output.StreamExportParams; + +public class DetectionsExportWizard extends PamDialog { + + private PamDataBlock dataBlock; + private CardLayout cardLayout; + private JPanel mainPanel; + private GranularityCard granularityCard; + private DescriptionCard descriptionCard; + private JButton prevButton; + private StreamExportParams streamExportParams; + + private ArrayList wizardCards = new ArrayList(); + + private DetectionsExportWizard(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { + super(parentFrame, "Detections Export", false); + this.dataBlock = dataBlock; + cardLayout = new CardLayout(); + mainPanel = new JPanel(cardLayout); + + addCard(granularityCard = new GranularityCard(tethysControl, dataBlock)); + addCard(descriptionCard = new DescriptionCard(tethysControl, dataBlock)); + + streamExportParams = tethysControl.getTethysExportParams().getStreamParams(dataBlock); + + cardLayout.first(mainPanel); + + setDialogComponent(mainPanel); + + getOkButton().setText("Next"); + prevButton = new JButton("Previous"); + getButtonPanel().add(prevButton, 0); + prevButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + previousButton(); + } + }); + + setResizable(true); + } + + private void addCard(ExportWizardCard wizPanel) { + mainPanel.add(wizPanel, wizPanel.getTitle()); + wizardCards.add(wizPanel); + } + + public static void showDilaog(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { + DetectionsExportWizard wiz = new DetectionsExportWizard(parentFrame, tethysControl, dataBlock); + wiz.setParams(); + wiz.setVisible(true); + } + + private void setParams() { + for (ExportWizardCard wizCard : wizardCards) { + wizCard.setParams(streamExportParams); + } +// granularityCard.setParams(streamExportParams); + } + + /** + * Called when 'previous' button is clicked. + */ + protected void previousButton() { + cardLayout.previous(mainPanel); + enableControls(); + } + + @Override + public boolean getParams() { + int iCard = getCardIndex(); + if (iCard < wizardCards.size()-1) { + if (checkCurrentCard()) { + cardLayout.next(mainPanel); + enableControls(); + } + return false; + } + +// if (cardLayout.) +// cardLayout.next(mainPanel); +// System.out.println(mainPanel.getComponent(0).isShowing()); + /* + * there seems to be no obvious way of knowing which card is showing except + * to go through and see which one has isShowing() == true, then test for first and + * last, etc. + */ + enableControls(); + return false; + } + + @Override + public void cancelButtonPressed() { + // TODO Auto-generated method stub + + } + + @Override + public void restoreDefaultSettings() { + // TODO Auto-generated method stub + + } + + private void enableControls() { + int iCard = getCardIndex(); + prevButton.setEnabled(iCard > 0); + boolean isLast = iCard == wizardCards.size()-1; + getOkButton().setText(isLast ? "Export" : "Next"); + } + + private boolean checkCurrentCard() { + int iCard = getCardIndex(); + if (iCard < 0) { + return true; + } + return wizardCards.get(iCard).getParams(streamExportParams); + } + + private int getCardIndex() { + for (int i = 0; i < mainPanel.getComponentCount(); i++) { + Component component = mainPanel.getComponent(i); + if (component.isVisible()) { + return i; + } + } + return -1; + } +} diff --git a/src/tethys/swing/export/ExportWizardCard.java b/src/tethys/swing/export/ExportWizardCard.java new file mode 100644 index 00000000..f81c7b12 --- /dev/null +++ b/src/tethys/swing/export/ExportWizardCard.java @@ -0,0 +1,43 @@ +package tethys.swing.export; + +import javax.swing.JPanel; + +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.output.StreamExportParams; + +/** + * Slightly standardised panels to put into the export wizard + * so that it's easy to work out which 'card' we're on, etc. + * @author dg50 + * + */ +abstract class ExportWizardCard extends JPanel { + + private String title; + private PamDataBlock dataBlock; + private TethysControl tethysControl; + + public ExportWizardCard(TethysControl tethysControl, String title, PamDataBlock dataBlock) { + this.tethysControl = tethysControl; + this.title = title; + this.dataBlock = dataBlock; + } + + public PamDataBlock getDataBlock() { + return dataBlock; + } + + public TethysControl getTethysControl() { + return tethysControl; + } + + public abstract boolean getParams(StreamExportParams streamExportParams); + + public abstract void setParams(StreamExportParams streamExportParams); + + public String getTitle() { + return title; + } + +} diff --git a/src/tethys/swing/export/GranularityCard.java b/src/tethys/swing/export/GranularityCard.java new file mode 100644 index 00000000..768119aa --- /dev/null +++ b/src/tethys/swing/export/GranularityCard.java @@ -0,0 +1,144 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import org.w3c.dom.Document; + +import PamController.settings.output.xml.PamguardXMLWriter; +import PamView.dialog.PamGridBagContraints; +import PamguardMVC.PamDataBlock; +import PamguardMVC.dataSelector.DataSelectParams; +import PamguardMVC.dataSelector.DataSelector; +import PamguardMVC.dataSelector.DataSelectorChangeListener; +import nilus.GranularityEnumType; +import tethys.TethysControl; +import tethys.niluswraps.PGranularityType; +import tethys.output.StreamExportParams; + +public class GranularityCard extends ExportWizardCard { + + private JRadioButton[] granularities; + + private JTextArea dataSelectionText; + + private JTextField binLength, encounterGap; + + private DataSelector dataSelector; + + public GranularityCard(TethysControl tethysControl, PamDataBlock dataBlock) { + super(tethysControl, "Granularity", dataBlock); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + // granularity + GranularityEnumType[] grans = GranularityEnumType.values(); + granularities = new JRadioButton[grans.length]; + JPanel granPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + granPanel.setBorder(new TitledBorder("Granularity")); + ButtonGroup granGroup = new ButtonGroup(); + for (int i = 0; i < grans.length; i++) { + c.gridx = 0; + granularities[i] = new JRadioButton(PGranularityType.prettyString(grans[i])); + granularities[i].setToolTipText(PGranularityType.toolTip(grans[i])); + granPanel.add(granularities[i], c); + granGroup.add(granularities[i]); + if (grans[i] == GranularityEnumType.BINNED) { + c.gridx++; + granPanel.add(new JLabel(" bin duration ", JLabel.RIGHT), c); + c.gridx++; + granPanel.add(binLength = new JTextField(5), c); + c.gridx++; + granPanel.add(new JLabel(" (s) ", JLabel.LEFT), c); + + } + if (grans[i] == GranularityEnumType.ENCOUNTER) { + c.gridx++; + granPanel.add(new JLabel(" min gap ", JLabel.RIGHT), c); + c.gridx++; + granPanel.add(encounterGap = new JTextField(5), c); + c.gridx++; + granPanel.add(new JLabel(" (s) ", JLabel.LEFT), c); + + } + c.gridy++; + } + this.add(granPanel); + + // data selection + dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); + if (dataSelector != null) { + dataSelectionText = new JTextArea(8, 40); + JPanel dataPanel = new JPanel(new BorderLayout()); + JPanel nPanel = new JPanel(new BorderLayout()); + dataPanel.add(BorderLayout.NORTH, nPanel); + JButton selectorButton = dataSelector.getDialogButton(tethysControl.getGuiFrame(), new DataSelectorChangeListener() { + @Override + public void selectorChange(DataSelector changedSelector) { + newDataSelection(); + } + }); + nPanel.add(BorderLayout.EAST, selectorButton); + newDataSelection(); + nPanel.add(BorderLayout.CENTER, new JLabel("Data selection filter ", JLabel.RIGHT)); + dataPanel.setBorder(new TitledBorder("Data selection filter")); + JScrollPane sp = new JScrollPane(dataSelectionText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + dataPanel.add(BorderLayout.CENTER, sp); + this.add(dataPanel); + } + + } + + protected void newDataSelection() { + if (dataSelector == null) { + return; + } + DataSelectParams params = dataSelector.getParams(); + if (params == null) { + return; + } + if (params.getCombinationFlag() == 2) { + dataSelectionText.setText("Not enabled"); + return; + } + String txt = dataSelector.getDescription(); + dataSelectionText.setText(txt); + } + + @Override + public boolean getParams(StreamExportParams streamExportParams) { + GranularityEnumType[] grans = GranularityEnumType.values(); + for (int i = 0; i < grans.length; i++) { + if (granularities[i].isSelected()) { + streamExportParams.granularity = grans[i]; + break; + } + } + + return streamExportParams.granularity != null; + } + + @Override + public void setParams(StreamExportParams streamExportParams) { + GranularityEnumType[] grans = GranularityEnumType.values(); + for (int i = 0; i < grans.length; i++) { + granularities[i].setSelected(streamExportParams.granularity == grans[i]); + } + newDataSelection(); + } + +} From 573785d98f9f9979f2ab195e2c958e3d031105d8 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 31 Mar 2023 19:07:02 +0100 Subject: [PATCH 28/65] GUi Play Export wizard for detections --- src/PamController/PamControlledUnit.java | 20 + src/dataMap/OfflineDataMap.java | 22 + src/tethys/TethysControl.java | 22 +- src/tethys/TethysMenuActions.java | 2 +- src/tethys/TethysState.java | 15 +- src/tethys/dbxml/DBQueryResult.java | 36 ++ src/tethys/dbxml/DBXMLConnect.java | 24 +- src/tethys/dbxml/DBXMLQueries.java | 327 ++++++---- .../detection/DetectionExportObserver.java | 11 + .../detection/DetectionExportProgress.java | 31 + src/tethys/detection/DetectionsHandler.java | 592 +++++++++++++----- .../detection/StreamDetectionsSummary.java | 21 + src/tethys/niluswraps/PDetections.java | 23 + src/tethys/output/TethysExporter.java | 20 +- .../swing/DatablockDetectionsPanel.java | 101 ++- src/tethys/swing/TethysMainPanel.java | 2 + src/tethys/swing/export/AlgorithmCard.java | 77 +++ .../swing/export/DetectionsExportWizard.java | 40 +- .../swing/export/ExportStreamInfoPanel.java | 95 +++ src/tethys/swing/export/ExportWorkerCard.java | 163 +++++ src/tethys/swing/export/GranularityCard.java | 3 +- 21 files changed, 1352 insertions(+), 295 deletions(-) create mode 100644 src/tethys/detection/DetectionExportObserver.java create mode 100644 src/tethys/detection/DetectionExportProgress.java create mode 100644 src/tethys/detection/StreamDetectionsSummary.java create mode 100644 src/tethys/niluswraps/PDetections.java create mode 100644 src/tethys/swing/export/AlgorithmCard.java create mode 100644 src/tethys/swing/export/ExportStreamInfoPanel.java create mode 100644 src/tethys/swing/export/ExportWorkerCard.java diff --git a/src/PamController/PamControlledUnit.java b/src/PamController/PamControlledUnit.java index 808ddccd..1f9e9d42 100644 --- a/src/PamController/PamControlledUnit.java +++ b/src/PamController/PamControlledUnit.java @@ -23,6 +23,7 @@ package PamController; import java.awt.Component; import java.awt.Frame; import java.util.ArrayList; +import java.util.List; import javax.swing.JFrame; import javax.swing.JMenu; @@ -39,7 +40,9 @@ import org.w3c.dom.Element; import PamController.status.ModuleStatus; import PamController.status.ModuleStatusManager; import PamController.status.ProcessCheck; +import PamModel.PamModel; import PamModel.PamModuleInfo; +import PamModel.PamPluginInterface; import PamView.ClipboardCopier; import PamView.PamGui; import PamView.PamSidePanel; @@ -863,5 +866,22 @@ public abstract class PamControlledUnit implements SettingsNameProvider { public int getInstanceIndex() { return instanceIndex; } + + /** + * Get detail if this is a plugin. + * @return plugin detail, or null if it's not a plugin. + */ + public PamPluginInterface getPlugin() { + List pluginList = ((PamModel) PamController.getInstance().getModelInterface()).getPluginList(); + if (pluginList == null) { + return null; + } + for (PamPluginInterface plugin : pluginList) { + if (plugin.getClassName().equals(this.getClass().getName())) { + return plugin; + } + } + return null; + } } diff --git a/src/dataMap/OfflineDataMap.java b/src/dataMap/OfflineDataMap.java index 2295af01..cf9d9d37 100644 --- a/src/dataMap/OfflineDataMap.java +++ b/src/dataMap/OfflineDataMap.java @@ -361,6 +361,28 @@ abstract public class OfflineDataMap { } } + /** + * Get the start time of the first datamap point or Long.minval + * @return + */ + public long getMapStartTime() { + if (mapPoints == null || mapPoints.size() == 0) { + return Long.MIN_VALUE; + } + return mapPoints.get(0).getStartTime(); + } + + /** + * Get the start time of the first datamap point or Long.minval + * @return + */ + public long getMapEndTime() { + if (mapPoints == null || mapPoints.size() == 0) { + return Long.MIN_VALUE; + } + return mapPoints.get(mapPoints.size()-1).getEndTime(); + } + /** * @return the lowestPoint */ diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 7fe79466..c3a450eb 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -71,6 +71,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet private ArrayList dataBlockSynchInfos; private DeploymentHandler deploymentHandler; + private DetectionsHandler detectionsHandler; public TethysControl(String unitName) { super(unitType, unitName); @@ -78,6 +79,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet dbxmlConnect = new DBXMLConnect(this); dbxmlQueries = new DBXMLQueries(this, dbxmlConnect); deploymentHandler = new DeploymentHandler(this); + detectionsHandler = new DetectionsHandler(this); serverCheckTimer = new Timer(10000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -111,14 +113,14 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet @Override public JMenuItem createFileMenu(JFrame parentFrame) { JMenu tethysMenu = new JMenu("Tethys"); - JMenuItem tethysExport = new JMenuItem("Export ..."); - tethysMenu.add(tethysExport); - tethysExport.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - tethysExport(parentFrame); - } - }); +// JMenuItem tethysExport = new JMenuItem("Export ..."); +// tethysMenu.add(tethysExport); +// tethysExport.addActionListener(new ActionListener() { +// @Override +// public void actionPerformed(ActionEvent e) { +// tethysExport(parentFrame); +// } +// }); JMenuItem openClient = new JMenuItem("Open client in browser"); openClient.addActionListener(new ActionListener() { @Override @@ -430,4 +432,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet return deploymentHandler; } + public DetectionsHandler getDetectionsHandler() { + return detectionsHandler; + } + } diff --git a/src/tethys/TethysMenuActions.java b/src/tethys/TethysMenuActions.java index 480399b0..38d147c7 100644 --- a/src/tethys/TethysMenuActions.java +++ b/src/tethys/TethysMenuActions.java @@ -25,7 +25,7 @@ public class TethysMenuActions { } public void deploymentMouseActions(MouseEvent e, PDeployment pDeployment) { - ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocsIds(pDeployment.deployment.getId()); + ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocuments(pDeployment.deployment.getId()); // System.out.println("Detections for deployment " + pDeployment.deployment.getId()); // for (String detName : detDocNames) { // System.out.println(detName); diff --git a/src/tethys/TethysState.java b/src/tethys/TethysState.java index 35e3b933..87606780 100644 --- a/src/tethys/TethysState.java +++ b/src/tethys/TethysState.java @@ -1,5 +1,7 @@ package tethys; +import tethys.detection.DetectionExportProgress; + /** * Basis for a message system which will get passed around whenever something happens in * Tethys, whether it be a new connection, progress during data output, etc. @@ -12,13 +14,24 @@ public class TethysState { 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!) + UPDATEMETADATA, // META Data being prepared for output have changed (so may be able to enable output!) + EXPORTING // currently exporting data. may be a while ... }; public StateType stateType; + private Object stateObject; public TethysState(StateType stateType) { super(); this.stateType = stateType; } + + public TethysState(StateType stateType, Object stateObject) { + this.stateType = stateType; + this.stateObject = stateObject; + } + + public Object getStateObject() { + return stateObject; + } } diff --git a/src/tethys/dbxml/DBQueryResult.java b/src/tethys/dbxml/DBQueryResult.java index 4af1a245..06a9c8ff 100644 --- a/src/tethys/dbxml/DBQueryResult.java +++ b/src/tethys/dbxml/DBQueryResult.java @@ -1,5 +1,16 @@ package tethys.dbxml; +import java.io.IOException; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + public class DBQueryResult { public long queryTimeMillis; @@ -23,4 +34,29 @@ public class DBQueryResult { this.queryException = queryException; } + /** + * Get the result as an XML document. + * @return XML document + * @throws ParserConfigurationException + * @throws SAXException + * @throws IOException + */ + public Document getDocument() throws ParserConfigurationException, SAXException, IOException { + if (queryResult == null) { + return null; + } + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + //API to obtain DOM Document instance + DocumentBuilder builder = null; + + //Create DocumentBuilder with default configuration + builder = factory.newDocumentBuilder(); + + //Parse the content to Document object + Document doc = builder.parse(new InputSource(new StringReader(queryResult))); + return doc; + } + } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index f1347d99..6da77e4c 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -125,6 +125,7 @@ public class DBXMLConnect { catch (Exception e) { System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); } + forceFlush(); return result == null; } @@ -164,10 +165,28 @@ public class DBXMLConnect { // TODO Auto-generated catch block e.printStackTrace(); } - +// System.out.println(fileError); return fileError; } + + /* + * force a fluch by sending a dummy document to th eimporter which will rail, but ... + */ + private void forceFlush() { + TethysExportParams params = new TethysExportParams(); + String fileError = null; + try { + fileError = Importer.ImportFiles(params.getFullServerName(), "NoCollection", + new String[] { "ThereIsNoFileE" }, "", "", false); + } + catch (Exception e) { + + } +// System.out.println(fileError); + + } + /** * Get a temp folder to hold xml output. This will be the standard * temp folder + /PAMGuardTethys. Files will be left here until PAMGUard @@ -266,7 +285,7 @@ public class DBXMLConnect { * @return */ public boolean deleteDeployment(String deploymentId) { - ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocsIds(deploymentId); + ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocuments(deploymentId); JerseyClient jerseyClient = getJerseyClient(); Queries queries = null; String result; @@ -324,6 +343,7 @@ public class DBXMLConnect { } return new ServerStatus(ok, null); } + // add whatever calls are necessary ... diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 76b236a0..fd508107 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -1,5 +1,6 @@ package tethys.dbxml; +import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; @@ -7,12 +8,14 @@ import java.util.Collections; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import PamController.settings.output.xml.PamguardXMLWriter; import PamguardMVC.PamDataBlock; @@ -21,6 +24,8 @@ import dbxml.Queries; import nilus.Deployment; import nilus.Deployment.Instrument; import nilus.DeploymentRecoveryDetails; +import nilus.DescriptionType; +import nilus.Detections; import nilus.Helper; import tethys.TethysControl; import tethys.TethysTimeFuncs; @@ -137,7 +142,9 @@ public class DBXMLQueries { } /** - * Get some basic (not all) data for deployments associated with a project. + * Get some basic (not all) data for deployments associated with a project. Note that + * this may include deployments which are NOT part of the current dataset. That requires + * a search on Instrument as well. * @param projectName * @return */ @@ -212,12 +219,18 @@ public class DBXMLQueries { return deployments; } - public int countData(PamDataBlock dataBlock, String deploymentId) { + /** + * Get a list of Detections documents which associate with a datablock and a deploymentId. + * @param dataBlock + * @param deploymentId + * @return + */ + public ArrayList getDetectionsDocuments(PamDataBlock dataBlock, String deploymentId) { /** * first query for Detections documents associated with this deployment and datablock. */ - 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 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/Algorithm/Software\",\"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/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"TheDeploymentId\"],\"optype\":\"binary\"}],\"enclose\":1}"; String query; if (deploymentId == null) { query = queryNoDepl; @@ -228,18 +241,92 @@ public class DBXMLQueries { query = query.replace("LongDataName", dataBlock.getLongDataName()); DBQueryResult queryResult = executeQuery(query); if (queryResult ==null) { - return 0; + return null; } - Document doc = convertStringToXMLDocument(queryResult.queryResult); - if (doc == null) { - return 0; + Document doc; + try { + doc = queryResult.getDocument(); + } catch (ParserConfigurationException | SAXException | IOException e) { + e.printStackTrace(); + return null; } - + ArrayList detectionsNames = new ArrayList(); 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(); + detectionsNames.add(docName); + } + return detectionsNames; + } + + /** + * Get the names of all detection documents for a given deployment for all data streams. + * @param deploymentId + * @return + */ + public ArrayList getDetectionsDocuments(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; + } + + public int countData(PamDataBlock dataBlock, String deploymentId) { +// /** +// * first query for Detections documents associated with this deployment and datablock. +// */ +// 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/Algorithm/Software\",\"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/Algorithm/Software\",\"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; +// try { +// doc = queryResult.getDocument(); +// } catch (ParserConfigurationException | SAXException | IOException e) { +// e.printStackTrace(); +// return 0; +// } +// +// int count = 0; +// NodeList returns = doc.getElementsByTagName("Return"); + ArrayList documentNames = getDetectionsDocuments(dataBlock, deploymentId); + if (documentNames == null) { + return 0; + } + int count = 0; + for (int i = 0; i < documentNames.size(); i++) { +// Node aNode = returns.item(i); + String docName = documentNames.get(i); // System.out.println(aNode.getTextContent()); int count2 = countDetections2(docName); count += count2; //countDetecionsData(docName); @@ -248,7 +335,12 @@ public class DBXMLQueries { return count; } - private int countDetections2(String docName) { + /** + * Count on effort detections in a Detections document + * @param docName + * @return + */ + public int countDetections2(String docName) { TethysExportParams params = tethysControl.getTethysExportParams(); String queryBase = "count(collection(\"Detections\")/Detections[Id=\"ReplaceDocumentId\"]/OnEffort/Detection)"; String query = queryBase.replace("ReplaceDocumentId", docName); @@ -275,115 +367,67 @@ public class DBXMLQueries { 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; - } - +// /** +// * 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; +// } - 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); +// /** +// * 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]; // } - int[] newN = countDataForDeployment(projectName, aDeployment.deployment.getId(), dataPrefixes); - for (int i = 0; i < n.length; i++) { - n[i] += newN[i]; - } - } - return n; - } +// } +// return n; +// } /** * Count data within a deployment document which is associated with a set of datablocks @@ -486,4 +530,55 @@ public class DBXMLQueries { return null; } + /** + * Get the basic information about a Detections document. This is basically everything apart from + * the actual detections themselves. + * @param aDoc + * @return + */ + public Detections getDetectionsDocInfo(String detectionsDocName) { + 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/Description\",\"Detections/DataSource\",\"Detections/Algorithm\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String query = queryBase.replace("DetectionsDocName", detectionsDocName); + DBQueryResult queryResult = executeQuery(query); + Document doc; + try { + doc = queryResult.getDocument(); + } catch (ParserConfigurationException | SAXException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } +// System.out.println(queryResult.queryResult); + + Detections detections = new Detections(); + try { + Helper.createRequiredElements(detections); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + e.printStackTrace(); + } + + NodeList returns = doc.getElementsByTagName("Result"); + // System.out.println("N projects = " + returns.getLength()); + int n = returns.getLength(); + if (n == 0) { + return null; + } + Element result = (Element) returns.item(0); + + DescriptionType description = detections.getDescription(); + if (description == null) { + description = new DescriptionType(); + detections.setDescription(description); + } + detections.setId(getElementData(result, "Id")); + description.setAbstract(getElementData(result, "Description.Abstract")); + description.setMethod(getElementData(result, "Description.Method")); + description.setObjectives(getElementData(result, "Description.Objectives")); + + + + // TODO Auto-generated method stub + return detections; + } + } diff --git a/src/tethys/detection/DetectionExportObserver.java b/src/tethys/detection/DetectionExportObserver.java new file mode 100644 index 00000000..29ed1fe7 --- /dev/null +++ b/src/tethys/detection/DetectionExportObserver.java @@ -0,0 +1,11 @@ +package tethys.detection; + +public interface DetectionExportObserver { + + /** + * Update message and state of export + * @param progress + */ + public void update(DetectionExportProgress progress); + +} diff --git a/src/tethys/detection/DetectionExportProgress.java b/src/tethys/detection/DetectionExportProgress.java new file mode 100644 index 00000000..4456547f --- /dev/null +++ b/src/tethys/detection/DetectionExportProgress.java @@ -0,0 +1,31 @@ +package tethys.detection; + +import nilus.Detections; +import tethys.niluswraps.PDeployment; + +public class DetectionExportProgress { + + public static final int STATE_GATHERING = 1; + public static final int STATE_CANCELED = 2; + public static final int STATE_COMPLETE = 3; + public static final int STATE_WRITING = 4; + public PDeployment currentDeployment; + public Detections currentDetections; + public long lastUnitTime; + public long totalUnits; + public int exportCount; + public int skipCount; + public int state; + + public DetectionExportProgress(PDeployment currentDeployment, Detections currentDetections, long lastUnitTime, + long totalUnits, int exportCount, int skipCount, int state) { + super(); + this.currentDeployment = currentDeployment; + this.currentDetections = currentDetections; + this.lastUnitTime = lastUnitTime; + this.totalUnits = totalUnits; + this.exportCount = exportCount; + this.skipCount = skipCount; + this.state = state; + } +} diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 6240defb..11baac0f 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -4,13 +4,21 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import javax.swing.SwingWorker; + +import PamController.PamControlledUnit; +import PamController.PamguardVersionInfo; +import PamModel.PamPluginInterface; import PamUtils.PamCalendar; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; +import PamguardMVC.PamProcess; import PamguardMVC.dataSelector.DataSelector; import dataMap.OfflineDataMap; import dataMap.OfflineDataMapPoint; import metadata.deployment.DeploymentData; +import nilus.AlgorithmType; +import nilus.AlgorithmType.SupportSoftware; import nilus.DataSourceType; import nilus.Deployment; import nilus.Detection; @@ -18,172 +26,225 @@ import nilus.DetectionEffort; import nilus.DetectionEffortKind; import nilus.DetectionGroup; import nilus.Detections; +import nilus.Helper; import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysState.StateType; +import tethys.deployment.DeploymentHandler; +import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; +import tethys.dbxml.DBXMLConnect; import tethys.detection.DetectionGranularity.GRANULARITY; +import tethys.niluswraps.PDeployment; +import tethys.niluswraps.PDetections; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; +import tethys.pamdata.TethysDataPoint; import tethys.pamdata.TethysDataProvider; +import tethys.swing.export.ExportWorkerCard; public class DetectionsHandler { private TethysControl tethysControl; - public int uniqueDetectionsId; + public int uniqueDetectionsId=1; public int uniqueDetectionId; + + private volatile boolean activeExport; + + private ExportWorker exportWorker; public DetectionsHandler(TethysControl tethysControl) { super(); this.tethysControl = tethysControl; } + + + /** + * Get a list of Detections documents associated with a particular data stream for + * this data set (not the entire project). + * @param dataBlock + */ + public StreamDetectionsSummary getStreamDetections(PamDataBlock dataBlock) { + ArrayList deployments = tethysControl.getDeploymentHandler().getMatchedDeployments(); + return getStreamDetections(dataBlock, deployments); + } /** - * Here is where we export data for a specific data stream to Tethys. - * - * @param aDataBlock - * @param aDeployment - * @param tethysExportParams - * @param streamExportParams - */ - public boolean exportDetections(PamDataBlock aDataBlock, Deployment deployment, DetectionGranularity granularity, TethysExportParams tethysExportParams, - StreamExportParams streamExportParams) { - if (granularity == null || granularity.granularity == null) { - granularity = new DetectionGranularity(GRANULARITY.TIME, 3600); - } - switch (granularity.granularity) { - case BINARYFILE: - return exportByBinaryFile(aDataBlock, deployment, tethysExportParams, streamExportParams); - case NONE: - return exportEverything(aDataBlock, deployment, tethysExportParams, streamExportParams); - case TIME: - return exportByTimeChunk(aDataBlock, deployment, granularity.granularityIntervalSeconds, tethysExportParams, streamExportParams); - default: - break; - } - - return false; - - - } - - private boolean exportByBinaryFile(PamDataBlock dataBlock, Deployment deployment, - TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { - long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); - long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); - /* - * there should be a pretty good correspondence between the start of a binary file and the deploymentStart - * since they all derived from the same start clock. - */ - OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); - if (dataMap == null) { - return false; - } - List mapPoints = dataMap.getMapPoints(); - boolean ok = true; - for (OfflineDataMapPoint mapPoint : mapPoints) { - if (mapPoint.getEndTime() < deploymentStart) { - continue; - } - if (mapPoint.getStartTime() >= deploymentStop) { - continue; - } - ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, mapPoint.getStartTime()), - Math.min(deploymentStop, mapPoint.getEndTime()), tethysExportParams, streamExportParams); - } - - - return ok; - } - - private boolean exportEverything(PamDataBlock dataBlock, Deployment deployment, - TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { - long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); - long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); - return loadAndExport(dataBlock, deployment, deploymentStart, deploymentStop, tethysExportParams, streamExportParams); - } - - private boolean exportByTimeChunk(PamDataBlock dataBlock, Deployment deployment, long granularityIntervalSeconds, - TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { - - long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); - long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); - long chunkMillis = granularityIntervalSeconds*1000; - long exportStart = deploymentStart / chunkMillis; - exportStart *= chunkMillis; - boolean ok = true; - while (exportStart < deploymentStop) { - ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, exportStart), - Math.min(deploymentStop, exportStart + chunkMillis), tethysExportParams, streamExportParams); - exportStart += chunkMillis; - } - - return ok; - } - -/** - * Load and export data for a given time period. This may be a complete deployment, it may be a short section. Do as told ! - * Hopefully data interval is small enough to hold all in memory - it needs to be if the document will fit in mempory, so should be OK + * Get a list of Detections documents associated with a particular data block for the list of deployments + * documents. Group them by abstract or something * @param dataBlock - * @param deployment - * @param max - * @param min - * @param tethysExportParams - * @param streamExportParams + * @param deployments + * @return */ - private boolean loadAndExport(PamDataBlock dataBlock, Deployment deployment, long startTimeMillis, long endTimeMillis, - TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { - // load the data - dataBlock.loadViewerData(startTimeMillis, endTimeMillis, null); - DataSelector dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); - /* - * for easier synching, get a copy of the data and also apply the data selector right away so that - * we've a list of exactly the right data. - */ - ArrayList data = dataBlock.getDataCopy(startTimeMillis, endTimeMillis, true, dataSelector); - /* - * Here, make Detection object and add the DetectionEffort data. - */ - DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); - TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); - Detections detections = new Detections(); -// 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()); -// dataSource.setEnsembleId(""); ToDo - detections.setDataSource(dataSource); - detections.setAlgorithm(dataProvider.getAlgorithm()); - detections.setUserId("Unknown user"); - detections.setEffort(getDetectorEffort(deployment, startTimeMillis, endTimeMillis)); - DetectionGroup detectionGroup = new DetectionGroup(); - detections.setOnEffort(detectionGroup); - List detectionList = detectionGroup.getDetection(); - for (int i = 0; i < data.size(); i++) { - PamDataUnit dataUnit = data.get(i); - Detection detection = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); - if (detection != null) { - detectionList.add(detection); + public StreamDetectionsSummary getStreamDetections(PamDataBlock dataBlock, ArrayList deployments) { + // get the basic data for each document including it's Description. + + ArrayList detectionsDocs = new ArrayList<>(); + for (PDeployment aDep : deployments) { + ArrayList someNames = tethysControl.getDbxmlQueries().getDetectionsDocuments(dataBlock, aDep.deployment.getId()); + if (someNames == null) { + continue; + } + // no have a list of all the Detections documents of interest for this datablock. + for (String aDoc : someNames) { + Detections detections = tethysControl.getDbxmlQueries().getDetectionsDocInfo(aDoc); + int count = tethysControl.getDbxmlQueries().countDetections2(aDoc); + PDetections pDetections = new PDetections(detections, null, count); + detectionsDocs.add(pDetections); } } - System.out.printf("Exporting %d %s detections for time period %s to %s\n", detectionList.size(), dataBlock.getDataName(), - detections.getEffort().getStart().toString(), detections.getEffort().getEnd().toString()); - /* - * We should now have a fully populated Detections object, so write it to the database - * using functions in DBXMLConnect - */ - ArrayList detectionDocuments = new ArrayList(); - detectionDocuments.add(detections); - -// tethysControl.getDbxmlConnect().postToTethys(detectionDocuments); // call whatever you need to call in here to write the Detections. - - - return true; - + return new StreamDetectionsSummary(detectionsDocs); } + +// /** +// * Here is where we export data for a specific data stream to Tethys. +// * +// * @param aDataBlock +// * @param aDeployment +// * @param tethysExportParams +// * @param streamExportParams +// */ +// public boolean exportDetections(PamDataBlock aDataBlock, Deployment deployment, DetectionGranularity granularity, TethysExportParams tethysExportParams, +// StreamExportParams streamExportParams) { +// if (granularity == null || granularity.granularity == null) { +// granularity = new DetectionGranularity(GRANULARITY.TIME, 3600); +// } +// switch (granularity.granularity) { +// case BINARYFILE: +// return exportByBinaryFile(aDataBlock, deployment, tethysExportParams, streamExportParams); +// case NONE: +// return exportEverything(aDataBlock, deployment, tethysExportParams, streamExportParams); +// case TIME: +// return exportByTimeChunk(aDataBlock, deployment, granularity.granularityIntervalSeconds, tethysExportParams, streamExportParams); +// default: +// break; +// } +// +// return false; +// +// +// } +// +// private boolean exportByBinaryFile(PamDataBlock dataBlock, Deployment deployment, +// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { +// long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); +// long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); +// /* +// * there should be a pretty good correspondence between the start of a binary file and the deploymentStart +// * since they all derived from the same start clock. +// */ +// OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); +// if (dataMap == null) { +// return false; +// } +// List mapPoints = dataMap.getMapPoints(); +// boolean ok = true; +// for (OfflineDataMapPoint mapPoint : mapPoints) { +// if (mapPoint.getEndTime() < deploymentStart) { +// continue; +// } +// if (mapPoint.getStartTime() >= deploymentStop) { +// continue; +// } +// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, mapPoint.getStartTime()), +// Math.min(deploymentStop, mapPoint.getEndTime()), tethysExportParams, streamExportParams); +// } +// +// +// return ok; +// } +// +// private boolean exportEverything(PamDataBlock dataBlock, Deployment deployment, +// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { +// long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); +// long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); +// return loadAndExport(dataBlock, deployment, deploymentStart, deploymentStop, tethysExportParams, streamExportParams); +// } +// +// private boolean exportByTimeChunk(PamDataBlock dataBlock, Deployment deployment, long granularityIntervalSeconds, +// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { +// +// long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); +// long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); +// long chunkMillis = granularityIntervalSeconds*1000; +// long exportStart = deploymentStart / chunkMillis; +// exportStart *= chunkMillis; +// boolean ok = true; +// while (exportStart < deploymentStop) { +// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, exportStart), +// Math.min(deploymentStop, exportStart + chunkMillis), tethysExportParams, streamExportParams); +// exportStart += chunkMillis; +// } +// +// return ok; +// } +// +///** +// * Load and export data for a given time period. This may be a complete deployment, it may be a short section. Do as told ! +// * Hopefully data interval is small enough to hold all in memory - it needs to be if the document will fit in mempory, so should be OK +// * @param dataBlock +// * @param deployment +// * @param max +// * @param min +// * @param tethysExportParams +// * @param streamExportParams +// */ +// private boolean loadAndExport(PamDataBlock dataBlock, Deployment deployment, long startTimeMillis, long endTimeMillis, +// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { +// // load the data +// dataBlock.loadViewerData(startTimeMillis, endTimeMillis, null); +// DataSelector dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); +// /* +// * for easier synching, get a copy of the data and also apply the data selector right away so that +// * we've a list of exactly the right data. +// */ +// ArrayList data = dataBlock.getDataCopy(startTimeMillis, endTimeMillis, true, dataSelector); +// /* +// * Here, make Detection object and add the DetectionEffort data. +// */ +// DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); +// TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); +// Detections detections = new Detections(); +//// 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()); +//// dataSource.setEnsembleId(""); ToDo +// detections.setDataSource(dataSource); +// detections.setAlgorithm(dataProvider.getAlgorithm()); +// detections.setUserId("Unknown user"); +// detections.setEffort(getDetectorEffort(deployment, startTimeMillis, endTimeMillis)); +// DetectionGroup detectionGroup = new DetectionGroup(); +// detections.setOnEffort(detectionGroup); +// List detectionList = detectionGroup.getDetection(); +// for (int i = 0; i < data.size(); i++) { +// PamDataUnit dataUnit = data.get(i); +// Detection detection = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); +// if (detection != null) { +// detectionList.add(detection); +// } +// } +// System.out.printf("Exporting %d %s detections for time period %s to %s\n", detectionList.size(), dataBlock.getDataName(), +// detections.getEffort().getStart().toString(), detections.getEffort().getEnd().toString()); +// /* +// * We should now have a fully populated Detections object, so write it to the database +// * using functions in DBXMLConnect +// */ +// ArrayList detectionDocuments = new ArrayList(); +// detectionDocuments.add(detections); +// +//// tethysControl.getDbxmlConnect().postToTethys(detectionDocuments); // call whatever you need to call in here to write the Detections. +// +// +// return true; +// +// } + // private boolean exportByTimeChunk(PamDataBlock aDataBlock, Deployment deployment, long granularityIntervalSeconds, // TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { // // TODO Auto-generated method stub @@ -218,6 +279,60 @@ public class DetectionsHandler { return effort; } + /** + * Method string for Detections Algorithm documents. + * @param dataBlock + * @return + */ + public String getMethodString(PamDataBlock dataBlock) { + if (dataBlock == null) { + return null; + } + PamProcess process = dataBlock.getParentProcess(); + return "PAMGuard " + process.getProcessName(); + + } + + /** + * Software string for Detections Algorithm documents. + * @param dataBlock + * @return + */ + public String getSoftwareString(PamDataBlock dataBlock) { + if (dataBlock == null) { + return null; + } + return dataBlock.getLongDataName(); + } + + /** + * Software string for Detections Algorithm documents. + * @param dataBlock + * @return + */ + public String getVersionString(PamDataBlock dataBlock) { + if (dataBlock == null) { + return null; + } + PamProcess process = dataBlock.getParentProcess(); + PamControlledUnit pcu = process.getPamControlledUnit(); + PamPluginInterface plugin = pcu.getPlugin(); + if (plugin == null) { + return PamguardVersionInfo.version; + } + else { + return plugin.getVersion(); + } + } + + public String getSupportSoftware(PamDataBlock dataBlock) { + return "PAMGuard"; + } + + public String getSupportSoftwareVersion(PamDataBlock dataBlock) { +// should try to dig into the binary store and get the version from there. + return PamguardVersionInfo.version; + } // /** // * 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 @@ -229,4 +344,201 @@ public class DetectionsHandler { // public static final String getDetectionsDocIdPrefix(String project, PamDataBlock dataBlock) { // return project + "_" + dataBlock.getDataName(); // } + + /** + * Detections will be exported in a separate worker thread since export may take some time and + * the user should be given ample opportunity to cancel it. + * @param pamDataBlock + * @param streamExportParams + * @param exportWorkerCard + */ + public void startExportThread(PamDataBlock pamDataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { + tethysControl.getTethysExportParams().setStreamParams(pamDataBlock, streamExportParams); + activeExport = true; + exportWorker = new ExportWorker(pamDataBlock, streamExportParams, exportObserver); + exportWorker.execute(); + } + + public void cancelExport() { + activeExport = false; + } + + /** + * Export detections in all deployments for this PAMGuard dataset. + * @param dataBlock + * @param streamExportParams + * @param exportObserver + * @return + */ + private int exportDetections(PamDataBlock dataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { + /* + * This is currently called for the entire dataset, but we will need to loop over specific Deployment documents + * and export the content of each separately. + */ + TethysExportParams exportParams = tethysControl.getTethysExportParams(); + DBXMLConnect dbxmlConnect = tethysControl.getDbxmlConnect(); + DeploymentHandler depHandler = tethysControl.getDeploymentHandler(); + ArrayList deployments = depHandler.getMatchedDeployments(); + Detections currentDetections = null; + OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); + TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); + DataSelector dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); + int totalCount = dataMap.getDataCount(); + int skipCount = 0; + int exportCount = 0; + long lastUnitTime = 0; + DetectionExportProgress prog; + for (PDeployment deployment : deployments) { + int documentCount = 0; + prog = new DetectionExportProgress(deployment, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + exportObserver.update(prog); + // export everything in that deployment. + // need to loop through all map points in this interval. + List mapPoints = dataMap.getMapPoints(); + for (OfflineDataMapPoint mapPoint : mapPoints) { + if (activeExport == false) { + prog = new DetectionExportProgress(deployment, currentDetections, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_CANCELED); + exportObserver.update(prog); + } + + if (currentDetections == null) { + currentDetections = startDetectionsDocument(deployment, dataBlock, streamExportParams); + currentDetections.getEffort().setStart(TethysTimeFuncs.xmlGregCalFromMillis(mapPoint.getStartTime())); + } + if (mapPoint.getEndTime() < deployment.getAudioStart()) { + continue; + } + if (mapPoint.getStartTime() >= deployment.getAudioEnd()) { + break; + } + dataBlock.loadViewerData(mapPoint.getStartTime(), mapPoint.getEndTime(), null); + ArrayList dataCopy = dataBlock.getDataCopy(deployment.getAudioStart(), deployment.getAudioEnd(), true, dataSelector); + skipCount += dataBlock.getUnitsCount() - dataCopy.size(); + DetectionGroup onEffort = currentDetections.getOnEffort(); + for (PamDataUnit dataUnit : dataCopy) { + Detection det = dataProvider.createDetection(dataUnit, exportParams, streamExportParams); + exportCount++; + documentCount++; + onEffort.getDetection().add(det); + lastUnitTime = dataUnit.getTimeMilliseconds(); + } + + prog = new DetectionExportProgress(deployment, currentDetections, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + exportObserver.update(prog); + + if (documentCount > 500000 && mapPoint != dataMap.getLastMapPoint()) { + prog = new DetectionExportProgress(deployment, currentDetections, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); + exportObserver.update(prog); + closeDetectionsDocument(currentDetections, mapPoint.getEndTime()); + dbxmlConnect.postToTethys(currentDetections); + currentDetections = null; + } + } + + if (currentDetections != null) { + prog = new DetectionExportProgress(deployment, currentDetections, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); + closeDetectionsDocument(currentDetections, deployment.getAudioEnd()); + dbxmlConnect.postToTethys(currentDetections); + currentDetections = null; + } + } + + prog = new DetectionExportProgress(null, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COMPLETE); + exportObserver.update(prog); + return DetectionExportProgress.STATE_COMPLETE; + } + private Detections startDetectionsDocument(PDeployment deployment, PamDataBlock dataBlock, + StreamExportParams exportParams) { + Detections detections = new Detections(); + try { + Helper.createRequiredElements(detections); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + e.printStackTrace(); + return null; + } + + String prefix = deployment.deployment.getId(); + detections.setId(String.format("%s_%d", prefix, uniqueDetectionsId++)); +// detections.setDescription(dataProvider.getDescription(deployment, tethysExportParams)); + detections.setDescription(exportParams.detectionDescription); + DataSourceType dataSource = new DataSourceType(); + dataSource.setDeploymentId(deployment.deployment.getId()); +// dataSource.setEnsembleId(""); ToDo + detections.setDataSource(dataSource); + AlgorithmType algorithm = detections.getAlgorithm(); + algorithm.setMethod(getMethodString(dataBlock)); + algorithm.setSoftware(getSoftwareString(dataBlock)); + algorithm.setVersion(getVersionString(dataBlock)); + List supSoft = algorithm.getSupportSoftware(); + SupportSoftware supportSoft = new SupportSoftware(); + supportSoft.setSoftware(getSupportSoftware(dataBlock)); + supportSoft.setVersion(getSupportSoftwareVersion(dataBlock)); + supSoft.add(supportSoft); + detections.setAlgorithm(algorithm); + detections.setUserId("Unknown user"); + detections.setEffort(getDetectorEffort(deployment.deployment, deployment.getAudioStart(), deployment.getAudioEnd())); + + return detections; + } + + /** + * Close a detections document. This basically just means rewriting the end time and it's only + * important in the event that a document got too big and has to be restarted. + * @param detections + * @param audioEnd + */ + private void closeDetectionsDocument(Detections detections, Long audioEnd) { + detections.getEffort().setEnd(TethysTimeFuncs.xmlGregCalFromMillis(audioEnd)); + } + + private class ExportWorker extends SwingWorker implements DetectionExportObserver { + + private PamDataBlock dataBlock; + private StreamExportParams exportParams; + private DetectionExportObserver exportObserver; + + public ExportWorker(PamDataBlock dataBlock, StreamExportParams exportParams, + DetectionExportObserver exportObserver) { + super(); + this.dataBlock = dataBlock; + this.exportParams = exportParams; + this.exportObserver = exportObserver; + } + + public void publish(DetectionExportProgress exportProgress) { + super.publish(exportProgress); + } + + @Override + protected Integer doInBackground() throws Exception { + // eventually need to switch over the four granularity options here. + return exportDetections(dataBlock, exportParams, this); + } + + @Override + protected void done() { +// this. + DetectionExportProgress prog = new DetectionExportProgress(null, null, 0, 0, 0, 0, DetectionExportProgress.STATE_COMPLETE); + exportObserver.update(prog); + } + + @Override + protected void process(List chunks) { + for (DetectionExportProgress prog : chunks) { + exportObserver.update(prog); + } + } + + @Override + public void update(DetectionExportProgress progress) { + publish(progress); + } + + } } diff --git a/src/tethys/detection/StreamDetectionsSummary.java b/src/tethys/detection/StreamDetectionsSummary.java new file mode 100644 index 00000000..70c11618 --- /dev/null +++ b/src/tethys/detection/StreamDetectionsSummary.java @@ -0,0 +1,21 @@ +package tethys.detection; + +import java.util.ArrayList; + +import tethys.niluswraps.PDetections; + +/** + * Summary information on all Detections documents for a Stream for this + * PAMGuard dataset. + * @author dg50 + * + */ +public class StreamDetectionsSummary { + + public ArrayList detectionsDocs; + + public StreamDetectionsSummary(ArrayList detectionsDocs) { + this.detectionsDocs = detectionsDocs; + } + +} diff --git a/src/tethys/niluswraps/PDetections.java b/src/tethys/niluswraps/PDetections.java new file mode 100644 index 00000000..00fb073a --- /dev/null +++ b/src/tethys/niluswraps/PDetections.java @@ -0,0 +1,23 @@ +package tethys.niluswraps; + +import nilus.Detections; + +public class PDetections { + + public Detections detections; + + public Integer count; + + public PDeployment deployment; + + public PDetections(Detections detections, PDeployment deployment, Integer count) { + super(); + this.detections = detections; + this.deployment = deployment; + this.count = count; + } + + + + +} diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 35733a2d..a624697c 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -204,16 +204,16 @@ public class TethysExporter { // * Outer loop is through deployemnt documents. Will then export detections within each // * deployment detector by detector // */ - for (Deployment aDeployment : deploymentDocs) { - for (PamDataBlock aDataBlock : allDataBlocks) { - StreamExportParams streamExportParams = tethysExportParams.getStreamParams(aDataBlock); - if (streamExportParams == null || !streamExportParams.selected) { - continue; // not interested in this one. - } - detectionsHandler.exportDetections(aDataBlock, aDeployment, - new DetectionGranularity(GRANULARITY.TIME, 3600*12), tethysExportParams, streamExportParams); - } - } +// for (Deployment aDeployment : deploymentDocs) { +// for (PamDataBlock aDataBlock : allDataBlocks) { +// StreamExportParams streamExportParams = tethysExportParams.getStreamParams(aDataBlock); +// if (streamExportParams == null || !streamExportParams.selected) { +// continue; // not interested in this one. +// } +// detectionsHandler.exportDetections(aDataBlock, aDeployment, +// new DetectionGranularity(GRANULARITY.TIME, 3600*12), tethysExportParams, streamExportParams); +// } +// } /* * Then do whatever else is needed to complete the document. */ diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index bf2ee42e..f0265439 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -1,10 +1,16 @@ package tethys.swing; import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import javax.swing.JComponent; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.border.TitledBorder; @@ -12,7 +18,10 @@ import javax.swing.table.AbstractTableModel; import PamView.tables.SwingTableColumnWidths; import PamguardMVC.PamDataBlock; +import nilus.Detections; import tethys.TethysControl; +import tethys.detection.StreamDetectionsSummary; +import tethys.niluswraps.PDetections; /** * Table of Detections documents for a single PAMGuard datablock. @@ -33,6 +42,8 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa private PamDataBlock dataBlock; + private StreamDetectionsSummary streamDetectionsSummary; + public DatablockDetectionsPanel(TethysControl tethysControl) { super(tethysControl); mainPanel = new JPanel(new BorderLayout()); @@ -46,6 +57,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa new SwingTableColumnWidths(tethysControl.getUnitName() + getClass().getName(), table); + table.addMouseListener(new MouseActions()); } @Override @@ -57,16 +69,78 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa public void selectDataBlock(PamDataBlock dataBlock) { this.dataBlock = dataBlock; dataBlockName.setText(dataBlock.getLongDataName()); + streamDetectionsSummary = getTethysControl().getDetectionsHandler().getStreamDetections(dataBlock); + tableModel.fireTableDataChanged(); + } + + private class MouseActions extends MouseAdapter { + + @Override + public void mousePressed(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + if (e.isPopupTrigger()) { + showPopupMenu(e); + } + } + + + } + + public void showPopupMenu(MouseEvent e) { + int row = table.getSelectedRow(); + if (row < 0) { + return; + } + + PDetections pDets = detectionsForRow(row); + if (pDets == null) { + return; + } + + JMenuItem menuItem = new JMenuItem("Delete " + pDets.detections.getId()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteDocument(pDets); + } + }); + JPopupMenu popMenu = new JPopupMenu(); + popMenu.add(menuItem); + popMenu.show(e.getComponent(), e.getX(), e.getY()); + + } + + protected void deleteDocument(PDetections pDets) { + getTethysControl().getDbxmlConnect().deleteDocument(pDets.detections); + selectDataBlock(dataBlock); // force table update. + } + + private PDetections detectionsForRow(int iRow) { + if (streamDetectionsSummary == null || streamDetectionsSummary.detectionsDocs == null) { + return null; + } + if (iRow < 0 || iRow >= streamDetectionsSummary.detectionsDocs.size()) { + return null; + } + return streamDetectionsSummary.detectionsDocs.get(iRow); } private class TableModel extends AbstractTableModel { - private String[] colNames = {"Person", "Name", "Abstract"}; + private String[] colNames = {"Document", "Count", "Abstract"}; @Override public int getRowCount() { - // TODO Auto-generated method stub - return 0; + if (streamDetectionsSummary == null || streamDetectionsSummary.detectionsDocs == null) { + return 0; + } + return streamDetectionsSummary.detectionsDocs.size(); } @Override @@ -81,7 +155,26 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa @Override public Object getValueAt(int rowIndex, int columnIndex) { - // TODO Auto-generated method stub + PDetections pDets = detectionsForRow(rowIndex); + return getValueAt(pDets, columnIndex); + } + + private Object getValueAt(PDetections pDets, int columnIndex) { + if (pDets == null) { + return null; + } + Detections dets = pDets.detections; + if (dets == null) { + return "Error in doc"; + } + switch (columnIndex) { + case 0: + return dets.getId(); + case 1: + return pDets.count; + case 2: + return dets.getDescription().getAbstract(); + } return null; } diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index 49b01c0b..251f6833 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -55,9 +55,11 @@ public class TethysMainPanel extends TethysGUIPanel { southEastPanel.add(detectionsExportPanel.getComponent(), BorderLayout.EAST); splitPane.add(southwestSplit); SwingUtilities.invokeLater(new Runnable() { + // these only work if called after display is visible @Override public void run() { splitPane.setDividerLocation(0.5); + southwestSplit.setDividerLocation(0.5); } }); } diff --git a/src/tethys/swing/export/AlgorithmCard.java b/src/tethys/swing/export/AlgorithmCard.java new file mode 100644 index 00000000..fd82e483 --- /dev/null +++ b/src/tethys/swing/export/AlgorithmCard.java @@ -0,0 +1,77 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamController.PamControlledUnit; +import PamView.dialog.PamGridBagContraints; +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.detection.DetectionsHandler; +import tethys.output.StreamExportParams; + +public class AlgorithmCard extends ExportWizardCard { + + private JTextField method, software, version, supportSoftware; + + public AlgorithmCard(TethysControl tethysControl, PamDataBlock dataBlock) { + super(tethysControl, "Algorithm", dataBlock); + setBorder(new TitledBorder("Algorithm details")); + method = new JTextField(40); + software = new JTextField(40); + version = new JTextField(40); + supportSoftware = new JTextField(40); + JPanel nPanel = new JPanel(new GridBagLayout()); + this.setLayout(new BorderLayout()); + this.add(BorderLayout.NORTH, nPanel); + GridBagConstraints c = new PamGridBagContraints(); + nPanel.add(new JLabel("Method ", JLabel.LEFT), c); + c.gridy++; + nPanel.add(method, c); +// c.gridx = 0; + c.gridy++; + nPanel.add(new JLabel("Software ", JLabel.LEFT), c); + c.gridy++; + nPanel.add(software, c); + c.gridx = 0; + c.gridy++; + nPanel.add(new JLabel("Version ", JLabel.LEFT), c); + c.gridy++; + nPanel.add(version, c); + c.gridx = 0; + c.gridy++; + nPanel.add(new JLabel("Support Software ", JLabel.LEFT), c); + c.gridy++; + nPanel.add(supportSoftware, c); + c.gridx = 0; + c.gridy++; + method.setEditable(false); + version.setEditable(false); + software.setEditable(false); + supportSoftware.setEditable(false); + + } + + @Override + public boolean getParams(StreamExportParams streamExportParams) { + // Nothing to actually enter here. + return true; + } + + @Override + public void setParams(StreamExportParams streamExportParams) { + PamDataBlock dataBlock = getDataBlock(); + DetectionsHandler detHandler = getTethysControl().getDetectionsHandler(); + method.setText(detHandler.getMethodString(dataBlock)); + software.setText(detHandler.getSoftwareString(dataBlock)); + version.setText(detHandler.getVersionString(dataBlock)); + supportSoftware.setText(detHandler.getSupportSoftware(dataBlock) + " V" + detHandler.getSupportSoftwareVersion(dataBlock)); + } + +} diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index 1e67d67d..d04a8550 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -9,6 +9,7 @@ import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.JButton; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.border.TitledBorder; @@ -22,26 +23,37 @@ public class DetectionsExportWizard extends PamDialog { private PamDataBlock dataBlock; private CardLayout cardLayout; - private JPanel mainPanel; + private JPanel cardPanel; private GranularityCard granularityCard; private DescriptionCard descriptionCard; private JButton prevButton; private StreamExportParams streamExportParams; private ArrayList wizardCards = new ArrayList(); + private AlgorithmCard algorithmCard; + private ExportWorkerCard exportWorkerCard; private DetectionsExportWizard(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { super(parentFrame, "Detections Export", false); this.dataBlock = dataBlock; - cardLayout = new CardLayout(); - mainPanel = new JPanel(cardLayout); + streamExportParams = tethysControl.getTethysExportParams().getStreamParams(dataBlock); + if (streamExportParams == null) { + streamExportParams = new StreamExportParams(dataBlock.getLongDataName(), false); + } + + cardLayout = new CardLayout(); + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(BorderLayout.NORTH, new ExportStreamInfoPanel(dataBlock)); + cardPanel = new JPanel(cardLayout); + mainPanel.add(BorderLayout.CENTER, cardPanel); + + addCard(algorithmCard = new AlgorithmCard(tethysControl, dataBlock)); addCard(granularityCard = new GranularityCard(tethysControl, dataBlock)); addCard(descriptionCard = new DescriptionCard(tethysControl, dataBlock)); - - streamExportParams = tethysControl.getTethysExportParams().getStreamParams(dataBlock); + addCard(exportWorkerCard = new ExportWorkerCard(tethysControl, dataBlock)); - cardLayout.first(mainPanel); + cardLayout.first(cardPanel); setDialogComponent(mainPanel); @@ -56,10 +68,12 @@ public class DetectionsExportWizard extends PamDialog { }); setResizable(true); + +// this.get } private void addCard(ExportWizardCard wizPanel) { - mainPanel.add(wizPanel, wizPanel.getTitle()); + cardPanel.add(wizPanel, wizPanel.getTitle()); wizardCards.add(wizPanel); } @@ -73,6 +87,7 @@ public class DetectionsExportWizard extends PamDialog { for (ExportWizardCard wizCard : wizardCards) { wizCard.setParams(streamExportParams); } + enableControls(); // granularityCard.setParams(streamExportParams); } @@ -80,7 +95,7 @@ public class DetectionsExportWizard extends PamDialog { * Called when 'previous' button is clicked. */ protected void previousButton() { - cardLayout.previous(mainPanel); + cardLayout.previous(cardPanel); enableControls(); } @@ -89,7 +104,7 @@ public class DetectionsExportWizard extends PamDialog { int iCard = getCardIndex(); if (iCard < wizardCards.size()-1) { if (checkCurrentCard()) { - cardLayout.next(mainPanel); + cardLayout.next(cardPanel); enableControls(); } return false; @@ -123,7 +138,8 @@ public class DetectionsExportWizard extends PamDialog { int iCard = getCardIndex(); prevButton.setEnabled(iCard > 0); boolean isLast = iCard == wizardCards.size()-1; - getOkButton().setText(isLast ? "Export" : "Next"); + getOkButton().setEnabled(!isLast); +// getOkButton().setText(isLast ? "Export" : "Next"); } private boolean checkCurrentCard() { @@ -135,8 +151,8 @@ public class DetectionsExportWizard extends PamDialog { } private int getCardIndex() { - for (int i = 0; i < mainPanel.getComponentCount(); i++) { - Component component = mainPanel.getComponent(i); + for (int i = 0; i < cardPanel.getComponentCount(); i++) { + Component component = cardPanel.getComponent(i); if (component.isVisible()) { return i; } diff --git a/src/tethys/swing/export/ExportStreamInfoPanel.java b/src/tethys/swing/export/ExportStreamInfoPanel.java new file mode 100644 index 00000000..15371771 --- /dev/null +++ b/src/tethys/swing/export/ExportStreamInfoPanel.java @@ -0,0 +1,95 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.Enumeration; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.border.TitledBorder; + +import PamUtils.PamCalendar; +import PamView.dialog.PamGridBagContraints; +import PamguardMVC.PamDataBlock; +import dataMap.OfflineDataMap; + +public class ExportStreamInfoPanel extends JPanel { + + private PamDataBlock dataBlock; + + public ExportStreamInfoPanel(PamDataBlock dataBlock) { + this.dataBlock = dataBlock; + setBorder(new TitledBorder("Stream information")); + JPanel infoPanel = new JPanel(); + this.setLayout(new BorderLayout()); + this.add(BorderLayout.WEST, infoPanel); + infoPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + infoPanel.add(new JLabel(dataBlock.getLongDataName(), JLabel.LEFT), c); + c.gridy++; + OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); + if (dataMap == null) { + infoPanel.add(new JLabel("This stream contains no mapped data!!"), c); + return; + } + else { + String mapName = dataMap.getDataMapName(); + int nData = dataMap.getDataCount(); + int nPoints = dataMap.getNumMapPoints(); + if (nPoints == 0) { + infoPanel.add(new JLabel("This stream contains no mapped data!!"), c); + return; + } + long startT = dataMap.getMapStartTime(); + long endT = dataMap.getMapEndTime(); + String str = String.format("Data stream contains %d data items", nData); + c.gridy++; + infoPanel.add(new JLabel(str, JLabel.LEFT), c); + str = String.format("Between %s and %s UTC", PamCalendar.formatDBDateTime(startT), PamCalendar.formatDBDateTime(endT)); + c.gridy++; + infoPanel.add(new JLabel(str, JLabel.LEFT), c); + +// for (Enumeration keys = UIManager.getDefaults().keys(); keys.hasMoreElements();) { +// Object key = keys.nextElement(); +// Object value = UIManager.get(key); +//// if (key.toLowerCase().contains("frame") ) +// String opStr = String.format("key %s value %s", key, value); +// if (opStr.toLowerCase().contains("window") && opStr.toLowerCase().contains("color")) { +// System.out.println(opStr); +// } +// } + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + Object titleColour = UIManager.get("window"); + if (titleColour instanceof Color) { + setBackground(ExportStreamInfoPanel.this, (Color) titleColour); + } + else { + setBackground(ExportStreamInfoPanel.this, Color.WHITE); + } + } + }); + } + } + + public void setBackground(Component component, Color bg) { + component.setBackground(bg); + if (component instanceof JComponent) { + JComponent jComponent = (JComponent) component; + int nSub = jComponent.getComponentCount(); + for (int i = 0; i < nSub; i++) { + setBackground(jComponent.getComponent(i), bg); + } + } + } + + +} diff --git a/src/tethys/swing/export/ExportWorkerCard.java b/src/tethys/swing/export/ExportWorkerCard.java new file mode 100644 index 00000000..3b4f73ee --- /dev/null +++ b/src/tethys/swing/export/ExportWorkerCard.java @@ -0,0 +1,163 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamGridBagContraints; +import PamView.panel.PamNorthPanel; +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysStateObserver; +import tethys.detection.DetectionExportObserver; +import tethys.detection.DetectionExportProgress; +import tethys.detection.DetectionsHandler; +import tethys.output.StreamExportParams; + +public class ExportWorkerCard extends ExportWizardCard implements DetectionExportObserver { + + private JProgressBar progressBar; + + private JTextField progressText; + + private JTextField itemCount, skipCount, projectedCount; + + private JButton export, cancel; + + private StreamExportParams streamExportParams; + + public ExportWorkerCard(TethysControl tethysControl, PamDataBlock dataBlock) { + super(tethysControl, "Export", dataBlock); + setLayout(new BorderLayout()); + setBorder(new TitledBorder("Export data")); + JPanel exPanel = new PamNorthPanel(new GridBagLayout()); + this.add(BorderLayout.WEST, exPanel); + GridBagConstraints c = new PamGridBagContraints(); + progressBar = new JProgressBar(); + progressText = new JTextField(30); + itemCount = new JTextField(6); + skipCount = new JTextField(6); + projectedCount = new JTextField(6); + progressText.setEditable(false); + itemCount.setEditable(false); + skipCount.setEditable(false); + projectedCount.setEditable(false); + export = new JButton("Export data"); + cancel = new JButton("Cancel export"); + cancel.setEnabled(false); + export.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportData(); + } + }); + cancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + cancelExport(); + } + }); + + exPanel.add(new JLabel("Items written ", JLabel.RIGHT), c); + c.gridx++; + exPanel.add(itemCount, c); + c.gridx = 0; + c.gridy++; + exPanel.add(new JLabel("Items skipped ", JLabel.RIGHT), c); + c.gridx++; + exPanel.add(skipCount, c); + c.gridx = 0; + c.gridy++; + exPanel.add(new JLabel("Total expected ", JLabel.RIGHT), c); + c.gridx++; + exPanel.add(projectedCount, c); + c.gridx = 0; + c.gridy++; + + exPanel.add(new JLabel("Progress ... ", JLabel.RIGHT), c); + c.gridx++; + c.gridwidth = 3; + exPanel.add(progressBar, c); + c.gridy++; + exPanel.add(progressText, c); + c.gridwidth = 1; + c.gridx = 1; + c.gridy++; + exPanel.add(export, c); + c.gridx++; + exPanel.add(cancel, c); + } + + protected void exportData() { + DetectionsHandler detHandler = getTethysControl().getDetectionsHandler(); + detHandler.startExportThread(getDataBlock(), streamExportParams, this); + enableControls(DetectionExportProgress.STATE_GATHERING); + } + + protected void cancelExport() { + DetectionsHandler detHandler = getTethysControl().getDetectionsHandler(); + detHandler.cancelExport(); + } + + @Override + public boolean getParams(StreamExportParams streamExportParams) { + // TODO Auto-generated method stub + return true; + } + + @Override + public void setParams(StreamExportParams streamExportParams) { + this.streamExportParams = streamExportParams; + } + + @Override + public void update(DetectionExportProgress progress) { + if (progress == null) { + return; + } + if (progress.totalUnits > 0) { + itemCount.setText(String.format("%d", progress.exportCount)); + skipCount.setText(String.format("%d", progress.skipCount)); + long totExpected = progress.totalUnits; + if (progress.exportCount +progress.skipCount > 0) { + totExpected *= progress.exportCount/(progress.exportCount+progress.skipCount); + } + projectedCount.setText(String.format("%d", totExpected)); + itemCount.setText(String.format("%d", totExpected)); + long perc = (progress.exportCount+progress.skipCount) * 100 / progress.totalUnits; + progressBar.setValue((int) perc); + } + switch (progress.state) { + case DetectionExportProgress.STATE_GATHERING: + progressText.setText("Running export"); + break; + case DetectionExportProgress.STATE_CANCELED: + progressText.setText("Export cancelled"); + break; + case DetectionExportProgress.STATE_COMPLETE: + progressText.setText("Export complete"); + break; + case DetectionExportProgress.STATE_WRITING: + progressText.setText("Writing to Tethys: " + progress.currentDetections.getId()); + break; + } + enableControls(progress.state); + } + + private void enableControls(int state) { + boolean stopped = state == DetectionExportProgress.STATE_CANCELED || state == DetectionExportProgress.STATE_COMPLETE; + export.setEnabled(stopped); + cancel.setEnabled(!stopped); + } + +} diff --git a/src/tethys/swing/export/GranularityCard.java b/src/tethys/swing/export/GranularityCard.java index 768119aa..c7f3c4b8 100644 --- a/src/tethys/swing/export/GranularityCard.java +++ b/src/tethys/swing/export/GranularityCard.java @@ -21,6 +21,7 @@ import org.w3c.dom.Document; import PamController.settings.output.xml.PamguardXMLWriter; import PamView.dialog.PamGridBagContraints; +import PamView.panel.WestAlignedPanel; import PamguardMVC.PamDataBlock; import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; @@ -47,7 +48,7 @@ public class GranularityCard extends ExportWizardCard { // granularity GranularityEnumType[] grans = GranularityEnumType.values(); granularities = new JRadioButton[grans.length]; - JPanel granPanel = new JPanel(new GridBagLayout()); + JPanel granPanel = new WestAlignedPanel(new GridBagLayout()); GridBagConstraints c = new PamGridBagContraints(); granPanel.setBorder(new TitledBorder("Granularity")); ButtonGroup granGroup = new ButtonGroup(); From 35fa8270d2ddb4a5a6e57b2e2ddff3997299feef Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 4 Apr 2023 20:53:14 +0100 Subject: [PATCH 29/65] Changes to match server updates Deleting now working. am automatically calling a windows curl command to disable the cache on the server. --- .../SweepClassifierSetPaneFX.java | 3 +- src/effortmonitor/swing/EffortDialog.java | 2 +- src/tethys/TethysControl.java | 104 +++++----- src/tethys/TethysLocationFuncs.java | 26 ++- src/tethys/TethysMenuActions.java | 6 +- src/tethys/TethysState.java | 10 +- src/tethys/TethysStateObserver.java | 8 +- src/tethys/TethysTimeFuncs.java | 12 +- src/tethys/dbxml/DBQueryResult.java | 12 +- src/tethys/dbxml/DBXMLConnect.java | 188 ++++++++++-------- src/tethys/dbxml/DBXMLQueries.java | 125 +++++++----- src/tethys/dbxml/DMXMLQueryTest.java | 20 +- src/tethys/dbxml/ServerStatus.java | 8 +- src/tethys/output/StreamExportParams.java | 5 +- .../swing/PAMGuardDeploymentsTable.java | 67 ++++--- 15 files changed, 310 insertions(+), 286 deletions(-) diff --git a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java index 1ba1ba05..6fd77115 100644 --- a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java +++ b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierSetPaneFX.java @@ -56,6 +56,7 @@ import PamUtils.PamUtils; import PamView.PamSymbol; import PamView.dialog.PamGridBagContraints; import PamView.symbol.SymbolData; +import PamguardMVC.debug.Debug; import clickDetector.ClickClassifiers.basicSweep.CodeHost; import clickDetector.ClickClassifiers.basicSweep.SweepClassifier; import clickDetector.ClickClassifiers.basicSweep.SweepClassifierSet; @@ -1707,7 +1708,7 @@ public class SweepClassifierSetPaneFX extends SettingsPane { // } // } // else multiChan = true; - Debug.out.println("Check multi-channel: " + multiChan); +// Debug.out.println("Check multi-channel: " + multiChan); return multiChan; } diff --git a/src/effortmonitor/swing/EffortDialog.java b/src/effortmonitor/swing/EffortDialog.java index 4f4d8b7f..6850415f 100644 --- a/src/effortmonitor/swing/EffortDialog.java +++ b/src/effortmonitor/swing/EffortDialog.java @@ -43,7 +43,7 @@ public class EffortDialog extends PamDialog { mainPanel.add(new JLabel("Observer name or initials"), c); c.gridx++; mainPanel.add(observer = new JComboBox(), c); - outerOnly = new JRadioButton("Log uter scroll only"); + outerOnly = new JRadioButton("Log outer scroll only"); allActions = new JRadioButton("Log all scroll actions"); ButtonGroup bg = new ButtonGroup(); bg.add(allActions); diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index c3a450eb..c98a7a88 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -19,13 +19,13 @@ import javax.swing.Timer; import PamController.PamControlledUnit; import PamController.PamControlledUnitSettings; import PamController.PamController; +import PamController.PamControllerInterface; import PamController.PamSettingManager; import PamController.PamSettings; import PamView.PamTabPanel; import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; -import pamViewFX.PamSettingsMenuPane; import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.DBXMLQueries; @@ -43,8 +43,8 @@ import tethys.swing.TethysTabPanel; /** * Quick play with a simple system for outputting data to Tethys. At it's start - * this is simply going to offer a dialog and have a few functions which show how - * to access data within PAMGuard. + * this is simply going to offer a dialog and have a few functions which show how + * to access data within PAMGuard. * @author dg50 * */ @@ -53,23 +53,23 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet 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; - + private TethysTabPanel tethysTabPanel; - + private DBXMLQueries dbxmlQueries; - + private ArrayList stateObservers; - + private Timer serverCheckTimer; - + private ServerStatus lastServerStatus; - - private ArrayList dataBlockSynchInfos; - + + private ArrayList dataBlockSynchInfos; + private DeploymentHandler deploymentHandler; private DetectionsHandler detectionsHandler; @@ -89,7 +89,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet serverCheckTimer.setInitialDelay(0); PamSettingManager.getInstance().registerSettings(this); addStateObserver(this); - + if (PamController.getInstance().isInitializationComplete()) { // must be adding module later on ... SwingUtilities.invokeLater(new Runnable() { @@ -103,8 +103,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet /** * Get DBXML Connector. This class contains all the functions that are needed - * to talk to the database. - * @return DBXML functions. + * to talk to the database. + * @return DBXML functions. */ public DBXMLConnect getDbxmlConnect() { return dbxmlConnect; @@ -139,7 +139,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet tethysMenu.add(showDeps); return tethysMenu; } - + public void showProjectDeploymentsDialog() { ProjectDeploymentsDialog.showDialog(getGuiFrame(), this); } @@ -154,11 +154,11 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } return sets; } - + /** - * Get the synchronisation info for all datablocks. + * Get the synchronisation info for all datablocks. * This list should be static, but check it in case something has been - * added or removed. + * added or removed. * @return */ public ArrayList getSynchronisationInfos() { @@ -172,13 +172,13 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet dataBlockSynchInfos.add(new DatablockSynchInfo(this, aBlock)); } } - // and remove any which are no longer there. + // and remove any which are no longer there. for (DatablockSynchInfo synchInfo : dataBlockSynchInfos) { - if (dataBlocks.contains(synchInfo.getDataBlock()) == false) { + if (!dataBlocks.contains(synchInfo.getDataBlock())) { dataBlockSynchInfos.remove(synchInfo); } } - + return dataBlockSynchInfos; } @@ -193,7 +193,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } return null; } - + /** * open client in the default web browser */ @@ -234,34 +234,34 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } /** - * We'll probably want to + * We'll probably want to * @param parentFrame */ protected void tethysExport(JFrame parentFrame) { TethysExportParams newExportParams = TethysExportDialog.showDialog(parentFrame, this); if (newExportParams != null) { - // dialog returns null if cancel was pressed. + // dialog returns null if cancel was pressed. tethysExportParams = newExportParams; exportTethysData(tethysExportParams); } } /** - * We'll arrive here if the dialog has been opened and we want to export Tethys data. + * We'll arrive here if the dialog has been opened and we want to export Tethys data. * @param tethysExportParams2 */ private void exportTethysData(TethysExportParams tethysExportParams) { TethysExporter tethysExporter = new TethysExporter(this, tethysExportParams); - tethysExporter.doExport(); - + 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 + * so that the rest of PAMGuard can use it, but creating the * @return */ public DeploymentData getGlobalDeplopymentData() { @@ -271,14 +271,14 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet // deployment.setPlatform("Yay a platform"); // Instrument instrument = new Instrument(); // instrument.setType("machiney"); -// instrument.setInstrumentId("12345555"); +// instrument.setInstrumentId("12345555"); // deployment.setInstrument(instrument); // return false; // } - + MetaDataContol metaControl = (MetaDataContol) aUnit; - DeploymentData deploymentData = metaControl != null ? metaControl.getDeploymentData() : getTethysProjectData(); - + DeploymentData deploymentData = metaControl != null ? metaControl.getDeploymentData() : getTethysProjectData(); + // deploymentData.setProject("thisIsAProject"); //// deploymentData.setPlatform("Yay a platform"); // deploymentData.setCruise("cruisey"); @@ -286,14 +286,14 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet //// deploymentData.setInstrumentId("super instrument"); // deploymentData.setSite("in the ocean somewhere"); // deploymentData.setRegion("ocean water"); -//// deploymentData.setInstrumentType("sensor of sorts"); - +//// 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. + * general meta data provider is not present. * @return */ private DeploymentData getTethysProjectData() { @@ -301,7 +301,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } /** - * Add a new state observer. + * Add a new state observer. * @param stateObserver */ public void addStateObserver(TethysStateObserver stateObserver) { @@ -309,16 +309,16 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } /** - * Remove a state observer. + * Remove a state observer. * @param stateObserver - * @return true if it existed. + * @return true if it existed. */ public boolean removeStateObserver(TethysStateObserver stateObserver) { return stateObservers.remove(stateObserver); } - + /** - * Send state updates around to all state observers. + * Send state updates around to all state observers. * @param tethysState */ public void sendStateUpdate(TethysState tethysState) { @@ -327,7 +327,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } } /** - * A name for any deta selectors. + * A name for any deta selectors. * @return */ public String getDataSelectName() { @@ -342,15 +342,15 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet public void notifyModelChanged(int changeType) { super.notifyModelChanged(changeType); switch (changeType) { - case PamController.INITIALIZATION_COMPLETE: + case PamControllerInterface.INITIALIZATION_COMPLETE: initializationStuff(); break; } } - + /** - * Stuff to do on initial load (initialization complete or addition of - * a Tethys module after initialisation). + * Stuff to do on initial load (initialization complete or addition of + * a Tethys module after initialisation). */ private void initializationStuff() { deploymentHandler.createPamguardOverview(); @@ -360,9 +360,9 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet /** * 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 + * has changed since the last call to this function, so it's unlikely you'll * need to use the return value - * @return server status. + * @return server status. */ public ServerStatus checkServer() { ServerStatus serverState = dbxmlConnect.pingServer(); @@ -425,8 +425,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet /** * 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. + * 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/TethysLocationFuncs.java b/src/tethys/TethysLocationFuncs.java index fb624fbc..dbfa68d8 100644 --- a/src/tethys/TethysLocationFuncs.java +++ b/src/tethys/TethysLocationFuncs.java @@ -5,18 +5,16 @@ import Array.HydrophoneLocator; import Array.PamArray; import Array.Streamer; import GPS.GPSControl; -import GPS.GpsData; import GPS.GpsDataUnit; import PamUtils.LatLong; import PamUtils.PamUtils; -import PamguardMVC.PamDataUnit; import generalDatabase.DBControlUnit; import generalDatabase.PamConnection; import nilus.Deployment; import nilus.DeploymentRecoveryDetails; /** - * Function(s) to get location information for Tethys in the required format. + * Function(s) to get location information for Tethys in the required format. * @author dg50 * */ @@ -25,10 +23,10 @@ public class TethysLocationFuncs { /** * Get everything we need for a deployment document including the track # - * and the deployment / recovery information. Basically this means we - * have to load the GPS data, then potentially filter it. Slight risk this - * may all be too much for memory, but give it a go by loading GPS data for - * the deployment times. + * and the deployment / recovery information. Basically this means we + * have to load the GPS data, then potentially filter it. Slight risk this + * may all be too much for memory, but give it a go by loading GPS data for + * the deployment times. * @param deployment */ public static void getTrackAndPositionData(Deployment deployment) { @@ -41,16 +39,16 @@ public class TethysLocationFuncs { boolean ok = true; ok &= addPositionData(deployment.getDeploymentDetails()); ok &= addPositionData(deployment.getRecoveryDetails()); - + } - + /** - * Add position data to DeploymentRecoveryDetails. + * Add position data to DeploymentRecoveryDetails. * @param drd * @return */ public static boolean addPositionData(DeploymentRecoveryDetails drd) { - long timeMillis = TethysTimeFuncs.millisFromGregorianXML(drd.getAudioTimeStamp()); + long timeMillis = TethysTimeFuncs.millisFromGregorianXML(drd.getAudioTimeStamp()); LatLong pos = getLatLongData(timeMillis); if (pos == null) { return false; @@ -61,15 +59,15 @@ public class TethysLocationFuncs { drd.setDepthInstrumentM(-pos.getHeight()); return true; } - + public static LatLong getLatLongData(long timeMillis) { - // check the array time. + // check the array time. PamArray array = ArrayManager.getArrayManager().getCurrentArray(); Streamer aStreamer = array.getStreamer(0); GPSControl gpsControl = GPSControl.getGpsControl(); PamConnection con = DBControlUnit.findConnection(); if (gpsControl != null) { -// check GPS data are loaded for times around this. +// check GPS data are loaded for times around this. GpsDataUnit gpsData = (GpsDataUnit) gpsControl.getGpsDataBlock().getLogging().findClosestDataPoint(con, timeMillis); if (gpsData != null) { return gpsData.getGpsData(); diff --git a/src/tethys/TethysMenuActions.java b/src/tethys/TethysMenuActions.java index 38d147c7..193affb1 100644 --- a/src/tethys/TethysMenuActions.java +++ b/src/tethys/TethysMenuActions.java @@ -12,11 +12,11 @@ import javax.swing.JPopupMenu; import tethys.niluswraps.PDeployment; /* - * Some standard meny dirven functions which we may want to call from - * a few different places. + * 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) { diff --git a/src/tethys/TethysState.java b/src/tethys/TethysState.java index 87606780..0430af6b 100644 --- a/src/tethys/TethysState.java +++ b/src/tethys/TethysState.java @@ -1,10 +1,8 @@ package tethys; -import tethys.detection.DetectionExportProgress; - /** - * Basis for a message system which will get passed around whenever something happens in - * Tethys, whether it be a new connection, progress during data output, etc. + * Basis for a message system which will get passed around whenever something happens in + * Tethys, whether it be a new connection, progress during data output, etc. * @author dg50 * */ @@ -16,8 +14,8 @@ public class TethysState { 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!) EXPORTING // currently exporting data. may be a while ... - }; - + } + public StateType stateType; private Object stateObject; diff --git a/src/tethys/TethysStateObserver.java b/src/tethys/TethysStateObserver.java index 8738beda..7c8aa11d 100644 --- a/src/tethys/TethysStateObserver.java +++ b/src/tethys/TethysStateObserver.java @@ -1,13 +1,13 @@ package tethys; public interface TethysStateObserver { - + /** * Receive state updates when Tethys has done something (made a connection, moved some data, etc.)
- * Note that this is for RECEIVING state updates, not for sending them. To avoid infinite notifications - * loops, use tethysControl.sendStateUpdate(TethysState) if this component knows something. + * Note that this is for RECEIVING state updates, not for sending them. To avoid infinite notifications + * loops, use tethysControl.sendStateUpdate(TethysState) if this component knows something. * @param tethysState */ public void updateState(TethysState tethysState); - + } diff --git a/src/tethys/TethysTimeFuncs.java b/src/tethys/TethysTimeFuncs.java index 569f6f3d..927212ef 100644 --- a/src/tethys/TethysTimeFuncs.java +++ b/src/tethys/TethysTimeFuncs.java @@ -14,7 +14,7 @@ import javax.xml.datatype.XMLGregorianCalendar; import PamUtils.PamCalendar; public class TethysTimeFuncs { - + private static TimeZone timeZone = TimeZone.getTimeZone("UTC"); /* @@ -33,9 +33,9 @@ public class TethysTimeFuncs { return null; } } - + /** - * Convert a Gregorian calendar value back to milliseconds. + * Convert a Gregorian calendar value back to milliseconds. * @param xmlGregorian * @return */ @@ -47,9 +47,9 @@ public class TethysTimeFuncs { gc2.setTimeZone(timeZone); return gc2.getTimeInMillis(); } - + /** - * Make a Gregorian calendar object from a returned XML string. + * Make a Gregorian calendar object from a returned XML string. * @param gregorianString * @return */ @@ -75,7 +75,7 @@ public class TethysTimeFuncs { //// gCal.se // return gCal; } - + public static String formatGregorianTime(XMLGregorianCalendar gregCal) { if (gregCal == null) { return null; diff --git a/src/tethys/dbxml/DBQueryResult.java b/src/tethys/dbxml/DBQueryResult.java index 06a9c8ff..80a27246 100644 --- a/src/tethys/dbxml/DBQueryResult.java +++ b/src/tethys/dbxml/DBQueryResult.java @@ -14,11 +14,11 @@ import org.xml.sax.SAXException; public class DBQueryResult { public long queryTimeMillis; - + public String queryResult; - + public String schemaPlan; - + public Exception queryException; public DBQueryResult(long queryTimeMillis, String queryResult, String schemaPlan) { @@ -33,9 +33,9 @@ public class DBQueryResult { this.queryTimeMillis = queryTimeMillis; this.queryException = queryException; } - + /** - * Get the result as an XML document. + * Get the result as an XML document. * @return XML document * @throws ParserConfigurationException * @throws SAXException @@ -58,5 +58,5 @@ public class DBQueryResult { Document doc = builder.parse(new InputSource(new StringReader(queryResult))); return doc; } - + } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index 6da77e4c..3dff4c3b 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -4,28 +4,20 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.nio.file.Files; import java.util.ArrayList; -import java.util.List; import javax.xml.bind.JAXBException; -import java.nio.file.Files; -import java.nio.file.Path; - import dbxml.JerseyClient; import dbxml.Queries; import dbxml.uploader.Importer; -import nilus.Deployment; import nilus.MarshalXML; import tethys.TethysControl; import tethys.output.TethysExportParams; -import tethys.output.StreamExportParams; -import PamguardMVC.PamDataBlock; /** * Class containing functions for managing the database connection. Opening, closing, - * writing, keeping track of performance, etc. + * writing, keeping track of performance, etc. * @author Doug Gillespie, Katie O'Laughlin * */ @@ -33,23 +25,23 @@ public class DBXMLConnect { private TethysControl tethysControl; private File tempDirectory; - + private JerseyClient jerseyClient; - + private Queries queries; - + private String currentSiteURL; - + public DBXMLConnect(TethysControl tethysControl) { this.tethysControl = tethysControl; checkTempFolder(); - + } - + /** * Check the jersey client and the queries. Need to recreate - * if the url has changed. + * if the url has changed. * @return */ private boolean checkClient() { @@ -57,34 +49,34 @@ public class DBXMLConnect { return false; } TethysExportParams params = tethysControl.getTethysExportParams(); - if (currentSiteURL.equalsIgnoreCase(params.getFullServerName()) == false) { + if (!currentSiteURL.equalsIgnoreCase(params.getFullServerName())) { return false; } return true; } - + /** * Get the client. The client will only be recreated if the url changes * @return Jersy client */ public synchronized JerseyClient getJerseyClient() { - if (checkClient() == false) { + if (!checkClient()) { openConnections(); } return jerseyClient; } - + /** - * Get the Queries object. This will only be recreated if the client changes. + * Get the Queries object. This will only be recreated if the client changes. * @return */ public synchronized Queries getTethysQueries() { - if (checkClient() == false) { + if (!checkClient()) { openConnections(); } return queries; } - + /** * Update a document within Tethys. We're assuming that a * document with the same name in the same collection already @@ -95,13 +87,13 @@ public class DBXMLConnect { */ public String updateDocument(Object nilusDocument) { deleteDocument(nilusDocument); - return postToTethys(nilusDocument); + return postToTethys(nilusDocument); } - + /** - * Delete a nilus document from the database. The only field which + * Delete a nilus document from the database. The only field which * needs to be populated here is the Id. The code also uses the object - * class to identify the correct collection. + * class to identify the correct collection. * @param nilusDocument * @return */ @@ -115,7 +107,7 @@ public class DBXMLConnect { result = jerseyClient.removeDocument(collection, docId ); /** * Return from a sucessful delete is something like - * + * deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId++, recordPeriod); ['ECoastNARW0'] @@ -123,19 +115,19 @@ public class DBXMLConnect { */ } catch (Exception e) { - System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); + System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); } - forceFlush(); +// forceFlush(); return result == null; } - + /** * take a nilus object loaded with PamGuard data and post it to the Tethys database - * + * * @param pamGuardObjs a nilus object loaded with PamGuard data * @return error string, null string means there are no errors */ - public String postToTethys(Object nilusObject) + public String postToTethys(Object nilusObject) { Class objClass = nilusObject.getClass(); String collection = getTethysCollection(objClass.getName()); @@ -146,14 +138,14 @@ public class DBXMLConnect { File tempFile = new File(tempName); try { MarshalXML marshal = new MarshalXML(); - marshal.createInstance(objClass); + marshal.createInstance(objClass); // Path tempFile = Files.createTempFile("pamGuardToTethys", ".xml"); marshal.marshal(nilusObject, tempFile.toString()); fileError = Importer.ImportFiles(params.getFullServerName(), collection, new String[] { tempFile.toString() }, "", "", false); // System.out.println(fileError); - + tempFile.deleteOnExit(); } catch(IllegalArgumentException e) { // TODO Auto-generated catch block @@ -164,55 +156,55 @@ public class DBXMLConnect { } catch (JAXBException e) { // TODO Auto-generated catch block e.printStackTrace(); - } + } // System.out.println(fileError); return fileError; } - - /* - * force a fluch by sending a dummy document to th eimporter which will rail, but ... - */ - private void forceFlush() { - TethysExportParams params = new TethysExportParams(); - String fileError = null; - try { - fileError = Importer.ImportFiles(params.getFullServerName(), "NoCollection", - new String[] { "ThereIsNoFileE" }, "", "", false); - } - catch (Exception e) { - - } -// System.out.println(fileError); - - } - + +// /* +// * force a fluch by sending a dummy document to th eimporter which will rail, but ... +// */ +// private void forceFlush() { +// TethysExportParams params = new TethysExportParams(); +// String fileError = null; +// try { +// fileError = Importer.ImportFiles(params.getFullServerName(), "NoCollection", +// new String[] { "ThereIsNoFileE" }, "", "", false); +// } +// catch (Exception e) { +// +// } +//// System.out.println(fileError); +// +// } + /** - * Get a temp folder to hold xml output. This will be the standard + * Get a temp folder to hold xml output. This will be the standard * temp folder + /PAMGuardTethys. Files will be left here until PAMGUard - * exits then should delete automatically + * exits then should delete automatically */ private void checkTempFolder() { String javaTmpDirs = System.getProperty("java.io.tmpdir") + File.separator + "PAMGuardTethys"; - + File tempDir = new File(javaTmpDirs); - if (tempDir.exists() == false) { + if (!tempDir.exists()) { tempDir.mkdirs(); } if (tempDir.exists()) { tempDirectory = tempDir; - }; + } if (tempDirectory == null) { tempDirectory = new File(System.getProperty("java.io.tmpdir")); } - + } /** * Get a document Id string. All Document objects should have a getId() function * however they do not have a type hierarchy, so it can't be accessed directly. - * instead go via the class.getDeclaredMethod function and it should be possible to find - * it. + * instead go via the class.getDeclaredMethod function and it should be possible to find + * it. * @param nilusObject * @return document Id for any type of document, or null if the document doesn't have a getID function */ @@ -235,7 +227,7 @@ public class DBXMLConnect { } /** - * needs to be based on the document id, + * needs to be based on the document id, * @param nilusObject * @return */ @@ -256,29 +248,29 @@ public class DBXMLConnect { */ public String getTethysCollection(String className) { switch(className) { - case "nilus.Deployment": - return "Deployments"; - case "nilus.Detections": + case "nilus.Deployment": + return "Deployments"; + case "nilus.Detections": return "Detections"; - case "nilus.Calibration": - return "Calibrations"; - case "nilus.Ensemble": - return "Ensembles"; - case "nilus.Localization": - return "Localizations"; - case "nilus.SpeciesAbbreviation": - return "SpeciesAbbreviations"; - case "nilus.SourceMap": - return "SourceMaps"; - case "nilus.ITIS": - return "ITIS"; - case "nilus.ranks": + case "nilus.Calibration": + return "Calibrations"; + case "nilus.Ensemble": + return "Ensembles"; + case "nilus.Localization": + return "Localizations"; + case "nilus.SpeciesAbbreviation": + return "SpeciesAbbreviations"; + case "nilus.SourceMap": + return "SourceMaps"; + case "nilus.ITIS": + return "ITIS"; + case "nilus.ranks": return "ITIS_ranks"; - default: - return ""; + default: + return ""; } } - + /** * Delete a Deploymnet and any contained Detections document. Doesn't work ! * @param deploymentId @@ -301,7 +293,7 @@ public class DBXMLConnect { // } // } try { - String doc = queries.getDocument("Deployments", deploymentId); +// String doc = queries.getDocument("Deployments", deploymentId); // queries. result = jerseyClient.removeDocument("Deployments", deploymentId ); } @@ -319,9 +311,29 @@ public class DBXMLConnect { jerseyClient = new JerseyClient(currentSiteURL); queries = new Queries(jerseyClient); ServerStatus state = pingServer(); + + setCache(false); + return state.ok; } - + + + private void setCache(boolean cacheOn) { + // from Marie. 4/4/2022: Basically it is a PUT to http://localhost:9979/Tethys/cache/off (or on). + TethysExportParams params = tethysControl.getTethysExportParams(); + + String cmd = String.format("curl -X PUT -data \"\" %s/Tethys/cache/%s", params.getFullServerName(), cacheOn ? "on" : "off"); + System.out.println(cmd); + try { + Runtime.getRuntime().exec(cmd); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } +// CUrl curl = new CUrl(cmd); + + } + public synchronized void closeConnections() { jerseyClient = null; queries = null; @@ -329,8 +341,8 @@ public class DBXMLConnect { } /** - * Get the server state via a ping ? - * @return Server state ? + * Get the server state via a ping ? + * @return Server state ? */ public ServerStatus pingServer() { @@ -344,7 +356,7 @@ public class DBXMLConnect { return new ServerStatus(ok, null); } - - // add whatever calls are necessary ... + + // add whatever calls are necessary ... } diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index fd508107..a1f5b2c2 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -29,7 +29,6 @@ import nilus.Detections; import nilus.Helper; import tethys.TethysControl; import tethys.TethysTimeFuncs; -import tethys.niluswraps.PDeployment; import tethys.output.TethysExportParams; /** @@ -42,7 +41,7 @@ public class DBXMLQueries { private TethysControl tethysControl; private DBXMLConnect dbXMLConnect; - + public DBXMLQueries(TethysControl tethysControl, DBXMLConnect dbXMLConnect) { super(); this.tethysControl = tethysControl; @@ -75,7 +74,7 @@ public class DBXMLQueries { // String url = jerseyClient.getURL(); Queries queries = new Queries(jerseyClient); - + queryResult = jerseyClient.queryJSON(jsonQueryString, 0); schemaPlan = jerseyClient.queryJSON(jsonQueryString, 1); @@ -114,26 +113,33 @@ public class DBXMLQueries { if (doc == null) { return null; } - NodeList returns = doc.getElementsByTagName("Return"); + NodeList returns = doc.getElementsByTagName("Project"); // 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) { - Node depEl = ((Element) aNode).getFirstChild(); - if (depEl == null) { - continue; - } - if (depEl instanceof Element) { - Element projEl = (Element) ((Element) depEl).getFirstChild(); - String projName = projEl.getTextContent(); - if (projName != null) { - if (!projectNames.contains(projName)) { - projectNames.add(projName); - } - } + String projName = aNode.getTextContent(); + if (projName != null) { + if (!projectNames.contains(projName)) { + projectNames.add(projName); } } +// } +// if (aNode instanceof Element) { +// Node depEl = ((Element) aNode).getFirstChild(); +// if (depEl == null) { +// continue; +// } +// if (depEl instanceof Element) { +// Element projEl = (Element) ((Element) depEl).getFirstChild(); +// String projName = projEl.getTextContent(); +// if (projName != null) { +// if (!projectNames.contains(projName)) { +// projectNames.add(projName); +// } +// } +// } +// } } Collections.sort(projectNames); @@ -142,9 +148,9 @@ public class DBXMLQueries { } /** - * Get some basic (not all) data for deployments associated with a project. Note that + * Get some basic (not all) data for deployments associated with a project. Note that * this may include deployments which are NOT part of the current dataset. That requires - * a search on Instrument as well. + * a search on Instrument as well. * @param projectName * @return */ @@ -164,20 +170,25 @@ public class DBXMLQueries { if (doc == null) { return null; } - + // System.out.println(pamXMLWriter.getAsString(doc)); ArrayList deployments = new ArrayList<>(); - NodeList returns = doc.getElementsByTagName("Return"); + NodeList returns = doc.getElementsByTagName("Deployment"); +// if (returns.getLength() == 0) { +// // try REsult instead ! +// returns = doc.getElementsByTagName("Result"); +// } // System.out.println("N projects = " + returns.getLength()); int n = returns.getLength(); + // Queries queries = new Queries(null) 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"); @@ -218,7 +229,7 @@ public class DBXMLQueries { } return deployments; } - + /** * Get a list of Detections documents which associate with a datablock and a deploymentId. * @param dataBlock @@ -227,7 +238,7 @@ public class DBXMLQueries { */ public ArrayList getDetectionsDocuments(PamDataBlock dataBlock, String deploymentId) { /** - * first query for Detections documents associated with this deployment and datablock. + * first query for Detections documents associated with this deployment and datablock. */ 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/Algorithm/Software\",\"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/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"TheDeploymentId\"],\"optype\":\"binary\"}],\"enclose\":1}"; @@ -252,7 +263,10 @@ public class DBXMLQueries { } ArrayList detectionsNames = new ArrayList(); int count = 0; - NodeList returns = doc.getElementsByTagName("Return"); + NodeList returns = doc.getElementsByTagName("Detections"); +// if (returns.getLength() == 0) { +// returns = doc.getElementsByTagName("Result"); +// } for (int i = 0; i < returns.getLength(); i++) { Node aNode = returns.item(i); String docName = aNode.getTextContent(); @@ -260,9 +274,9 @@ public class DBXMLQueries { } return detectionsNames; } - + /** - * Get the names of all detection documents for a given deployment for all data streams. + * Get the names of all detection documents for a given deployment for all data streams. * @param deploymentId * @return */ @@ -273,17 +287,20 @@ public class DBXMLQueries { 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"); + if (returns.getLength() == 0) { + returns = doc.getElementsByTagName("Result"); + } for (int i = 0; i < returns.getLength(); i++) { Node aNode = returns.item(i); detectionDocs.add(aNode.getTextContent()); @@ -293,7 +310,7 @@ public class DBXMLQueries { public int countData(PamDataBlock dataBlock, String deploymentId) { // /** -// * first query for Detections documents associated with this deployment and datablock. +// * first query for Detections documents associated with this deployment and datablock. // */ // 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/Algorithm/Software\",\"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/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"TheDeploymentId\"],\"optype\":\"binary\"}],\"enclose\":1}"; @@ -324,17 +341,15 @@ public class DBXMLQueries { return 0; } int count = 0; - for (int i = 0; i < documentNames.size(); i++) { -// Node aNode = returns.item(i); - String docName = documentNames.get(i); + for (String docName : documentNames) { // System.out.println(aNode.getTextContent()); int count2 = countDetections2(docName); count += count2; //countDetecionsData(docName); - + } return count; } - + /** * Count on effort detections in a Detections document * @param docName @@ -349,7 +364,7 @@ public class DBXMLQueries { try { Queries queries = dbXMLConnect.getTethysQueries(); result = queries.QueryTethys(query); -// System.out.println(result); +// System.out.println(result); } catch (Exception e) { System.out.println("Error executing " + query); @@ -368,11 +383,11 @@ public class DBXMLQueries { } // /** -// * Get a count of the detections in a detections document. -// * Only looking in onEffort so far. +// * Get a count of the detections in a detections document. +// * Only looking in onEffort so far. // * @param deploymentId // * @param detectionDocId -// * @param dataBlock +// * @param dataBlock // * @return // */ // public int getDetectionsDetectionCount(String deploymentId, String detectionDocId, PamDataBlock dataBlock) { @@ -391,7 +406,7 @@ public class DBXMLQueries { // if (doc == null) { // return 0; // } -// +// //// System.out.println(pamXMLWriter.getAsString(doc)); // //// ArrayList detectionDocs = new ArrayList<>(); @@ -404,7 +419,7 @@ public class DBXMLQueries { // /** // * 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. +// * it wll probably get data for all deployments in a project, which may not be what we want. // * @param projectName // * @param dataPrefixes // * @return @@ -432,7 +447,7 @@ public class DBXMLQueries { /** * 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. + * that it can all happen off a single query. * @param id * @param dataBlockPrefixes * @return @@ -450,20 +465,20 @@ public class DBXMLQueries { 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; @@ -531,8 +546,8 @@ public class DBXMLQueries { } /** - * Get the basic information about a Detections document. This is basically everything apart from - * the actual detections themselves. + * Get the basic information about a Detections document. This is basically everything apart from + * the actual detections themselves. * @param aDoc * @return */ @@ -549,7 +564,7 @@ public class DBXMLQueries { return null; } // System.out.println(queryResult.queryResult); - + Detections detections = new Detections(); try { Helper.createRequiredElements(detections); @@ -564,7 +579,7 @@ public class DBXMLQueries { return null; } Element result = (Element) returns.item(0); - + DescriptionType description = detections.getDescription(); if (description == null) { description = new DescriptionType(); @@ -574,9 +589,9 @@ public class DBXMLQueries { description.setAbstract(getElementData(result, "Description.Abstract")); description.setMethod(getElementData(result, "Description.Method")); description.setObjectives(getElementData(result, "Description.Objectives")); - - - + + + // TODO Auto-generated method stub return detections; } diff --git a/src/tethys/dbxml/DMXMLQueryTest.java b/src/tethys/dbxml/DMXMLQueryTest.java index f6691f64..e16e202a 100644 --- a/src/tethys/dbxml/DMXMLQueryTest.java +++ b/src/tethys/dbxml/DMXMLQueryTest.java @@ -1,24 +1,14 @@ package tethys.dbxml; -import java.io.ByteArrayOutputStream; import java.io.StringReader; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactoryConfigurationError; -import javax.xml.transform.sax.SAXTransformerFactory; -import javax.xml.transform.stream.StreamResult; -import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; import org.xml.sax.InputSource; import PamController.settings.output.xml.PamguardXMLWriter; -import PamUtils.XMLUtils; import dbxml.JerseyClient; import tethys.output.TethysExportParams; @@ -30,23 +20,23 @@ public class DMXMLQueryTest { private void runTest() { TethysExportParams params = new TethysExportParams(); - + JerseyClient jerseyClient = new JerseyClient(params.getFullServerName()); - + // String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/DeploymentId\",\"Deployment/Site\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\"],\"select\":[],\"enclose\":1}"; // String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/Region\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/DeploymentId\",\"2\"],\"optype\":\"binary\"}],\"enclose\":1}"; //String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/Region\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/DeploymentId\",\"2\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; // String testJson = "{\"return\":[\"Deployment/Project\",\"Deployment/Region\",\"Deployment/DeploymentDetails/AudioTimeStamp\",\"Deployment/RecoveryDetails/AudioTimeStamp\",\"Deployment/DeploymentId\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/DeploymentId\",\"2\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; String testJson = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; // web browse to http://localhost:9779/Client - + String testResult = jerseyClient.queryJSON(testJson); Document doc = convertStringToXMLDocument(testResult); - + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); String formettedXML = pamXMLWriter.getAsString(doc, true); - + System.out.println(testResult); System.out.println(formettedXML); // try { diff --git a/src/tethys/dbxml/ServerStatus.java b/src/tethys/dbxml/ServerStatus.java index d79f6095..25c9ebbf 100644 --- a/src/tethys/dbxml/ServerStatus.java +++ b/src/tethys/dbxml/ServerStatus.java @@ -1,9 +1,9 @@ package tethys.dbxml; public class ServerStatus { - + public boolean ok; - + public Exception error; public ServerStatus(boolean ok, Exception error) { @@ -11,7 +11,7 @@ public class ServerStatus { this.ok = ok; this.error = error; } - + public String getFormatted() { if (ok) { return "Server OK"; @@ -30,6 +30,6 @@ public class ServerStatus { public String toString() { return getFormatted(); } - + } diff --git a/src/tethys/output/StreamExportParams.java b/src/tethys/output/StreamExportParams.java index 2ba4f2fd..02e81e2f 100644 --- a/src/tethys/output/StreamExportParams.java +++ b/src/tethys/output/StreamExportParams.java @@ -29,7 +29,10 @@ public class StreamExportParams implements Serializable { public GranularityEnumType granularity = GranularityEnumType.CALL; - public nilus.DescriptionType detectionDescription; + /* + * Can't have this here since it isn't serializable. + */ + transient public nilus.DescriptionType detectionDescription; public DescriptionType getDetectionDescription() { if (detectionDescription == null) { diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 59a8d354..7585ce85 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -86,7 +86,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { public void mouseClicked(MouseEvent e) { int aRow = table.getSelectedRow(); int col = table.getSelectedColumn(); - if (aRow >= 0 && aRow < selection.length && col == 6) { + if (aRow >= 0 && aRow < selection.length && col == TableModel.SELECTCOLUMN) { selection[aRow] = !selection[aRow]; for (DeploymentTableObserver obs : observers) { obs.selectionChanged(); @@ -193,7 +193,9 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { private class TableModel extends AbstractTableModel { - private String[] columnNames = {"Id", "Start", "Stop", "Duration", "Cycle", "Tethys Deployment", "Select"}; + private String[] columnNames = {"Id", "Start", "Stop", "Gap", "Duration", "Cycle", "Tethys Deployment", "Select"}; + + private static final int SELECTCOLUMN = 7; @Override public int getRowCount() { @@ -217,7 +219,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { @Override public Class getColumnClass(int columnIndex) { - if (columnIndex == 6) { + if (columnIndex == SELECTCOLUMN) { return Boolean.class; // return JCheckBox.class; } @@ -228,40 +230,45 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { public Object getValueAt(int rowIndex, int columnIndex) { RecordingPeriod period = deploymentOverview.getRecordingPeriods().get(rowIndex); // DeploymentRecoveryPair deplInfo = deploymentInfo.get(rowIndex); - if (columnIndex == 4) { + if (columnIndex == 5) { return deploymentOverview.getDutyCycleInfo(); } + if (columnIndex == 3 && rowIndex > 0) { + RecordingPeriod prevPeriod = deploymentOverview.getRecordingPeriods().get(rowIndex-1); + long gap = period.getRecordStart() - prevPeriod.getRecordStop(); + return PamCalendar.formatDuration(gap); + } 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 4: + // long t1 = TethysTimeFuncs.millisFromGregorianXML(deplInfo.deploymentDetails.getAudioTimeStamp()); + // long t2 = TethysTimeFuncs.millisFromGregorianXML(deplInfo.recoveryDetails.getAudioTimeStamp()); + return PamCalendar.formatDuration(period.getRecordStop()-period.getRecordStart()); + case 6: + PDeployment deployment = period.getMatchedTethysDeployment(); + return makeDeplString(period, deployment); + case SELECTCOLUMN: + // return selectBoxes[rowIndex]; + return selection[rowIndex]; + } + + return null; + } + @Override public boolean isCellEditable(int rowIndex, int columnIndex) { - return columnIndex == 6; - } - - 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); - case 6: -// return selectBoxes[rowIndex]; - return selection[rowIndex]; - } - - return null; + return columnIndex == SELECTCOLUMN; } private String makeDeplString(RecordingPeriod period, PDeployment deployment) { From dbc8f0c682c8c27f12514bb04f8bf7079919c360 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 4 Apr 2023 21:02:09 +0100 Subject: [PATCH 30/65] Men items to open collections in browser --- src/tethys/TethysControl.java | 70 +++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index c98a7a88..43f11e63 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -121,14 +121,52 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet // tethysExport(parentFrame); // } // }); - JMenuItem openClient = new JMenuItem("Open client in browser"); - openClient.addActionListener(new ActionListener() { + JMenuItem menuItem; + menuItem = new JMenuItem("Open client in browser"); + menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openTethysClient(); } }); - tethysMenu.add(openClient); + tethysMenu.add(menuItem); + + JMenuItem collections = new JMenu("Collections"); + + menuItem = new JMenuItem("Open Deployments collection in browser"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTethysCollection("Deployments"); + } + }); + collections.add(menuItem); + menuItem = new JMenuItem("Open Detections collection in browser"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTethysCollection("Detections"); + } + }); + collections.add(menuItem); + menuItem = new JMenuItem("Open Calibrations collection in browser"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTethysCollection("Calibrations"); + } + }); + collections.add(menuItem); + menuItem = new JMenuItem("Open Species Abbreviations collection in browser"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTethysCollection("SpeciesAbbreviations"); + } + }); + collections.add(menuItem); + tethysMenu.add(collections); + tethysMenu.addSeparator(); JMenuItem showDeps = new JMenuItem("Show project deployments"); showDeps.addActionListener(new ActionListener() { @Override @@ -198,7 +236,31 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet * open client in the default web browser */ public void openTethysClient() { - String urlString = tethysExportParams.getFullServerName() + "/Client"; +// String urlString = tethysExportParams.getFullServerName() + "/Client"; +// System.out.println("Opening url " + urlString); +// URL url = null; +// try { +// url = new URL(urlString); +// } catch (MalformedURLException e) { +// e.printStackTrace(); +// } +// if (url == null) { +// return; +// } +// try { +// Desktop.getDesktop().browse(url.toURI()); +// } catch (IOException e) { +// e.printStackTrace(); +// } catch (URISyntaxException e) { +// e.printStackTrace(); +// } + openTethysCollection("Client"); + } + /** + * open client in the default web browser + */ + public void openTethysCollection(String collectionName) { + String urlString = tethysExportParams.getFullServerName() + "/" + collectionName; System.out.println("Opening url " + urlString); URL url = null; try { From dfb78813ea3ed87635c0576b9a21d1ceb340085e Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 13 Apr 2023 17:41:52 +0100 Subject: [PATCH 31/65] Update TethysControl.java --- src/tethys/TethysControl.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 43f11e63..ec7bb23a 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -149,6 +149,14 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } }); collections.add(menuItem); + menuItem = new JMenuItem("Open Localizations collection in browser"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTethysCollection("Localizations"); + } + }); + collections.add(menuItem); menuItem = new JMenuItem("Open Calibrations collection in browser"); menuItem.addActionListener(new ActionListener() { @Override From 8a9719b425144868f6807c711e556db75604d1eb Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 1 May 2023 18:55:35 +0100 Subject: [PATCH 32/65] Tethys working again And a bit more playing around with metadata handling for Deploymnet documents --- .classpath | 4 +- src/Array/Hydrophone.java | 4 +- src/PamModel/PamModel.java | 3 +- .../sqlite/SqliteSQLTypes.java | 2 +- src/metadata/deployment/DeploymentData.java | 57 ++++++++++ src/tethys/TethysControl.java | 3 +- src/tethys/dbxml/DBXMLConnect.java | 65 ++++++++++- src/tethys/dbxml/DBXMLQueries.java | 3 + src/tethys/deployment/DeploymentHandler.java | 81 ++++++++++++-- src/tethys/deployment/DutyCycleInfo.java | 5 +- src/tethys/detection/DetectionsHandler.java | 2 +- src/tethys/niluswraps/PDescriptionType.java | 101 ++++++++++++++++++ src/tethys/output/StreamExportParams.java | 16 ++- src/tethys/swing/DeploymentExportPanel.java | 6 ++ src/tethys/swing/FancyClientButton.java | 84 +++++++++++++++ .../swing/PAMGuardDeploymentsTable.java | 65 +++++++++-- src/tethys/swing/TethysConnectionPanel.java | 6 +- .../swing/export/DescriptionTypePanel.java | 5 +- 18 files changed, 476 insertions(+), 36 deletions(-) create mode 100644 src/tethys/niluswraps/PDescriptionType.java create mode 100644 src/tethys/swing/FancyClientButton.java diff --git a/.classpath b/.classpath index 4339aee1..8f3f9191 100644 --- a/.classpath +++ b/.classpath @@ -6,7 +6,7 @@ - + @@ -17,7 +17,7 @@ - + diff --git a/src/Array/Hydrophone.java b/src/Array/Hydrophone.java index d726f0b8..2ec068d7 100644 --- a/src/Array/Hydrophone.java +++ b/src/Array/Hydrophone.java @@ -158,7 +158,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters { return sensitivity; } - protected void setSensitivity(double sensitivity) { + public void setSensitivity(double sensitivity) { this.sensitivity = sensitivity; } @@ -364,7 +364,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters { /** * @param preampGain The preampGain to set. */ - protected void setPreampGain(double preampGain) { + public void setPreampGain(double preampGain) { this.preampGain = preampGain; } diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index 055a7989..003d657b 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -46,8 +46,6 @@ import fftManager.PamFFTControl; import group3dlocaliser.Group3DLocaliserControl; import metadata.MetaDataContol; import meygenturbine.MeygenTurbine; -import networkTransfer.receive.BuoyStatusDataUnit; -import networkTransfer.receive.NetworkReceiver; import printscreen.PrintScreenControl; import rockBlock.RockBlockControl; import tethys.TethysControl; @@ -1098,6 +1096,7 @@ final public class PamModel implements PamModelInterface, PamSettings { * PamModel ! */ +// pluginList.add(new MorlaisWP1aPlugin()); // Load up whatever default classloader was used to create this class. Must use the same classloader // for all plugins, or else we will not be able to create proper dependencies between them or be able diff --git a/src/generalDatabase/sqlite/SqliteSQLTypes.java b/src/generalDatabase/sqlite/SqliteSQLTypes.java index 580c8c5e..4f2baf73 100644 --- a/src/generalDatabase/sqlite/SqliteSQLTypes.java +++ b/src/generalDatabase/sqlite/SqliteSQLTypes.java @@ -10,7 +10,7 @@ import PamUtils.PamCalendar; public class SqliteSQLTypes extends SQLTypes { - protected static final SQLiteConfig.DateClass dateClass = SQLiteConfig.DateClass.TEXT; + public static final SQLiteConfig.DateClass dateClass = SQLiteConfig.DateClass.TEXT; @Override public String typeToString(int sqlType, int length, boolean counter) { diff --git a/src/metadata/deployment/DeploymentData.java b/src/metadata/deployment/DeploymentData.java index 3f7e351b..d9aaaf19 100644 --- a/src/metadata/deployment/DeploymentData.java +++ b/src/metadata/deployment/DeploymentData.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamModel.parametermanager.FieldNotFoundException; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamUtils.LatLong; /** * Class to hold Deployment data in a form consistent with the ANSI PAM @@ -65,6 +66,46 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter * Name of geographic region. */ private String region; + + /** + * time of instrument deployment (different to recording start); + */ + private Long deploymentMillis; + + /** + * time of actual recovery (different to recording end); + */ + private Long recoveryMillis; + + private LatLong recoverLatLong; + + /** + * @return the deploymentMillis + */ + public Long getDeploymentMillis() { + return deploymentMillis; + } + + /** + * @param deploymentMillis the deploymentMillis to set + */ + public void setDeploymentMillis(Long deploymentMillis) { + this.deploymentMillis = deploymentMillis; + } + + /** + * @return the recoveryMillis + */ + public Long getRecoveryMillis() { + return recoveryMillis; + } + + /** + * @param recoveryMillis the recoveryMillis to set + */ + public void setRecoveryMillis(Long recoveryMillis) { + this.recoveryMillis = recoveryMillis; + } // /** // * Instrument type, e.g. HARP, EAR, Popup, DMON, etc. @@ -235,6 +276,22 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter this.region = region; } + /** + * Set the recovery position latlong for a static recorder. + * Deployment lat long is in the hydrophone array data. + * @param recoverLatLong + */ + public void setRecoveryLatLong(LatLong recoverLatLong) { + this.recoverLatLong = recoverLatLong; + } + + /** + * @return the recoverLatLong (may often be null) + */ + public LatLong getRecoverLatLong() { + return recoverLatLong; + } + // /** // * @return the instrumentType // */ diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index ec7bb23a..7d3a0e48 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -412,7 +412,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet public void notifyModelChanged(int changeType) { super.notifyModelChanged(changeType); switch (changeType) { - case PamControllerInterface.INITIALIZATION_COMPLETE: + case PamControllerInterface.INITIALIZE_LOADDATA: +// case PamControllerInterface.INITIALIZATION_COMPLETE: initializationStuff(); break; } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index 3dff4c3b..e7b54820 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -1,9 +1,15 @@ package tethys.dbxml; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.nio.file.Files; import java.util.ArrayList; import javax.xml.bind.JAXBException; @@ -31,6 +37,8 @@ public class DBXMLConnect { private Queries queries; private String currentSiteURL; + + public static String[] collections = {"Deployments", "Detections", "Localizations", "Calibrations", "SpeciesAbbreviations"}; public DBXMLConnect(TethysControl tethysControl) { this.tethysControl = tethysControl; @@ -136,13 +144,15 @@ public class DBXMLConnect { String tempName = getTempFileName(nilusObject); tempName = tempDirectory.getAbsolutePath() + File.separator + tempName + ".xml"; File tempFile = new File(tempName); + String bodgeName = tempName;//"C:\\Users\\dg50\\AppData\\Local\\Temp\\PAMGuardTethys\\Meygen2022_10a.xml"; try { MarshalXML marshal = new MarshalXML(); marshal.createInstance(objClass); // Path tempFile = Files.createTempFile("pamGuardToTethys", ".xml"); marshal.marshal(nilusObject, tempFile.toString()); +// tempFile = stripXMLHeader(tempFile); fileError = Importer.ImportFiles(params.getFullServerName(), collection, - new String[] { tempFile.toString() }, "", "", false); + new String[] { bodgeName }, "", "", false); // System.out.println(fileError); @@ -157,11 +167,62 @@ public class DBXMLConnect { // TODO Auto-generated catch block e.printStackTrace(); } -// System.out.println(fileError); + System.out.println(fileError); return fileError; } + /** + * Seems we have to get rid of the line + * which is being put there by the marshaller ? + * @param tempFile + */ + private File stripXMLHeader(File tempFile) { + // TODO Auto-generated method stub + + File tempTemp = new File(tempFile.getAbsolutePath().replace(".temp.xml", ".xml")); + try { + BufferedReader reader = new BufferedReader(new FileReader(tempFile)); + BufferedWriter writer = new BufferedWriter(new FileWriter(tempTemp)); + String line = reader.readLine(); + while (line != null) { + // see if the line has any unicode in it + int len = line.length(); + byte[] bytes = line.getBytes(); + if (len == bytes.length) { + System.out.println(line); + } + + if (line.startsWith(" getProjectDeployments(String projectName) { + if (projectName == null) { + return null; + } String qBase = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"%s\"],\"optype\":\"binary\"}],\"enclose\":1}"; String qStr = String.format(qBase, projectName); diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 578a3c99..a87f8d30 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -2,6 +2,8 @@ package tethys.deployment; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.List; import java.util.ListIterator; @@ -21,6 +23,8 @@ import Array.Streamer; import Array.ThreadingHydrophoneLocator; import PamController.PamControlledUnit; import PamController.PamController; +import PamUtils.LatLong; +import PamUtils.PamCalendar; import PamUtils.PamUtils; import PamguardMVC.PamDataBlock; import SoundRecorder.RecordingInfo; @@ -156,6 +160,20 @@ public class DeploymentHandler implements TethysStateObserver { // just load everything. Probably OK for the acqusition, but will bring down daqInfoDataBlock.loadViewerData(0, Long.MAX_VALUE, null); ArrayList allStatusData = daqInfoDataBlock.getDataCopy(); + /** + * Due to seird file overlaps we need to resort this by id if we can. + * + */ + Collections.sort(allStatusData, new Comparator() { + + @Override + public int compare(DaqStatusDataUnit o1, DaqStatusDataUnit o2) { + if (o1.getDatabaseIndex() == 0) { + return (int) (o1.getTimeMilliseconds()-o2.getTimeMilliseconds()); + } + return o1.getDatabaseIndex()-o2.getDatabaseIndex(); + } + }); ArrayList tempPeriods = null; @@ -168,8 +186,15 @@ public class DeploymentHandler implements TethysStateObserver { } if (tempPeriods == null || tempPeriods.size() == 0) { System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); + return null; } + int nPeriods = tempPeriods.size(); +// int i = 0; +// for (RecordingPeriod aP : tempPeriods) { +// System.out.printf("Pre merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), +// PamCalendar.formatDBDateTime(aP.getRecordStop())); +// } // now go through those and merge into longer periods where there is no gap between files. ListIterator iterator = tempPeriods.listIterator(); RecordingPeriod prevPeriod = null; @@ -191,6 +216,11 @@ public class DeploymentHandler implements TethysStateObserver { } prevPeriod = nextPeriod; } +// i = 0; +// for (RecordingPeriod aP : tempPeriods) { +// System.out.printf("Post merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), +// PamCalendar.formatDBDateTime(aP.getRecordStop())); +// } // System.out.printf("Data have %d distinct files, but only %d distinct recording periods\n", nPeriods, tempPeriods.size()); DutyCycleInfo dutyCycleinfo = assessDutyCycle(tempPeriods); // if it's duty cycles, then we only want a single entry. @@ -202,6 +232,17 @@ public class DeploymentHandler implements TethysStateObserver { deploymentPeriods = new ArrayList<>(); deploymentPeriods.add(new RecordingPeriod(tempPeriods.get(0).getRecordStart(), tempPeriods.get(tempPeriods.size()-1).getRecordStop())); } + /* + * do another sort of the deploymentPeriods. The start stops were in the order they went into the + * database in the hope that pairs were the right way round. Now check all data are/ + */ + Collections.sort(deploymentPeriods, new Comparator() { + @Override + public int compare(RecordingPeriod o1, RecordingPeriod o2) { + return (int) (o1.getRecordStart()-o2.getRecordStart()); + } + }); + DeploymentOverview deploymentOverview = new DeploymentOverview(dutyCycleinfo, deploymentPeriods); matchPamguard2Tethys(deploymentOverview, projectDeployments); this.deploymentOverview = deploymentOverview; @@ -300,7 +341,6 @@ public class DeploymentHandler implements TethysStateObserver { return cycleInfo; } - private ArrayList extractTimesFromStatus(ArrayList allStatusData) { ArrayList tempPeriods = new ArrayList<>(); long dataStart = Long.MAX_VALUE; @@ -315,14 +355,20 @@ public class DeploymentHandler implements TethysStateObserver { nStart++; dataStart = Math.min(dataStart, daqStatus.getTimeMilliseconds()); lastStart = daqStatus.getTimeMilliseconds(); +// System.out.println("Start at " + PamCalendar.formatDBDateTime(lastStart)); break; case "Stop": nStop++; dataEnd = Math.max(dataEnd, daqStatus.getEndTimeInMilliseconds()); long lastEnd = daqStatus.getEndTimeInMilliseconds(); if (lastStart != null) { +// System.out.printf("Adding period %s to %s\n", PamCalendar.formatDBDateTime(lastStart), +// PamCalendar.formatDBDateTime(lastEnd)); tempPeriods.add(new RecordingPeriod(lastStart, lastEnd)); } + else { +// System.out.println("Skipping stop at " + PamCalendar.formatDBDateTime(lastEnd)); + } lastStart = null; break; case "NextFile": @@ -505,16 +551,23 @@ public class DeploymentHandler implements TethysStateObserver { e.printStackTrace(); } DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); - String id = String.format("%s%d", globalDeplData.getProject(), i); + String id = String.format("%s_%d", globalDeplData.getProject(), i); deployment.setId(id); deployment.setDeploymentId(i); - DeploymentRecoveryDetails deploymentDetails = new DeploymentRecoveryDetails(); - DeploymentRecoveryDetails recoveryDetails = new DeploymentRecoveryDetails(); - deploymentDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); + DeploymentRecoveryDetails deploymentDetails = deployment.getDeploymentDetails(); + if (deploymentDetails == null) { + deploymentDetails = new DeploymentRecoveryDetails(); + } + DeploymentRecoveryDetails recoveryDetails = deployment.getRecoveryDetails(); + if (recoveryDetails == null) { + recoveryDetails = new DeploymentRecoveryDetails(); + } deploymentDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); - recoveryDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); recoveryDetails.setAudioTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); + + deploymentDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStart())); + recoveryDetails.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recordingPeriod.getRecordStop())); deployment.setDeploymentDetails(deploymentDetails); deployment.setRecoveryDetails(recoveryDetails); @@ -572,6 +625,22 @@ public class DeploymentHandler implements TethysStateObserver { String geomType = getGeometryType(); instrument.setGeometryType(geomType); deployment.setInstrument(instrument); + + // overwrite the default deployment and recovery times if there is non null data + Long depMillis = deploymentData.getDeploymentMillis(); + if (depMillis != null) { + deployment.getDeploymentDetails().setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(depMillis)); + } + Long recMillis = deploymentData.getRecoveryMillis(); + if (recMillis != null) { + deployment.getRecoveryDetails().setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recMillis)); + } + LatLong recLatLong = deploymentData.getRecoverLatLong(); + if (recLatLong != null) { + deployment.getRecoveryDetails().setLatitude(recLatLong.getLatitude()); + deployment.getRecoveryDetails().setLongitude(PamUtils.constrainedAngle(recLatLong.getLongitude())); + } + return true; } diff --git a/src/tethys/deployment/DutyCycleInfo.java b/src/tethys/deployment/DutyCycleInfo.java index 740b63c9..5bdbe89f 100644 --- a/src/tethys/deployment/DutyCycleInfo.java +++ b/src/tethys/deployment/DutyCycleInfo.java @@ -1,5 +1,7 @@ package tethys.deployment; +import PamUtils.PamCalendar; + public class DutyCycleInfo { public boolean isDutyCycled; @@ -24,7 +26,8 @@ public class DutyCycleInfo { return "No duty cycle"; } else { - return String.format("%3.1fs on, %3.1fs off, for %d cycles", meanOnTimeS, meanGapS, nCycles); + return String.format("%s on, %s off, for %d cycles", PamCalendar.formatDuration((long) (meanOnTimeS*1000)), + PamCalendar.formatDuration((long) (meanGapS*1000)), nCycles); } } diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 11baac0f..e559b752 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -466,7 +466,7 @@ public class DetectionsHandler { String prefix = deployment.deployment.getId(); detections.setId(String.format("%s_%d", prefix, uniqueDetectionsId++)); // detections.setDescription(dataProvider.getDescription(deployment, tethysExportParams)); - detections.setDescription(exportParams.detectionDescription); + detections.setDescription(exportParams.getNilusDetectionDescription()); DataSourceType dataSource = new DataSourceType(); dataSource.setDeploymentId(deployment.deployment.getId()); // dataSource.setEnsembleId(""); ToDo diff --git a/src/tethys/niluswraps/PDescriptionType.java b/src/tethys/niluswraps/PDescriptionType.java new file mode 100644 index 00000000..889d0684 --- /dev/null +++ b/src/tethys/niluswraps/PDescriptionType.java @@ -0,0 +1,101 @@ +package tethys.niluswraps; + +import java.io.Serializable; + +import nilus.DescriptionType; + +/** + * Because we want to save DescriptionType objects in serialised + * psfx files and because Nilus description types are not serialised + * these have to be wrapped in a total bodge way with reasonably convenient + * constructors and getters for converting back and forth from the nilus object. + * @author dg50 + * + */ +public class PDescriptionType implements Serializable { + + private static final long serialVersionUID = 1L; + + protected String objectives; + + protected String _abstract; + + protected String method; + + /** + * Constructor from a set of strings + * @param objectives + * @param _abstract + * @param method + */ + public PDescriptionType(String objectives, String _abstract, String method) { + super(); + this.objectives = objectives; + this._abstract = _abstract; + this.method = method; + } + + /** + * Construct from a nilus object + * @param descriptionType + */ + public PDescriptionType(DescriptionType descriptionType) { + this.objectives = descriptionType.getObjectives(); + this._abstract = descriptionType.getAbstract(); + this.method = descriptionType.getMethod(); + } + + public PDescriptionType() { + } + + public String getObjectives() { + return objectives; + } + + /** + * @return the _abstract + */ + public String getAbstract() { + return _abstract; + } + + /** + * @param _abstract the _abstract to set + */ + public void setAbstract(String _abstract) { + this._abstract = _abstract; + } + + /** + * @return the method + */ + public String getMethod() { + return method; + } + + /** + * @param method the method to set + */ + public void setMethod(String method) { + this.method = method; + } + + /** + * @param objectives the objectives to set + */ + public void setObjectives(String objectives) { + this.objectives = objectives; + } + + /** + * convert into a nilus object for output. + * @return + */ + public DescriptionType getDescription() { + DescriptionType descriptionType = new DescriptionType(); + descriptionType.setAbstract(_abstract); + descriptionType.setObjectives(objectives); + descriptionType.setMethod(method); + return descriptionType; + } +} diff --git a/src/tethys/output/StreamExportParams.java b/src/tethys/output/StreamExportParams.java index 02e81e2f..2db881fa 100644 --- a/src/tethys/output/StreamExportParams.java +++ b/src/tethys/output/StreamExportParams.java @@ -1,9 +1,11 @@ package tethys.output; +import java.io.IOException; import java.io.Serializable; import nilus.DescriptionType; import nilus.GranularityEnumType; +import tethys.niluswraps.PDescriptionType; /** * Parameters controlling export of a single stream. @@ -32,13 +34,21 @@ public class StreamExportParams implements Serializable { /* * Can't have this here since it isn't serializable. */ - transient public nilus.DescriptionType detectionDescription; + public PDescriptionType detectionDescription; - public DescriptionType getDetectionDescription() { + public PDescriptionType getDetectionDescription() { if (detectionDescription == null) { - detectionDescription = new DescriptionType(); + detectionDescription = new PDescriptionType(); } return detectionDescription; } + + /** + * Get the nilus detection description + * @return + */ + public DescriptionType getNilusDetectionDescription() { + return getDetectionDescription().getDescription(); + } } diff --git a/src/tethys/swing/DeploymentExportPanel.java b/src/tethys/swing/DeploymentExportPanel.java index dd9a7ce0..5a6c6f33 100644 --- a/src/tethys/swing/DeploymentExportPanel.java +++ b/src/tethys/swing/DeploymentExportPanel.java @@ -32,6 +32,7 @@ import nilus.Deployment; import nilus.Deployment.Data; import tethys.TethysControl; import tethys.TethysState; +import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; import tethys.deployment.RecordingPeriod; import tethys.niluswraps.PDeployment; @@ -149,6 +150,10 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT case NEWPROJECTSELECTION: updateDeployments(); enableControls(); + break; + case UPDATEMETADATA: + setInternal(); + break; } } @@ -258,6 +263,7 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT dbxmlConnect.postToTethys(deployment); } } + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); } private void enableControls() { diff --git a/src/tethys/swing/FancyClientButton.java b/src/tethys/swing/FancyClientButton.java new file mode 100644 index 00000000..3967ae28 --- /dev/null +++ b/src/tethys/swing/FancyClientButton.java @@ -0,0 +1,84 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; + +import tethys.TethysControl; +import tethys.dbxml.DBXMLConnect; + +/** + * Fancy button which has a normal button AND a drop down + * for other web client pages. + * @author dg50 + * + */ +public class FancyClientButton extends JPanel { + + private JButton clientButton; + private JButton dropButton; + private JPopupMenu collectionsMenu; + private TethysControl tethysControl; + + public FancyClientButton(TethysControl tethysControl) { + this.tethysControl = tethysControl; + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.ipadx = c.ipady = 0; + c.insets = new Insets(0,0,0,0); + clientButton = new JButton("Open Client"); + clientButton.setToolTipText("Open Tethys web client in default browser"); + dropButton = new JButton("v"); + dropButton.setToolTipText("Open Tethys collections pages in default browser"); + c.gridx = 0; + add(clientButton, c); + c.gridx++; + add(dropButton, c); + + String[] collections = DBXMLConnect.collections; + collectionsMenu = new JPopupMenu(); + for (int i = 0; i < collections.length; i++) { + JMenuItem menuItem = new JMenuItem(collections[i]); + menuItem.addActionListener(new OpenCollection(collections[i])); + collectionsMenu.add(menuItem); + } + + dropButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + collectionsMenu.show(dropButton, 0, 0); + } + }); + } + + public void addActionListener(ActionListener actionListener) { + clientButton.addActionListener(actionListener); + } + + private class OpenCollection implements ActionListener { + + private String collection; + + public OpenCollection(String collection) { + super(); + this.collection = collection; + } + + @Override + public void actionPerformed(ActionEvent e) { + tethysControl.openTethysCollection(collection); + } + + } + + +} diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 7585ce85..74eba98f 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -2,6 +2,8 @@ package tethys.swing; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; @@ -9,7 +11,9 @@ import java.util.Arrays; import javax.swing.JCheckBox; 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; @@ -17,10 +21,13 @@ import javax.swing.border.TitledBorder; import javax.swing.table.AbstractTableModel; import PamUtils.PamCalendar; +import PamView.dialog.warn.WarnOnce; import PamView.panel.PamPanel; import PamView.tables.SwingTableColumnWidths; +import nilus.Deployment; import tethys.TethysControl; import tethys.TethysState; +import tethys.TethysState.StateType; import tethys.deployment.DeploymentHandler; import tethys.deployment.DeploymentOverview; import tethys.deployment.RecordingPeriod; @@ -71,14 +78,14 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { @Override public void mousePressed(MouseEvent e) { if (e.isPopupTrigger()) { - showPopup(); + showPopup(e); } } @Override public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { - showPopup(); + showPopup(e); } } @@ -96,7 +103,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } - public void showPopup() { + public void showPopup(MouseEvent e) { int aRow = table.getSelectedRow(); int[] selRows = table.getSelectedRows(); if (selRows == null || selRows.length == 0) { @@ -111,21 +118,56 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { // make a list of RecordingPeriods which don't currently have a Deployment document ArrayList newPeriods = new ArrayList<>(); ArrayList allPeriods = deploymentOverview.getRecordingPeriods(); + ArrayList matchedDeployments = new ArrayList<>(); for (int i = 0; i < selRows.length; i++) { - if (allPeriods.get(selRows[i]).getMatchedTethysDeployment() == null) { + PDeployment tethysDeployment = allPeriods.get(selRows[i]).getMatchedTethysDeployment(); + if (tethysDeployment == null) { newPeriods.add(allPeriods.get(i)); } + else { + if (matchedDeployments.contains(tethysDeployment) == false) { + matchedDeployments.add(tethysDeployment); + } + } } - if (newPeriods.size() == 0) { - return; + if (matchedDeployments.size() == 1) { + JPopupMenu popMenu = new JPopupMenu(); + JMenuItem menuItem = new JMenuItem("Remove deployment document " + matchedDeployments.get(0)); + menuItem.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + deleteDeployment(matchedDeployments.get(0)); + } + }); + popMenu.add(menuItem); + popMenu.show(e.getComponent(), e.getX(), e.getY()); } - /* - * if we get here, we've one or more rows without a Tethys output, so can have - * a menu to create them. - */ +// 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. +// */ + } + protected void deleteDeployment(PDeployment pDeployment) { + Deployment dep = pDeployment.deployment; + if (dep == null) { + return; + } + int ans = WarnOnce.showWarning(getTethysControl().getGuiFrame(), "Delete Deployment document", + "Are you sure you want to delete the deployment document " + dep.getId(), WarnOnce.OK_CANCEL_OPTION); + if (ans == WarnOnce.CANCEL_OPTION) { + return; + } + boolean gone = getTethysControl().getDbxmlConnect().deleteDocument(dep); + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + } + /** * Get a list of selected recording periods. * @return list of selected periods. @@ -172,6 +214,9 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { private void updateDeployments() { DeploymentHandler deploymentHandler = getTethysControl().getDeploymentHandler(); deploymentOverview = deploymentHandler.getDeploymentOverview(); + if (deploymentOverview == null) { + return; + } int n = deploymentOverview.getRecordingPeriods().size(); if (selection.length < n) { selection = Arrays.copyOf(selection, n); diff --git a/src/tethys/swing/TethysConnectionPanel.java b/src/tethys/swing/TethysConnectionPanel.java index 95837d6a..23c531dd 100644 --- a/src/tethys/swing/TethysConnectionPanel.java +++ b/src/tethys/swing/TethysConnectionPanel.java @@ -62,7 +62,7 @@ public class TethysConnectionPanel extends TethysGUIPanel { private JButton newInstrument; - private JButton openClient; + private FancyClientButton openClient; public TethysConnectionPanel(TethysControl tethysControl) { super(tethysControl); @@ -73,8 +73,8 @@ public class TethysConnectionPanel extends TethysGUIPanel { serverSelButton.setToolTipText("Select server"); serverStatus = new ScrollingPamLabel(SERVERSTATUSLENGTH); serverName.setEditable(false); - openClient = new JButton("Open Client"); - openClient.setToolTipText("Open Tethys client in web browser"); + openClient = new FancyClientButton(tethysControl); +// openClient.setToolTipText("Open Tethys client in web browser"); // serverStatus.setEditable(false); serverSelButton.addActionListener(new ActionListener() { @Override diff --git a/src/tethys/swing/export/DescriptionTypePanel.java b/src/tethys/swing/export/DescriptionTypePanel.java index 2fc968cb..ffa75651 100644 --- a/src/tethys/swing/export/DescriptionTypePanel.java +++ b/src/tethys/swing/export/DescriptionTypePanel.java @@ -12,6 +12,7 @@ import javax.swing.border.TitledBorder; import PamView.dialog.PamDialog; import nilus.DescriptionType; +import tethys.niluswraps.PDescriptionType; /** * Panel containing the three test entry fields for nilus.DescriptionType @@ -65,7 +66,7 @@ public class DescriptionTypePanel { return mainPanel; } - public void setParams(DescriptionType description) { + public void setParams(PDescriptionType description) { if (description == null) { tObjectives.setText(null); tAbstract.setText(null); @@ -73,7 +74,7 @@ public class DescriptionTypePanel { } } - public boolean getParams(DescriptionType description) { + public boolean getParams(PDescriptionType description) { if (checkField(requireObjective, tObjectives) == false) { return PamDialog.showWarning(null, "Objectives", "The objectives field must be competed"); } From 71c8415bc87a32bb99c39f71c8948c902931792a Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sat, 6 May 2023 19:26:04 +0100 Subject: [PATCH 33/65] Playing with export options --- src/PamController/PamSensor.java | 20 ++ src/PamUtils/PamCalendar.java | 2 +- src/dataMap/DataStreamPanel.java | 4 +- src/soundtrap/STClickControl.java | 14 +- src/tethys/dbxml/DBXMLQueries.java | 3 + src/tethys/deployment/DeploymentHandler.java | 257 ++++++++++++++++++- src/tethys/output/TethysExportParams.java | 46 ++++ src/tethys/swing/DeploymentExportPanel.java | 86 +++---- src/tethys/swing/FancyClientButton.java | 24 +- 9 files changed, 386 insertions(+), 70 deletions(-) create mode 100644 src/PamController/PamSensor.java diff --git a/src/PamController/PamSensor.java b/src/PamController/PamSensor.java new file mode 100644 index 00000000..03e51efe --- /dev/null +++ b/src/PamController/PamSensor.java @@ -0,0 +1,20 @@ +package PamController; + +/** + * Interface to define modules which can be considered as sensors of some sort. + * e.g. depth and orientation modules and the SoundTrap clickdetecotr + * @author dg50 + * + */ +public interface PamSensor { + + public String getUnitName(); + + public String getUnitType(); + + public String getSensorDescription(); + + public String getSensorId(); + + +} diff --git a/src/PamUtils/PamCalendar.java b/src/PamUtils/PamCalendar.java index d41aa018..7c4c3eb8 100644 --- a/src/PamUtils/PamCalendar.java +++ b/src/PamUtils/PamCalendar.java @@ -47,7 +47,7 @@ public class PamCalendar { public static TimeZone defaultTimeZone = TimeZone.getTimeZone("UTC"); - private static TimeZone localTimeZone = TimeZone.getDefault(); + private static TimeZone localTimeZone = defaultTimeZone;// TimeZone.getDefault(); public static final long millisPerDay = 1000L*24L*3600L; diff --git a/src/dataMap/DataStreamPanel.java b/src/dataMap/DataStreamPanel.java index 957eadfd..aaa743e3 100644 --- a/src/dataMap/DataStreamPanel.java +++ b/src/dataMap/DataStreamPanel.java @@ -747,9 +747,9 @@ public class DataStreamPanel extends JPanel implements DataMapObserver { String tipText; if (startTimeArrow != null && startTimeArrow.contains(me.getPoint())) { - tipText = "Data Start: " + PamCalendar.formatDateTime(dataBlock.getCurrentViewDataStart(), true); + tipText = "Data Start: " + PamCalendar.formatDateTime(dataBlock.getCurrentViewDataStart(), false); } else if (endTimeArrow != null && endTimeArrow.contains(me.getPoint())) { - tipText = "Data End: " + PamCalendar.formatDateTime(dataBlock.getCurrentViewDataEnd(), true); + tipText = "Data End: " + PamCalendar.formatDateTime(dataBlock.getCurrentViewDataEnd(), false); } else { tipText = "Cursor: " + PamCalendar.formatDateTime(tm, true); } diff --git a/src/soundtrap/STClickControl.java b/src/soundtrap/STClickControl.java index f210c765..4457f4e7 100644 --- a/src/soundtrap/STClickControl.java +++ b/src/soundtrap/STClickControl.java @@ -37,6 +37,7 @@ import javax.swing.JSeparator; import org.pamguard.x3.sud.SUDClickDetectorInfo; import Acquisition.AcquisitionControl; +import PamController.PamSensor; import PamController.PamControlledUnitSettings; import PamController.PamController; import PamguardMVC.PamRawDataBlock; @@ -51,7 +52,7 @@ import soundtrap.sud.SudFileDWVHandler; * @author mo55 * */ -public class STClickControl extends ClickControl { +public class STClickControl extends ClickControl implements PamSensor { private SUDClickDetectorInfo sudClickDetectorInfo; @@ -217,6 +218,17 @@ public class STClickControl extends ClickControl { public void setSudClickDetectorInfo(SUDClickDetectorInfo sudClickDetectorInfo) { this.sudClickDetectorInfo = sudClickDetectorInfo; } + + @Override + public String getSensorDescription() { + String desc = String.format("SoundTrap Click Detector at %dHz", (int) getClickDataBlock().getSampleRate()); + return desc; + } + + @Override + public String getSensorId() { + return null; + } } diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index f906badd..40190546 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -264,6 +264,9 @@ public class DBXMLQueries { e.printStackTrace(); return null; } + if (doc == null) { + return null; + } ArrayList detectionsNames = new ArrayList(); int count = 0; NodeList returns = doc.getElementsByTagName("Detections"); diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index a87f8d30..42b87035 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -15,21 +15,24 @@ import Acquisition.AcquisitionControl; import Acquisition.AcquisitionParameters; import Acquisition.DaqStatusDataUnit; import Acquisition.DaqSystem; +import Acquisition.FolderInputSystem; import Array.ArrayManager; import Array.Hydrophone; import Array.HydrophoneLocator; import Array.PamArray; import Array.Streamer; import Array.ThreadingHydrophoneLocator; +import PamController.PamSensor; import PamController.PamControlledUnit; import PamController.PamController; import PamUtils.LatLong; -import PamUtils.PamCalendar; import PamUtils.PamUtils; import PamguardMVC.PamDataBlock; -import SoundRecorder.RecordingInfo; -import javafx.scene.chart.PieChart.Data; -import metadata.MetaDataContol; +import PamguardMVC.PamRawDataBlock; +import binaryFileStorage.BinaryStore; +import dataMap.OfflineDataMap; +import dataMap.OfflineDataMapPoint; +import generalDatabase.DBControlUnit; import metadata.deployment.DeploymentData; import nilus.Audio; import nilus.ChannelInfo; @@ -39,12 +42,14 @@ import nilus.ChannelInfo.DutyCycle.Regimen.RecordingIntervalS; import nilus.ChannelInfo.Sampling; import nilus.ChannelInfo.Sampling.Regimen; import nilus.Deployment; +import nilus.Deployment.Data; import nilus.Deployment.Instrument; import nilus.Deployment.SamplingDetails; import nilus.Deployment.Sensors; import nilus.DeploymentRecoveryDetails; import nilus.GeometryTypeM; import nilus.Helper; +import nilus.UnknownSensor; import pamMaths.PamVector; import pamMaths.STD; import tethys.TethysControl; @@ -52,7 +57,10 @@ import tethys.TethysLocationFuncs; import tethys.TethysState; import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; +import tethys.TethysState.StateType; +import tethys.dbxml.DBXMLConnect; import tethys.niluswraps.PDeployment; +import tethys.output.TethysExportParams; /** * Functions to gather data for the deployment document from all around PAMGuard. @@ -66,6 +74,13 @@ public class DeploymentHandler implements TethysStateObserver { private TethysControl tethysControl; + /** + * @return the tethysControl + */ + public TethysControl getTethysControl() { + return tethysControl; + } + private DeploymentOverview deploymentOverview; private ArrayList projectDeployments; @@ -184,6 +199,10 @@ public class DeploymentHandler implements TethysStateObserver { else { tempPeriods = extractTimesFromStatus(allStatusData); } + if (tempPeriods == null || tempPeriods.size() == 0) { + System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); + tempPeriods = extractTimesFromOutputMaps(); + } if (tempPeriods == null || tempPeriods.size() == 0) { System.out.println("Data appear to have no logged recording periods available either from the database or the raw recordings."); return null; @@ -255,6 +274,123 @@ public class DeploymentHandler implements TethysStateObserver { } + /** + * Exprt deployments docs. Playing with a couple of different ways of doing this. + * @param selectedDeployments + */ + public void exportDeployments(ArrayList selectedDeployments) { + if (false) { + exportSeparateDeployments(selectedDeployments); + } + else { + exportOneDeploymnet(selectedDeployments); + } + + } + /** + * Make one big deployment document with all the recording periods in it. + */ + private void exportOneDeploymnet(ArrayList selectedDeployments) { + // do the lot, whatever ... + selectedDeployments = getDeploymentOverview().getRecordingPeriods(); + int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId(); + RecordingPeriod onePeriod = new RecordingPeriod(selectedDeployments.get(freeId).getRecordStart(), + selectedDeployments.get(selectedDeployments.size()-1).getRecordStop()); + Deployment deployment = createDeploymentDocument(freeId, onePeriod); + // fill in a few things from here + DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); + deployment.setCruise(globalMeta.getCruise()); + deployment.setSite(globalMeta.getSite()); + if (selectedDeployments.size() > 1) { + // now need to remove the + SamplingDetails samplingDetails = deployment.getSamplingDetails(); + samplingDetails.getChannel().clear(); + for (int i = 0; i < selectedDeployments.size(); i++) { + addSamplingDetails(deployment, selectedDeployments.get(i)); + } + } + DBXMLConnect dbxmlConnect = getTethysControl().getDbxmlConnect(); + PDeployment exDeploymnet = onePeriod.getMatchedTethysDeployment(); + if (exDeploymnet != null) { + deployment.setId(exDeploymnet.deployment.getId()); + dbxmlConnect.updateDocument(deployment); + } + else { + dbxmlConnect.postToTethys(deployment); + } + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + } + + /** + * Make a separate deployment document for every recording period. + */ + private void exportSeparateDeployments(ArrayList selectedDeployments) { + + int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId(); + for (int i = 0; i < selectedDeployments.size(); i++) { + RecordingPeriod recordPeriod = selectedDeployments.get(i); + PDeployment exDeploymnet = recordPeriod.getMatchedTethysDeployment(); + Deployment deployment = null; + if (exDeploymnet != null) { + deployment = createDeploymentDocument(freeId, recordPeriod); + deployment.setId(exDeploymnet.deployment.getId()); + } + if (deployment == null) { + deployment = createDeploymentDocument(freeId++, recordPeriod); + } + // fill in a few things from here + DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); + deployment.setCruise(globalMeta.getCruise()); + deployment.setSite(globalMeta.getSite()); + // also need to sort out track data here, etc. + DBXMLConnect dbxmlConnect = getTethysControl().getDbxmlConnect(); + if (exDeploymnet != null) { + dbxmlConnect.updateDocument(deployment); + } + else { + dbxmlConnect.postToTethys(deployment); + } + } + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + } + + /** + * Get data times from any other datamap, since this will generally match the acquisition anyway + * @return + */ + private ArrayList extractTimesFromOutputMaps() { + OfflineDataMap bestMap = null; + PamDataBlock bestBlock = null; + long firstStart = Long.MAX_VALUE; + long lastEnd = Long.MIN_VALUE; + ArrayList dataBlocks = PamController.getInstance().getDetectorDataBlocks(); + for (PamDataBlock aBlock : dataBlocks) { + if (aBlock instanceof PamRawDataBlock) { + continue; // don't want acquisition ! + } + OfflineDataMap dataMap = aBlock.getPrimaryDataMap(); + if (dataMap == null) { + continue; + } + if (dataMap.getFirstDataTime() < firstStart && dataMap.getLastDataTime() > lastEnd) { + bestMap = dataMap; + bestBlock = aBlock; + firstStart = dataMap.getFirstDataTime(); + lastEnd = dataMap.getLastDataTime(); + } + } + if (bestMap == null) { + return null; + } + // get the times out of it. + ArrayList recPeriods = new ArrayList<>(); + List mapPoints = bestMap.getMapPoints(); + for (OfflineDataMapPoint mapPoint : mapPoints) { + recPeriods.add(new RecordingPeriod(mapPoint.getStartTime(), mapPoint.getEndTime())); + } + return recPeriods; + } + public DeploymentOverview getDeploymentOverview() { return deploymentOverview; } @@ -305,7 +441,7 @@ public class DeploymentHandler implements TethysStateObserver { * @return overlap in milliseconds */ public long getDeploymentOverlap(PDeployment aDeployment, RecordingPeriod aPeriod) { - long start = aPeriod.getRecordStart(); + long start = aPeriod.getRecordStart(); // recording period. long stop = aPeriod.getRecordStop(); long depStart = aDeployment.getAudioStart(); long depStop = aDeployment.getAudioEnd(); @@ -551,7 +687,8 @@ public class DeploymentHandler implements TethysStateObserver { e.printStackTrace(); } DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); - String id = String.format("%s_%d", globalDeplData.getProject(), i); + TethysExportParams exportParams = tethysControl.getTethysExportParams(); + String id = String.format("%s_%d", exportParams.getDatasetName(), i); deployment.setId(id); deployment.setDeploymentId(i); @@ -576,9 +713,11 @@ public class DeploymentHandler implements TethysStateObserver { getProjectData(deployment); - getSamplingDetails(deployment, recordingPeriod); + addSamplingDetails(deployment, recordingPeriod); getSensorDetails(deployment); + + getSensors(deployment); /** * Stuff that may need to be put into the UI: @@ -586,12 +725,101 @@ public class DeploymentHandler implements TethysStateObserver { * this may be for the export UI ? * Tracks: trackline information. General problem in PAMGUard. */ - + getDataDetails(deployment); return deployment; } + public String getBinaryDataURI() { + BinaryStore binStore = BinaryStore.findBinaryStoreControl(); + if (binStore != null) { + return binStore.getBinaryStoreSettings().getStoreLocation(); + } + return null; + } + + public String getDatabaseURI() { + DBControlUnit databaseControl = DBControlUnit.findDatabaseControl(); + if (databaseControl != null) { + return databaseControl.getLongDatabaseName(); + } + return null; + } + + public String getRawDataURI() { + try { + PamControlledUnit daq = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); + if (daq instanceof AcquisitionControl) { + AcquisitionControl daqCtrl = (AcquisitionControl) daq; + DaqSystem system = daqCtrl.findDaqSystem(null);// getAcquisitionProcess().getRunningSystem(); + if (system instanceof FolderInputSystem) { + FolderInputSystem fip = (FolderInputSystem) system; + return fip.getFolderInputParameters().recentFiles.get(0); + } + } + } + catch (Exception e) { + } + return "unknown"; + } + + private void getDataDetails(Deployment deployment) { + Data data = deployment.getData(); + if (data == null) { + data = new Data(); + deployment.setData(data); + } + nilus.Deployment.Data.Audio audio = data.getAudio(); + if (audio == null) { + audio = new nilus.Deployment.Data.Audio(); + data.setAudio(audio); + } + audio.setURI(getRawDataURI()); + String processed = "Database:"+getDatabaseURI(); + String binary = getBinaryDataURI(); + if (binary != null) { + binary += ";Binary:"+binary; + } + audio.setProcessed(processed); + + } + + /** + * Get sensor information. The Soundtrap CTD will count as a sensor. + * Modules that are sensors will have to implement a PAMSensor interface + * @param deployment + */ + private void getSensors(Deployment deployment) { + ArrayList sensorModules = PamController.getInstance().findControlledUnits(PamSensor.class, true); + if (sensorModules == null || sensorModules.size() == 0) { + return; + } + Sensors sensors = deployment.getSensors(); + if (sensors == null) { + sensors = new Sensors(); + deployment.setSensors(sensors); + } + List sensorList = sensors.getSensor(); + for (PamControlledUnit aUnit : sensorModules) { + PamSensor pamSensor = (PamSensor) aUnit; + UnknownSensor nilusSensor = new UnknownSensor(); + try { + Helper.createRequiredElements(nilusSensor); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } +// nilusSensor.setName(pamSensor.getUnitName()); + nilusSensor.setType(pamSensor.getUnitType()); + nilusSensor.setNumber(BigInteger.ZERO); + nilusSensor.setDescription(pamSensor.getSensorDescription()); + nilusSensor.setSensorId(pamSensor.getUnitType()); + + sensorList.add(nilusSensor); + } + } + /** * Add project Metadata to a Deploymnet document. This is currently being * made available in the MetaDataControl module which should be added to PAMGuard @@ -781,10 +1009,18 @@ public class DeploymentHandler implements TethysStateObserver { * @param deployment * @param recordingPeriod */ - private boolean getSamplingDetails(Deployment deployment, RecordingPeriod recordingPeriod) { - SamplingDetails samplingDetails = new SamplingDetails(); + private boolean addSamplingDetails(Deployment deployment, RecordingPeriod recordingPeriod) { + + SamplingDetails samplingDetails = deployment.getSamplingDetails(); + if (samplingDetails == null) { + samplingDetails = new SamplingDetails(); + deployment.setSamplingDetails(samplingDetails); + } // this is basically going to be a list of almost identical channel information // currently just for the first acquisition. May extend to more. + // see if there is > 1 acquisition. May want to include many. + ArrayList daqUnits = PamController.getInstance().findControlledUnits(AcquisitionControl.class); + AcquisitionControl daq = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); if (daq == null) { return false; @@ -858,7 +1094,6 @@ public class DeploymentHandler implements TethysStateObserver { * earlier to a wrapper around the Deployment class. */ } - deployment.setSamplingDetails(samplingDetails); return true; } diff --git a/src/tethys/output/TethysExportParams.java b/src/tethys/output/TethysExportParams.java index 7172b6cc..81ad0eb3 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 generalDatabase.DBControlUnit; import metadata.deployment.DeploymentData; @@ -30,6 +31,51 @@ public class TethysExportParams implements Serializable, Cloneable{ private HashMap streamParamsMap = new HashMap(); private DeploymentData deploymentData; + + /** + * PAMGuard HAS to have a dataset name to link to data in Tethys, or it all gets + * very confusing. This will be used in Deployment and Detection document names. + */ + private String datasetName; + + /** + * @return the datasetName + */ + public String getDatasetName() { + if (datasetName == null) { + datasetName = getDefaultDatasetName(); + } + return datasetName; + } + + private String getDefaultDatasetName() { + // get the database name. It must exist in viewer mode ! + DBControlUnit dbControl = DBControlUnit.findDatabaseControl(); + String dbName = dbControl.getDatabaseName(); + // strip off trailing file type. + int dPos = dbName.lastIndexOf('.'); + if (dPos > 0) { + dbName = dbName.substring(0, dPos); + } + /* + * if the name ends in database, then remove that too (this is quite + * common since it's the default for batch output + */ + if (dbName.toLowerCase().endsWith("database")) { + dbName = dbName.substring(0, dbName.length()-"database".length()); + } + if (dbName.endsWith("_")) { + dbName = dbName.substring(0, dbName.length()-1); + } + return dbName; + } + + /** + * @param datasetName the datasetName to set + */ + public void setDatasetName(String datasetName) { + this.datasetName = datasetName; + } @Override public TethysExportParams clone() { diff --git a/src/tethys/swing/DeploymentExportPanel.java b/src/tethys/swing/DeploymentExportPanel.java index 5a6c6f33..2c2c6332 100644 --- a/src/tethys/swing/DeploymentExportPanel.java +++ b/src/tethys/swing/DeploymentExportPanel.java @@ -34,6 +34,7 @@ import tethys.TethysControl; import tethys.TethysState; import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; +import tethys.deployment.DeploymentHandler; import tethys.deployment.RecordingPeriod; import tethys.niluswraps.PDeployment; @@ -94,7 +95,7 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT addPair("Cruise ", cruise, c); addPair("Raw data URI ", rawURI, c); addPair("Binary data URI ", binaryURI, c); - addPair("Datbase URI ", databaseURI, c); + addPair("Database URI ", databaseURI, c); addPair("Contact ", contact, c); addPair("Date ", date, c); addPair("Set from ", projectDeployments, c); @@ -197,31 +198,35 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT private void setDefaultStores() { + DeploymentHandler deploymentHandler = getTethysControl().getDeploymentHandler(); + binaryURI.setText(deploymentHandler.getBinaryDataURI()); + databaseURI.setText(deploymentHandler.getDatabaseURI()); + rawURI.setText(deploymentHandler.getRawDataURI()); - BinaryStore binStore = BinaryStore.findBinaryStoreControl(); - if (binStore != null) { - binaryURI.setText(binStore.getBinaryStoreSettings().getStoreLocation()); - } - - DBControlUnit databaseControl = DBControlUnit.findDatabaseControl(); - if (databaseControl != null) { - databaseURI.setText(databaseControl.getLongDatabaseName()); - } - - try { - PamControlledUnit daq = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); - if (daq instanceof AcquisitionControl) { - AcquisitionControl daqCtrl = (AcquisitionControl) daq; - DaqSystem system = daqCtrl.findDaqSystem(null);// getAcquisitionProcess().getRunningSystem(); - if (system instanceof FolderInputSystem) { - FolderInputSystem fip = (FolderInputSystem) system; - rawURI.setText(fip.getFolderInputParameters().recentFiles.get(0)); - } - } - } - catch (Exception e) { - rawURI.setText("unknown"); - } +// BinaryStore binStore = BinaryStore.findBinaryStoreControl(); +// if (binStore != null) { +// binaryURI.setText(binStore.getBinaryStoreSettings().getStoreLocation()); +// } +// +// DBControlUnit databaseControl = DBControlUnit.findDatabaseControl(); +// if (databaseControl != null) { +// databaseURI.setText(databaseControl.getLongDatabaseName()); +// } +// +// try { +// PamControlledUnit daq = PamController.getInstance().findControlledUnit(AcquisitionControl.class, null); +// if (daq instanceof AcquisitionControl) { +// AcquisitionControl daqCtrl = (AcquisitionControl) daq; +// DaqSystem system = daqCtrl.findDaqSystem(null);// getAcquisitionProcess().getRunningSystem(); +// if (system instanceof FolderInputSystem) { +// FolderInputSystem fip = (FolderInputSystem) system; +// rawURI.setText(fip.getFolderInputParameters().recentFiles.get(0)); +// } +// } +// } +// catch (Exception e) { +// rawURI.setText("unknown"); +// } } @@ -235,36 +240,9 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT if (selectedDeployments == null || selectedDeployments.size() == 0) { return; }; - int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId(); - for (int i = 0; i < selectedDeployments.size(); i++) { - RecordingPeriod recordPeriod = selectedDeployments.get(i); - PDeployment exDeploymnet = recordPeriod.getMatchedTethysDeployment(); - Deployment deployment = null; - if (exDeploymnet != null) { - deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId, recordPeriod); - deployment.setId(exDeploymnet.deployment.getId()); - } - if (deployment == null) { - deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId++, recordPeriod); - } - // fill in a few things from here - deployment.setCruise(cruise.getText()); - deployment.setSite(site.getText()); - // also need to sort out track data here, etc. -// Should really tidy this up a lot and move functionality to DeploymentHandler with all -// the metadata in a object ? -// Data data = new nilus.Deployment.Data(); -// d - DBXMLConnect dbxmlConnect = getTethysControl().getDbxmlConnect(); - if (exDeploymnet != null) { - dbxmlConnect.updateDocument(deployment); - } - else { - dbxmlConnect.postToTethys(deployment); - } - } - getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + getTethysControl().getDeploymentHandler().exportDeployments(selectedDeployments); } + private void enableControls() { boolean enable = selectedDeployments != null && selectedDeployments.size() > 0; diff --git a/src/tethys/swing/FancyClientButton.java b/src/tethys/swing/FancyClientButton.java index 3967ae28..deb9a8b4 100644 --- a/src/tethys/swing/FancyClientButton.java +++ b/src/tethys/swing/FancyClientButton.java @@ -1,6 +1,7 @@ package tethys.swing; import java.awt.BorderLayout; +import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -8,10 +9,12 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BoxLayout; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import javax.swing.border.EmptyBorder; import tethys.TethysControl; import tethys.dbxml.DBXMLConnect; @@ -32,17 +35,36 @@ public class FancyClientButton extends JPanel { public FancyClientButton(TethysControl tethysControl) { this.tethysControl = tethysControl; setLayout(new GridBagLayout()); +// setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); GridBagConstraints c = new GridBagConstraints(); c.ipadx = c.ipady = 0; c.insets = new Insets(0,0,0,0); + c.fill = GridBagConstraints.VERTICAL; clientButton = new JButton("Open Client"); clientButton.setToolTipText("Open Tethys web client in default browser"); - dropButton = new JButton("v"); + ImageIcon arrowDown= null; + try { + arrowDown = new ImageIcon(ClassLoader + .getSystemResource("Resources/SidePanelShowH.png")); + } + catch (Exception e) { + } + if (arrowDown != null) { + dropButton = new JButton(arrowDown); + } + else { + dropButton = new JButton("v"); + } dropButton.setToolTipText("Open Tethys collections pages in default browser"); c.gridx = 0; add(clientButton, c); c.gridx++; add(dropButton, c); + Insets dInsets = dropButton.getInsets(); + if (dInsets != null) { + dInsets.left = dInsets.right = 4; + dropButton.setBorder(new EmptyBorder(dInsets)); + } String[] collections = DBXMLConnect.collections; collectionsMenu = new JPopupMenu(); From 00410d20179a1ada8d87b1212b2c760902c510a2 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sun, 7 May 2023 19:00:37 +0100 Subject: [PATCH 34/65] Exception handling Exception handling and warning messages on Tethys transactions --- src/tethys/TethysControl.java | 24 ++ src/tethys/TethysMenuActions.java | 15 +- src/tethys/dbxml/DBXMLConnect.java | 288 ++++++++++-------- src/tethys/dbxml/DBXMLQueries.java | 55 +++- src/tethys/dbxml/TethysException.java | 18 ++ src/tethys/dbxml/TethysQueryException.java | 16 + src/tethys/deployment/DeploymentHandler.java | 30 +- src/tethys/detection/DetectionsHandler.java | 13 +- src/tethys/output/TethysExporter.java | 7 +- .../swing/DatablockDetectionsPanel.java | 7 +- .../swing/PAMGuardDeploymentsTable.java | 7 +- 11 files changed, 326 insertions(+), 154 deletions(-) create mode 100644 src/tethys/dbxml/TethysException.java create mode 100644 src/tethys/dbxml/TethysQueryException.java diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 7d3a0e48..f167bf32 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -23,6 +23,7 @@ import PamController.PamControllerInterface; import PamController.PamSettingManager; import PamController.PamSettings; import PamView.PamTabPanel; +import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; @@ -30,6 +31,7 @@ import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.DBXMLQueries; import tethys.dbxml.ServerStatus; +import tethys.dbxml.TethysException; import tethys.deployment.DeploymentHandler; import tethys.detection.DetectionsHandler; import tethys.niluswraps.PDeployment; @@ -507,4 +509,26 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet return detectionsHandler; } + public void showException(TethysException tethysException) { + String title = tethysException.getMessage(); + StackTraceElement[] stack = tethysException.getStackTrace(); + String msg = ""; + if (stack != null) { + msg = "Caused in"; + for (int i = 0; i < Math.min(stack.length, 2); i++) { + msg += "
" + stack[i].getClassName() + "." + stack[i].getMethodName(); + } + } + String xml = tethysException.getXmlError(); + if (xml != null) { +// msg += ""; + xml = xml.replace("<", "<"); + xml = xml.replace(">", ">"); + xml = xml.replace("\n", "
"); +// msg += xml; + msg += "
"+xml+"
"; + } + WarnOnce.showWarning(title, msg, WarnOnce.WARNING_MESSAGE); + } + } diff --git a/src/tethys/TethysMenuActions.java b/src/tethys/TethysMenuActions.java index 193affb1..625096a9 100644 --- a/src/tethys/TethysMenuActions.java +++ b/src/tethys/TethysMenuActions.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import tethys.dbxml.TethysException; import tethys.niluswraps.PDeployment; /* @@ -36,7 +37,11 @@ public class TethysMenuActions { menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - deleteDeployment(pDeployment); + try { + deleteDeployment(pDeployment); + } catch (TethysException e1) { + tethysControl.showException(e1); + } } }); menu.add(menuItem); @@ -47,7 +52,11 @@ public class TethysMenuActions { menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - deleteDeployment(pDeployment); + try { + deleteDeployment(pDeployment); + } catch (TethysException e1) { + tethysControl.showException(e1); + } } }); menu.add(menuItem); @@ -55,7 +64,7 @@ public class TethysMenuActions { menu.show(e.getComponent(), e.getX(), e.getY()); } - protected void deleteDeployment(PDeployment pDeployment) { + protected void deleteDeployment(PDeployment pDeployment) throws TethysException { tethysControl.getDbxmlConnect().deleteDeployment(pDeployment.deployment.getId()); } } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index e7b54820..26ed2b2e 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -37,7 +37,7 @@ public class DBXMLConnect { private Queries queries; private String currentSiteURL; - + public static String[] collections = {"Deployments", "Detections", "Localizations", "Calibrations", "SpeciesAbbreviations"}; public DBXMLConnect(TethysControl tethysControl) { @@ -85,6 +85,54 @@ public class DBXMLConnect { return queries; } + /** + * take a nilus object loaded with PamGuard data and post it to the Tethys database + * + * @param pamGuardObjs a nilus object loaded with PamGuard data + * @return error string, null string means there are no errors + * @throws TethysException + */ + public boolean postToTethys(Object nilusObject) throws TethysException + { + Class objClass = nilusObject.getClass(); + String collection = getTethysCollection(objClass.getName()); + TethysExportParams params = new TethysExportParams(); + String importReturn = null; + String tempName = getTempFileName(nilusObject); + tempName = tempDirectory.getAbsolutePath() + File.separator + tempName + ".xml"; + File tempFile = new File(tempName); + String bodgeName = tempName;//"C:\\Users\\dg50\\AppData\\Local\\Temp\\PAMGuardTethys\\Meygen2022_10a.xml"; + try { + MarshalXML marshal = new MarshalXML(); + marshal.createInstance(objClass); + marshal.marshal(nilusObject, tempFile.toString()); + // tempFile = stripXMLHeader(tempFile); + importReturn = Importer.ImportFiles(params.getFullServerName(), collection, + new String[] { bodgeName }, "", "", false); + + + tempFile.deleteOnExit(); + } catch(IllegalArgumentException e) { + throw new TethysException("IllegalArgumentException posting to Tethys: " + e.getMessage(), null); + } catch (IOException e) { + throw new TethysException("IOException posting to Tethys: " + e.getMessage(), null); + } catch (JAXBException e) { + throw new TethysException("JAXBException posting to Tethys: " + e.getMessage(), null); + } + + /* + * The returned string consists of the file name, then an XML report. + * Quite hard to see much common structure in this, so just look for + * two words, and + */ + boolean error = importReturn.contains(""); + boolean success = importReturn.contains(""); + if (error) { + throw new TethysException("Error posting to Tethys", importReturn); + } + return success; + } + /** * Update a document within Tethys. We're assuming that a * document with the same name in the same collection already @@ -92,8 +140,9 @@ public class DBXMLConnect { * the removedocument function * @param nilusDocument * @return + * @throws TethysException */ - public String updateDocument(Object nilusDocument) { + public boolean updateDocument(Object nilusDocument) throws TethysException { deleteDocument(nilusDocument); return postToTethys(nilusDocument); } @@ -104,15 +153,16 @@ public class DBXMLConnect { * class to identify the correct collection. * @param nilusDocument * @return + * @throws TethysException */ - public boolean deleteDocument(Object nilusDocument) { + public boolean deleteDocument(Object nilusDocument) throws TethysException { Class objClass = nilusDocument.getClass(); String collection = getTethysCollection(objClass.getName()); String docId = getDocumentId(nilusDocument); String result = null; try { - result = jerseyClient.removeDocument(collection, docId ); + result = jerseyClient.removeDocument(collection+" uio", docId ); /** * Return from a sucessful delete is something like * @@ -120,57 +170,73 @@ public class DBXMLConnect { ['ECoastNARW0'] +An error will throw an exception. */ } catch (Exception e) { - System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); +// System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); + String msg = String.format("Error deleting %s:%s", collection, docId); + throw new TethysException(msg, e.getLocalizedMessage()); } -// forceFlush(); - return result == null; + // forceFlush(); + return true; } /** - * take a nilus object loaded with PamGuard data and post it to the Tethys database - * - * @param pamGuardObjs a nilus object loaded with PamGuard data - * @return error string, null string means there are no errors + * Delete a Deploymnet and any contained Detections document. Doesn't work ! + * @param deploymentId + * @return + * @throws TethysException */ - public String postToTethys(Object nilusObject) - { - Class objClass = nilusObject.getClass(); - String collection = getTethysCollection(objClass.getName()); - TethysExportParams params = new TethysExportParams(); - String fileError = null; - String tempName = getTempFileName(nilusObject); - tempName = tempDirectory.getAbsolutePath() + File.separator + tempName + ".xml"; - File tempFile = new File(tempName); - String bodgeName = tempName;//"C:\\Users\\dg50\\AppData\\Local\\Temp\\PAMGuardTethys\\Meygen2022_10a.xml"; + public boolean deleteDeployment(String deploymentId) throws TethysException { + ArrayList detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocuments(deploymentId); + JerseyClient jerseyClient = getJerseyClient(); + Queries queries = null; + String result = null; try { - MarshalXML marshal = new MarshalXML(); - marshal.createInstance(objClass); -// Path tempFile = Files.createTempFile("pamGuardToTethys", ".xml"); - marshal.marshal(nilusObject, tempFile.toString()); -// tempFile = stripXMLHeader(tempFile); - fileError = Importer.ImportFiles(params.getFullServerName(), collection, - new String[] { bodgeName }, "", "", false); - -// System.out.println(fileError); - - tempFile.deleteOnExit(); - } catch(IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (JAXBException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + result = jerseyClient.removeDocument("Deployments", deploymentId ); } - System.out.println(fileError); - return fileError; + catch (Exception e) { + throw new TethysException("Error deleting deployment document " + deploymentId, e.getMessage()); + } + return true; } + /** + * check the return string from importFiles and if it's an + * error, throw an exception. Otherwise do nothing. + * @param fileError + */ + private void checkReturnString(String fileError) { + /** + * Example good string is + * +C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xml: 7360 bytes + + + added + + + 20080311_2DSimplex_0 + + + + +Example error (file not existing) +C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xmlnot: 0 bytes + + + C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xmlnot + does not exist + + + + + + */ + + + } /** * Seems we have to get rid of the line @@ -192,9 +258,9 @@ public class DBXMLConnect { if (len == bytes.length) { System.out.println(line); } - + if (line.startsWith(" detDocNames = tethysControl.getDbxmlQueries().getDetectionsDocuments(deploymentId); - JerseyClient jerseyClient = getJerseyClient(); - Queries queries = null; - 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 { -// String doc = queries.getDocument("Deployments", deploymentId); -// queries. - result = jerseyClient.removeDocument("Deployments", deploymentId ); - } - catch (Exception e) { -// e.printStackTrace(); - return false; - } - return true; - } - - public synchronized boolean openConnections() { TethysExportParams params = tethysControl.getTethysExportParams(); currentSiteURL = params.getFullServerName(); @@ -391,7 +423,7 @@ public class DBXMLConnect { // TODO Auto-generated catch block e.printStackTrace(); } -// CUrl curl = new CUrl(cmd); + // CUrl curl = new CUrl(cmd); } diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 40190546..13ee3ca5 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -54,8 +54,9 @@ public class DBXMLQueries { * Or will return null if the server is not connected * @param jsonQueryString * @return query result + * @throws TethysQueryException */ - private DBQueryResult executeQuery(String jsonQueryString) { + private DBQueryResult executeQuery(String jsonQueryString) throws TethysQueryException { long t1 = System.currentTimeMillis(); @@ -73,14 +74,15 @@ public class DBXMLQueries { JerseyClient jerseyClient = dbxmlConnect.getJerseyClient(); // String url = jerseyClient.getURL(); - Queries queries = new Queries(jerseyClient); +// Queries queries = new Queries(jerseyClient); queryResult = jerseyClient.queryJSON(jsonQueryString, 0); schemaPlan = jerseyClient.queryJSON(jsonQueryString, 1); } catch (Exception e) { - return new DBQueryResult(System.currentTimeMillis()-t1, e); +// return new DBQueryResult(System.currentTimeMillis()-t1, e); + throw new TethysQueryException("Error running JSON query", jsonQueryString); } return new DBQueryResult(System.currentTimeMillis()-t1, queryResult, schemaPlan); @@ -90,7 +92,13 @@ public class DBXMLQueries { String projectQuery = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; - DBQueryResult result = executeQuery(projectQuery); + DBQueryResult result; + try { + result = executeQuery(projectQuery); + } catch (TethysQueryException e) { + tethysControl.showException(e); + return null; + } if (result == null || result.queryResult == null) { return null; @@ -161,7 +169,12 @@ public class DBXMLQueries { String qBase = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"%s\"],\"optype\":\"binary\"}],\"enclose\":1}"; String qStr = String.format(qBase, projectName); - DBQueryResult result = executeQuery(qStr); + DBQueryResult result = null; + try { + result = executeQuery(qStr); + } catch (TethysQueryException e1) { + tethysControl.showException(e1); + } if (result == null) { return null; } @@ -253,7 +266,13 @@ public class DBXMLQueries { query = queryWithDepl.replace("TheDeploymentId", deploymentId); } query = query.replace("LongDataName", dataBlock.getLongDataName()); - DBQueryResult queryResult = executeQuery(query); + DBQueryResult queryResult = null; + try { + queryResult = executeQuery(query); + } catch (TethysQueryException e1) { + tethysControl.showException(e1); + return null; + } if (queryResult ==null) { return null; } @@ -289,7 +308,13 @@ public class DBXMLQueries { public ArrayList getDetectionsDocuments(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); + DBQueryResult queryResult = null; + try { + queryResult = executeQuery(queryStr); + } catch (TethysQueryException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } if (queryResult == null || queryResult.queryException != null) { return null; } @@ -461,7 +486,13 @@ public class DBXMLQueries { 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); + DBQueryResult result; + try { + result = executeQuery(queryString); + } catch (TethysQueryException e) { + tethysControl.showException(e); + return null; + } if (result == null || result.queryResult == null) { return null; } @@ -560,7 +591,13 @@ public class DBXMLQueries { public Detections getDetectionsDocInfo(String detectionsDocName) { 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/Description\",\"Detections/DataSource\",\"Detections/Algorithm\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; String query = queryBase.replace("DetectionsDocName", detectionsDocName); - DBQueryResult queryResult = executeQuery(query); + DBQueryResult queryResult; + try { + queryResult = executeQuery(query); + } catch (TethysQueryException e) { + tethysControl.showException(e); + return null; + } Document doc; try { doc = queryResult.getDocument(); diff --git a/src/tethys/dbxml/TethysException.java b/src/tethys/dbxml/TethysException.java new file mode 100644 index 00000000..034a1970 --- /dev/null +++ b/src/tethys/dbxml/TethysException.java @@ -0,0 +1,18 @@ +package tethys.dbxml; + +public class TethysException extends Exception { + + private static final long serialVersionUID = 1L; + + private String xmlError; + + public TethysException(String message, String xmlError) { + super(message); + this.xmlError = xmlError; + } + + public String getXmlError() { + return xmlError; + } + +} diff --git a/src/tethys/dbxml/TethysQueryException.java b/src/tethys/dbxml/TethysQueryException.java new file mode 100644 index 00000000..bb3dd741 --- /dev/null +++ b/src/tethys/dbxml/TethysQueryException.java @@ -0,0 +1,16 @@ +package tethys.dbxml; + +public class TethysQueryException extends TethysException { + + private String queryString; + + public TethysQueryException(String message, String queryString) { + super(message, null); + this.queryString = queryString; + } + + public String getQueryString() { + return queryString; + } + +} diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 42b87035..1c29d8b4 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -27,6 +27,7 @@ import PamController.PamControlledUnit; import PamController.PamController; import PamUtils.LatLong; import PamUtils.PamUtils; +import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import PamguardMVC.PamRawDataBlock; import binaryFileStorage.BinaryStore; @@ -59,6 +60,7 @@ import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; +import tethys.dbxml.TethysException; import tethys.niluswraps.PDeployment; import tethys.output.TethysExportParams; @@ -311,12 +313,17 @@ public class DeploymentHandler implements TethysStateObserver { } DBXMLConnect dbxmlConnect = getTethysControl().getDbxmlConnect(); PDeployment exDeploymnet = onePeriod.getMatchedTethysDeployment(); - if (exDeploymnet != null) { - deployment.setId(exDeploymnet.deployment.getId()); - dbxmlConnect.updateDocument(deployment); + try { + if (exDeploymnet != null) { + deployment.setId(exDeploymnet.deployment.getId()); + dbxmlConnect.updateDocument(deployment); + } + else { + dbxmlConnect.postToTethys(deployment); + } } - else { - dbxmlConnect.postToTethys(deployment); + catch (TethysException e) { + getTethysControl().showException(e); } getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); } @@ -344,11 +351,16 @@ public class DeploymentHandler implements TethysStateObserver { deployment.setSite(globalMeta.getSite()); // also need to sort out track data here, etc. DBXMLConnect dbxmlConnect = getTethysControl().getDbxmlConnect(); - if (exDeploymnet != null) { - dbxmlConnect.updateDocument(deployment); + try { + if (exDeploymnet != null) { + dbxmlConnect.updateDocument(deployment); + } + else { + dbxmlConnect.postToTethys(deployment); + } } - else { - dbxmlConnect.postToTethys(deployment); + catch (TethysException e) { + getTethysControl().showException(e); } } getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index e559b752..677addc0 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -34,6 +34,7 @@ import tethys.deployment.DeploymentHandler; import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; import tethys.dbxml.DBXMLConnect; +import tethys.dbxml.TethysException; import tethys.detection.DetectionGranularity.GRANULARITY; import tethys.niluswraps.PDeployment; import tethys.niluswraps.PDetections; @@ -434,7 +435,11 @@ public class DetectionsHandler { lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); exportObserver.update(prog); closeDetectionsDocument(currentDetections, mapPoint.getEndTime()); - dbxmlConnect.postToTethys(currentDetections); + try { + dbxmlConnect.postToTethys(currentDetections); + } catch (TethysException e) { + tethysControl.showException(e); + } currentDetections = null; } } @@ -443,7 +448,11 @@ public class DetectionsHandler { prog = new DetectionExportProgress(deployment, currentDetections, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); closeDetectionsDocument(currentDetections, deployment.getAudioEnd()); - dbxmlConnect.postToTethys(currentDetections); + try { + dbxmlConnect.postToTethys(currentDetections); + } catch (TethysException e) { + tethysControl.showException(e); + } currentDetections = null; } } diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index a624697c..96e8c38c 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -32,6 +32,7 @@ import metadata.deployment.DeploymentData; import nilus.Deployment; import tethys.TethysControl; import tethys.dbxml.DBXMLConnect; +import tethys.dbxml.TethysException; import tethys.deployment.DeploymentHandler; import tethys.deployment.DeploymentOverview; import tethys.deployment.DeploymentRecoveryPair; @@ -189,7 +190,11 @@ public class TethysExporter { Deployment deployment = deploymentHandler.createDeploymentDocument(i++, recordingPeriod); // System.out.println(deployment.toString()); deploymentDocs.add(deployment); - tethysControl.getDbxmlConnect().postToTethys(deployment); + try { + tethysControl.getDbxmlConnect().postToTethys(deployment); + } catch (TethysException e) { + tethysControl.showException(e); + } } diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index f0265439..99042e11 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -20,6 +20,7 @@ import PamView.tables.SwingTableColumnWidths; import PamguardMVC.PamDataBlock; import nilus.Detections; import tethys.TethysControl; +import tethys.dbxml.TethysException; import tethys.detection.StreamDetectionsSummary; import tethys.niluswraps.PDetections; @@ -117,7 +118,11 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } protected void deleteDocument(PDetections pDets) { - getTethysControl().getDbxmlConnect().deleteDocument(pDets.detections); + try { + getTethysControl().getDbxmlConnect().deleteDocument(pDets.detections); + } catch (TethysException e) { + getTethysControl().showException(e); + } selectDataBlock(dataBlock); // force table update. } diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 74eba98f..7ec8459d 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -28,6 +28,7 @@ import nilus.Deployment; import tethys.TethysControl; import tethys.TethysState; import tethys.TethysState.StateType; +import tethys.dbxml.TethysException; import tethys.deployment.DeploymentHandler; import tethys.deployment.DeploymentOverview; import tethys.deployment.RecordingPeriod; @@ -164,7 +165,11 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { if (ans == WarnOnce.CANCEL_OPTION) { return; } - boolean gone = getTethysControl().getDbxmlConnect().deleteDocument(dep); + try { + boolean gone = getTethysControl().getDbxmlConnect().deleteDocument(dep); + } catch (TethysException e) { + getTethysControl().showException(e); + } getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); } From 49843b405fbdb8c502eaf3db805c71a0e8cc4489 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sun, 7 May 2023 20:06:59 +0100 Subject: [PATCH 35/65] Some query changes Changes in server code required update of some of the json for existing queries. --- src/tethys/TethysControl.java | 14 ++++- src/tethys/dbxml/DBXMLQueries.java | 73 ++++++++++++++++++++-- src/tethys/dbxml/TethysQueryException.java | 3 + src/warnings/WarningSystem.java | 2 +- 4 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index f167bf32..2fa785b1 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -32,6 +32,7 @@ import tethys.dbxml.DBXMLConnect; import tethys.dbxml.DBXMLQueries; import tethys.dbxml.ServerStatus; import tethys.dbxml.TethysException; +import tethys.dbxml.TethysQueryException; import tethys.deployment.DeploymentHandler; import tethys.detection.DetectionsHandler; import tethys.niluswraps.PDeployment; @@ -515,17 +516,24 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet String msg = ""; if (stack != null) { msg = "Caused in"; - for (int i = 0; i < Math.min(stack.length, 2); i++) { + for (int i = 0; i < Math.min(stack.length, 3); i++) { msg += "
" + stack[i].getClassName() + "." + stack[i].getMethodName(); } } + if (tethysException instanceof TethysQueryException) { + TethysQueryException tqe = (TethysQueryException) tethysException; +// msg += tqe. + } + String xml = tethysException.getXmlError(); if (xml != null) { -// msg += ""; + /** + * html can't handle the < and > in xml without getting very confused + * but it seems to work fine if they are replaced with their html codes. + */ xml = xml.replace("<", "<"); xml = xml.replace(">", ">"); xml = xml.replace("\n", "
"); -// msg += xml; msg += "
"+xml+"
"; } WarnOnce.showWarning(title, msg, WarnOnce.WARNING_MESSAGE); diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 13ee3ca5..8529a286 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -30,6 +30,8 @@ import nilus.Helper; import tethys.TethysControl; import tethys.TethysTimeFuncs; import tethys.output.TethysExportParams; +import warnings.PamWarning; +import warnings.WarningSystem; /** * Some standard queries we're going to want to make from various @@ -41,11 +43,14 @@ public class DBXMLQueries { private TethysControl tethysControl; private DBXMLConnect dbXMLConnect; + + private PamWarning queryWarning; public DBXMLQueries(TethysControl tethysControl, DBXMLConnect dbXMLConnect) { super(); this.tethysControl = tethysControl; this.dbXMLConnect = dbXMLConnect; + queryWarning = new PamWarning("Tethys Query", null, 0); } /** @@ -57,7 +62,63 @@ public class DBXMLQueries { * @throws TethysQueryException */ private DBQueryResult executeQuery(String jsonQueryString) throws TethysQueryException { + long t1 = System.currentTimeMillis(); + DBQueryResult result = null; + TethysQueryException tException = null; + try { + result = executeQueryT(jsonQueryString); + } + catch (TethysQueryException e) { + tException = e; + } + if (result == null) { + // try pinging the server and throw an exception if it's not alive. + ServerStatus serverStatus = tethysControl.getDbxmlConnect().pingServer(); + if (serverStatus.ok) { + queryWarning.setWarnignLevel(2); + queryWarning.setWarningMessage("null return from Tethys json query"); + queryWarning.setWarningTip(jsonQueryString); + queryWarning.setEndOfLife(Long.MAX_VALUE); + WarningSystem.getWarningSystem().addWarning(queryWarning); + } + else { + queryWarning.setWarnignLevel(2); + if (serverStatus.error != null) { + queryWarning.setWarningMessage(serverStatus.error.getMessage()); + } + else { + queryWarning.setWarningMessage("Unknown Tethys server error"); + } + queryWarning.setWarningTip(jsonQueryString); + queryWarning.setEndOfLife(Long.MAX_VALUE); + WarningSystem.getWarningSystem().addWarning(queryWarning); + return null; + } + } + long t2 = System.currentTimeMillis(); + if (tException != null) { + // display query warning then throw the exception anyway + queryWarning.setWarnignLevel(2); + queryWarning.setWarningMessage("Error running Tethys json query"); + queryWarning.setWarningTip(jsonQueryString); + queryWarning.setEndOfLife(Long.MAX_VALUE); + WarningSystem.getWarningSystem().addWarning(queryWarning); + throw tException; + } + else { + // clear warning, but say how long query took still + queryWarning.setWarnignLevel(0); + queryWarning.setWarningMessage(String.format("Tethys json query executed in %4.2f seconds", (double) (t2-t1)/1000.)); + queryWarning.setWarningTip(jsonQueryString); + WarningSystem.getWarningSystem().addWarning(queryWarning); + queryWarning.setEndOfLife(t2+10000); + return result; + } + + } + + private DBQueryResult executeQueryT(String jsonQueryString) throws TethysQueryException { long t1 = System.currentTimeMillis(); DBXMLConnect dbxmlConnect = tethysControl.getDbxmlConnect(); @@ -72,8 +133,6 @@ public class DBXMLQueries { try { JerseyClient jerseyClient = dbxmlConnect.getJerseyClient(); -// String url = jerseyClient.getURL(); - // Queries queries = new Queries(jerseyClient); queryResult = jerseyClient.queryJSON(jsonQueryString, 0); @@ -81,7 +140,6 @@ public class DBXMLQueries { } catch (Exception e) { -// return new DBQueryResult(System.currentTimeMillis()-t1, e); throw new TethysQueryException("Error running JSON query", jsonQueryString); } @@ -255,9 +313,10 @@ public class DBXMLQueries { public ArrayList getDetectionsDocuments(PamDataBlock dataBlock, String deploymentId) { /** * first query for Detections documents associated with this deployment and datablock. + * updated May 23 */ - 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/Algorithm/Software\",\"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/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"TheDeploymentId\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String queryNoDepl = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String queryWithDepl = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/DataSource/DeploymentId\",\"TheDeploymentId\"],\"optype\":\"binary\"},{\"op\":\"=\",\"operands\":[\"Detections/Algorithm/Software\",\"LongDataName\"],\"optype\":\"binary\"}],\"enclose\":1}"; String query; if (deploymentId == null) { query = queryNoDepl; @@ -589,7 +648,9 @@ public class DBXMLQueries { * @return */ public Detections getDetectionsDocInfo(String detectionsDocName) { - 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/Description\",\"Detections/DataSource\",\"Detections/Algorithm\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String oldqueryBase = "{\"species\":{\"query\":{\"op\":\"lib:abbrev2tsn\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]},\"return\":{\"op\":\"lib:tsn2abbrev\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; + // updated May 23 + String queryBase = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\",\"Detections/QualityAssurance\",\"Detections/UserId\",\"Detections/MetadataInfo\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; String query = queryBase.replace("DetectionsDocName", detectionsDocName); DBQueryResult queryResult; try { diff --git a/src/tethys/dbxml/TethysQueryException.java b/src/tethys/dbxml/TethysQueryException.java index bb3dd741..7f46d7bc 100644 --- a/src/tethys/dbxml/TethysQueryException.java +++ b/src/tethys/dbxml/TethysQueryException.java @@ -2,6 +2,9 @@ package tethys.dbxml; public class TethysQueryException extends TethysException { + + private static final long serialVersionUID = 1L; + private String queryString; public TethysQueryException(String message, String queryString) { diff --git a/src/warnings/WarningSystem.java b/src/warnings/WarningSystem.java index aa5cab96..14a024f9 100644 --- a/src/warnings/WarningSystem.java +++ b/src/warnings/WarningSystem.java @@ -149,7 +149,7 @@ public class WarningSystem { * Remove old warnings. */ private synchronized void removeOldWarnings() { - long now = PamCalendar.getTimeInMillis(); + long now = System.currentTimeMillis(); ListIterator it = warnings.listIterator(); int removals = 0; while (it.hasNext()) { From 3df05c3ec61298b5058dcb05c0cb6a4aba0d8815 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 29 May 2023 15:27:21 +0100 Subject: [PATCH 36/65] Document export --- src/PamController/PamController.java | 2 +- src/tethys/TethysControl.java | 76 +++++++++++++++++++ src/tethys/dbxml/DBXMLConnect.java | 40 +++++++++- src/tethys/dbxml/DBXMLQueries.java | 31 ++++++++ src/tethys/detection/DetectionsHandler.java | 73 ++++++++++++++++++ src/tethys/niluswraps/TethysCollections.java | 13 ++++ src/tethys/pamdata/AutoTethysProvider.java | 7 +- src/tethys/pamdata/TethysDataProvider.java | 4 + .../swing/PAMGuardDeploymentsTable.java | 28 ++++++- src/tethys/swing/XMLStringView.java | 54 +++++++++++++ 10 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 src/tethys/niluswraps/TethysCollections.java create mode 100644 src/tethys/swing/XMLStringView.java diff --git a/src/PamController/PamController.java b/src/PamController/PamController.java index 2b54ec35..d63fa262 100644 --- a/src/PamController/PamController.java +++ b/src/PamController/PamController.java @@ -2598,7 +2598,7 @@ public class PamController implements PamControllerInterface, PamSettings { if (dbc == null) { return null; } - return dbc.getDatabaseName(); + return dbc.getLongDatabaseName(); } return null; } diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 2fa785b1..8722cecb 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -3,6 +3,10 @@ package tethys; import java.awt.Desktop; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; import java.io.Serializable; import java.net.MalformedURLException; @@ -10,9 +14,11 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; +import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.Timer; @@ -20,8 +26,11 @@ import PamController.PamControlledUnit; import PamController.PamControlledUnitSettings; import PamController.PamController; import PamController.PamControllerInterface; +import PamController.PamFolders; import PamController.PamSettingManager; import PamController.PamSettings; +import PamUtils.PamFileChooser; +import PamUtils.PamFileFilter; import PamView.PamTabPanel; import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; @@ -43,6 +52,7 @@ import tethys.output.TethysExporter; import tethys.output.swing.TethysExportDialog; import tethys.swing.ProjectDeploymentsDialog; import tethys.swing.TethysTabPanel; +import tethys.swing.XMLStringView; /** * Quick play with a simple system for outputting data to Tethys. At it's start @@ -539,4 +549,70 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet WarnOnce.showWarning(title, msg, WarnOnce.WARNING_MESSAGE); } + /** + * Load a document from the database and display it in a popup window + * @param collection + * @param documentId + */ + public void displayDocument(String collection, String documentId) { + String doc = getDbxmlQueries().getDocument(collection, documentId); + if (doc == null | doc.length() == 0) { + doc = String.format("Unable to retrieve document %s/%s from database\n", collection, documentId); + + } + XMLStringView.showDialog(getGuiFrame(), collection, documentId, doc); + } + + /** + * Load a document from the database and write to a file selected by the user + * @param collection + * @param documentId + */ + public void exportDocument(String collection, String documentId) { + String doc = getDbxmlQueries().getDocument(collection, documentId); + if (doc == null | doc.length() == 0) { + String msg = String.format("Unable to retrieve document %s/%s from database\n", collection, documentId); + WarnOnce.showWarning("Error", msg, WarnOnce.WARNING_MESSAGE); + } + + PamFileFilter fileFilter = new PamFileFilter("XML documents", ".xml"); +// fileFilter + JFileChooser fileChooser = new PamFileChooser(); + fileChooser.setFileFilter(fileFilter); + fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); + // make a default name based on the document id and the dataset directory. + String defFolder = PamFolders.getDefaultProjectFolder(); + if (defFolder != null) { + defFolder = String.format("%s%s%s.xml", defFolder,File.separator,documentId); + File defFile = new File(defFolder); + fileChooser.setSelectedFile(defFile); + fileChooser.setAcceptAllFileFilterUsed(true); + + } + int state = fileChooser.showSaveDialog(getGuiFrame()); + if (state != JFileChooser.APPROVE_OPTION) return; + File newFile = fileChooser.getSelectedFile(); + if (newFile == null) return; + newFile = PamFileFilter.checkFileEnd(newFile, "xml", true); + if (newFile == null) { + return; + } + if (newFile.exists()) { + int ans2 = WarnOnce.showWarning(newFile.getAbsolutePath(), + "The file already exists. Do you want to overwrite it ?", WarnOnce.OK_CANCEL_OPTION); + if (ans2 == WarnOnce.CANCEL_OPTION) { + return; + } + } + try { + BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(newFile)); + bos.write(doc.getBytes()); + bos.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index 26ed2b2e..eb79d670 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -85,6 +85,44 @@ public class DBXMLConnect { return queries; } + /** + * Convert a nilus Object into a file + * @param nilusObject nilus object + * @param file file (should not exist) + * @return file (will be the same as input file) + * @throws TethysException + */ + public File createXMLDocument(Object nilusObject, File file) throws TethysException { + Class objClass = nilusObject.getClass(); + try { + MarshalXML marshal = new MarshalXML(); + marshal.createInstance(objClass); + marshal.marshal(nilusObject, file.toString()); + } catch(IllegalArgumentException e) { + throw new TethysException("IllegalArgumentException posting to Tethys: " + e.getMessage(), null); + } catch (IOException e) { + throw new TethysException("IOException posting to Tethys: " + e.getMessage(), null); + } catch (JAXBException e) { + throw new TethysException("JAXBException posting to Tethys: " + e.getMessage(), null); + } + return file; + } + + /** + * Create a temporary nilus file. + * @param nilusObject + * @return + * @throws TethysException + */ + public File createTempXMLDocument(Object nilusObject) throws TethysException { + String tempName = getTempFileName(nilusObject); + tempName = tempDirectory.getAbsolutePath() + File.separator + tempName + ".xml"; + File tempFile = new File(tempName); + File retFile = createXMLDocument(nilusObject, tempFile); + retFile.deleteOnExit(); + return retFile; + } + /** * take a nilus object loaded with PamGuard data and post it to the Tethys database * @@ -162,7 +200,7 @@ public class DBXMLConnect { String docId = getDocumentId(nilusDocument); String result = null; try { - result = jerseyClient.removeDocument(collection+" uio", docId ); + result = jerseyClient.removeDocument(collection, docId ); /** * Return from a sucessful delete is something like * diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 8529a286..0bf67e9a 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -440,6 +440,37 @@ public class DBXMLQueries { return count; } + public String getDocument(String collection, String documentId) { +// String queryBase = "return:(collection(\"replaceCollectionName\")/Detections[Id=\"ReplaceDocumentId\"])"; +// queryBase = queryBase.replace("replaceCollectionName", collection); +// queryBase = queryBase.replace("ReplaceDocumentId", documentId); +// +// String result = null; +// try { +// Queries queries = dbXMLConnect.getTethysQueries(); +// result = queries.QueryTethys(queryBase); +//// System.out.println(result); +// } +// catch (Exception e) { +// System.out.println("Error executing " + queryBase); +//// e.printStackTrace(); +// return null; +// } +// return result; + + Queries queries = dbXMLConnect.getTethysQueries(); + String result = null; + try { + result = queries.getDocument(collection, documentId); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return result; + +// String queryBase = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; + } + /** * Count on effort detections in a Detections document * @param docName diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 677addc0..706dd7fc 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -5,11 +5,20 @@ import java.util.ArrayList; import java.util.List; import javax.swing.SwingWorker; +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBException; +import javax.xml.parsers.ParserConfigurationException; + +import org.pamguard.x3.sud.SUDClickDetectorInfo; +import org.w3c.dom.Document; +import org.w3c.dom.Element; import PamController.PamControlledUnit; import PamController.PamguardVersionInfo; +import PamController.settings.output.xml.PamguardXMLWriter; import PamModel.PamPluginInterface; import PamUtils.PamCalendar; +import PamUtils.XMLUtils; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamProcess; @@ -18,6 +27,7 @@ import dataMap.OfflineDataMap; import dataMap.OfflineDataMapPoint; import metadata.deployment.DeploymentData; import nilus.AlgorithmType; +import nilus.AlgorithmType.Parameters; import nilus.AlgorithmType.SupportSoftware; import nilus.DataSourceType; import nilus.Deployment; @@ -484,6 +494,69 @@ public class DetectionsHandler { algorithm.setMethod(getMethodString(dataBlock)); algorithm.setSoftware(getSoftwareString(dataBlock)); algorithm.setVersion(getVersionString(dataBlock)); + + TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); + if (dataProvider != null) { +// Parameters parameters = dataProvider.getAlgorithmParameters(); + Parameters parameters = algorithm.getParameters(); + if (parameters == null) { + parameters = new Parameters(); + algorithm.setParameters(parameters); + } + List paramList = parameters.getAny(); +// algorithm.setParameters(parameters); + // make a really simple parameter or two to see if it works with simpler xml than PG generates. + /** + * Parameters should look something like + * + + Analyst detections + Triton + unknown + + 0.75 + 0.0 + 5000.0 + 30.0 + + + */ + // this works. Can look at the source to see how it's done. + // may have fun making this work for more complex structures. + try { + Helper helper = new Helper(); + helper.AddAnyElement(paramList, "Threshold", "3.5"); + /* + * and see Matlab code for dbStruct2DOM for more complex structures + * This looks like it may be possible to rewrite my functions for + * writing structures to XML using the helper.AddAnyElement function as + * an example and I should be able to output my complex structures. + */ + } catch (JAXBException | ParserConfigurationException e) { + e.printStackTrace(); + } + + Document doc = XMLUtils.createBlankDoc(); +// PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + Element dummyEl = doc.createElement("SomeParam"); +//// dummyEl.setNodeValue("nothing"); + dummyEl.setTextContent("3.0"); + /* + * xsl:stylesheet version=\"1.0\" \n" + + " xmlns:xsl=http://www.w3.org/1999/XSL/Transform\n" + + " xmlns:ns0=http://mydata.com/H2H/Automation\n" + */ + dummyEl.setAttribute("xmlns:ns0", TethysControl.xmlNameSpace); +// dummyEl.set +// paramList.add(dummyEl); + +// Element mainEl = doc.createElement("CONFIG"); +// mainEl.appendChild(dummyEl); +// doc.appendChild(mainEl); +// System.out.println(pamXMLWriter.getAsString(doc)); + } + + List supSoft = algorithm.getSupportSoftware(); SupportSoftware supportSoft = new SupportSoftware(); supportSoft.setSoftware(getSupportSoftware(dataBlock)); diff --git a/src/tethys/niluswraps/TethysCollections.java b/src/tethys/niluswraps/TethysCollections.java new file mode 100644 index 00000000..7c321cac --- /dev/null +++ b/src/tethys/niluswraps/TethysCollections.java @@ -0,0 +1,13 @@ +package tethys.niluswraps; + +public enum TethysCollections { + + Deployments, Detections, Localizations, Calibrations, SpeciesAbreviations; + + @Override + public String toString() { + return super.toString(); + } + + +} diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 0be8daab..ebe498fa 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -112,7 +112,8 @@ public class AutoTethysProvider implements TethysDataProvider { return algorithm; } - private Parameters getAlgorithmParameters() { + @Override + public Parameters getAlgorithmParameters() { if (pamControlledUnit instanceof PamSettings == false) { return null; } @@ -127,13 +128,13 @@ public class AutoTethysProvider implements TethysDataProvider { if (settingsObjs == null) { return null; } - // pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); +// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); if (settingsEl == null) { return null; } - settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); +// settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); dummyEl.appendChild(settingsEl); diff --git a/src/tethys/pamdata/TethysDataProvider.java b/src/tethys/pamdata/TethysDataProvider.java index e844675a..1a89c505 100644 --- a/src/tethys/pamdata/TethysDataProvider.java +++ b/src/tethys/pamdata/TethysDataProvider.java @@ -2,6 +2,7 @@ package tethys.pamdata; import PamguardMVC.PamDataUnit; import nilus.AlgorithmType; +import nilus.AlgorithmType.Parameters; import nilus.Deployment; import nilus.DescriptionType; import nilus.Detection; @@ -62,5 +63,8 @@ public interface TethysDataProvider { */ public Detection createDetection(PamDataUnit dataUnit, TethysExportParams tethysExportParams, StreamExportParams streamExportParams); + + + public Parameters getAlgorithmParameters(); } diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 7ec8459d..d17a659d 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -33,6 +33,7 @@ import tethys.deployment.DeploymentHandler; import tethys.deployment.DeploymentOverview; import tethys.deployment.RecordingPeriod; import tethys.niluswraps.PDeployment; +import tethys.niluswraps.TethysCollections; /** * Table view of PAMGuard deployments. For a really simple deployment, this may have only @@ -135,13 +136,30 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { JPopupMenu popMenu = new JPopupMenu(); JMenuItem menuItem = new JMenuItem("Remove deployment document " + matchedDeployments.get(0)); menuItem.addActionListener(new ActionListener() { - @Override public void actionPerformed(ActionEvent e) { deleteDeployment(matchedDeployments.get(0)); } }); popMenu.add(menuItem); + menuItem = new JMenuItem("Display deployment document " + matchedDeployments.get(0)); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + displayDeployment(matchedDeployments.get(0)); + } + }); + popMenu.add(menuItem); + menuItem = new JMenuItem("Export deployment document " + matchedDeployments.get(0)); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportDeployment(matchedDeployments.get(0)); + } + }); + popMenu.add(menuItem); + + popMenu.show(e.getComponent(), e.getX(), e.getY()); } // if (newPeriods.size() == 0) { @@ -155,6 +173,14 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } + protected void exportDeployment(PDeployment pDeployment) { + getTethysControl().exportDocument(TethysCollections.Deployments.toString(), pDeployment.deployment.getId()); + } + + protected void displayDeployment(PDeployment pDeployment) { + getTethysControl().displayDocument(TethysCollections.Deployments.toString(), pDeployment.deployment.getId()); + } + protected void deleteDeployment(PDeployment pDeployment) { Deployment dep = pDeployment.deployment; if (dep == null) { diff --git a/src/tethys/swing/XMLStringView.java b/src/tethys/swing/XMLStringView.java new file mode 100644 index 00000000..0d871c86 --- /dev/null +++ b/src/tethys/swing/XMLStringView.java @@ -0,0 +1,54 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.awt.Window; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +import PamView.dialog.PamDialog; + +public class XMLStringView extends PamDialog { + + private JTextArea textArea; + + private XMLStringView(Window parentFrame, String title, String xmlString) { + super(parentFrame, title, false); + + JTextArea textArea = new JTextArea(50, 100); + JPanel mainPanel = new JPanel(new BorderLayout()); + JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + mainPanel.add(scrollPane, BorderLayout.CENTER); + setDialogComponent(mainPanel); + setResizable(true); + textArea.setText(xmlString); + + getCancelButton().setVisible(false); + } + + public static void showDialog(Window parent, String collection, String documentId, String xmlString) { + String title = String.format("\"%s\"/\"%s\"", collection, documentId); + XMLStringView view = new XMLStringView(parent, title, xmlString); + view.setVisible(true); + } + + @Override + public boolean getParams() { + // TODO Auto-generated method stub + return false; + } + + @Override + public void cancelButtonPressed() { + // TODO Auto-generated method stub + + } + + @Override + public void restoreDefaultSettings() { + // TODO Auto-generated method stub + + } + +} From f3944e35361842672c7d35148cf76b2a45a1cf86 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 9 Jun 2023 12:56:23 +0100 Subject: [PATCH 37/65] Update FolderInputSystem.java --- src/Acquisition/FolderInputSystem.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index 9acc836c..c34a2cf9 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -314,6 +314,9 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D } selection = folderInputParameters.getSelectedFiles(); } + if (selection == null) { + return 0; + } if (selection.length > 0) { System.out.println("FolderInputSystem.makeSelFileList(): Searching for sound files in " + selection[0]); } From cd70026cf8c6fd13c9a9ad3d031c47c007a072ef Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 3 Jul 2023 17:53:35 +0100 Subject: [PATCH 38/65] Starting to map species codes --- src/PamguardMVC/PamDataBlock.java | 10 ++ src/tethys/dbxml/DBXMLQueries.java | 1 - src/tethys/pamdata/TethysDataProvider.java | 1 + src/tethys/species/DatablockSpeciesMap.java | 12 +++ src/tethys/species/SpeciesMapItem.java | 19 ++++ src/tethys/species/SpeciesMapManager.java | 5 + src/tethys/species/SpeciesTest.java | 110 ++++++++++++++++++++ src/tethys/species/SpeciesTypes.java | 20 ++++ src/tethys/species/TethysITISResult.java | 37 +++++++ 9 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 src/tethys/species/DatablockSpeciesMap.java create mode 100644 src/tethys/species/SpeciesMapItem.java create mode 100644 src/tethys/species/SpeciesMapManager.java create mode 100644 src/tethys/species/SpeciesTest.java create mode 100644 src/tethys/species/SpeciesTypes.java create mode 100644 src/tethys/species/TethysITISResult.java diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index 09f5c94d..d22a8543 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -3102,6 +3102,16 @@ public class PamDataBlock extends PamObservable { public void setTethysDataProvider(TethysDataProvider tethysDataProvider) { this.tethysDataProvider = tethysDataProvider; } + + + /** + * Get information about species types that may occur within this data + * block. + * @return Types of species information available within this datablock. + */ + public tethys.species.SpeciesTypes getSpeciesTypes() { + return null; + } final public boolean getCanLog() { return (logging != null); diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 0bf67e9a..6d2e9c47 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -129,7 +129,6 @@ public class DBXMLQueries { String queryResult = null; String schemaPlan = null; - TethysExportParams params = tethysControl.getTethysExportParams(); try { JerseyClient jerseyClient = dbxmlConnect.getJerseyClient(); diff --git a/src/tethys/pamdata/TethysDataProvider.java b/src/tethys/pamdata/TethysDataProvider.java index 1a89c505..efbd47e5 100644 --- a/src/tethys/pamdata/TethysDataProvider.java +++ b/src/tethys/pamdata/TethysDataProvider.java @@ -67,4 +67,5 @@ public interface TethysDataProvider { public Parameters getAlgorithmParameters(); + } diff --git a/src/tethys/species/DatablockSpeciesMap.java b/src/tethys/species/DatablockSpeciesMap.java new file mode 100644 index 00000000..07239ab5 --- /dev/null +++ b/src/tethys/species/DatablockSpeciesMap.java @@ -0,0 +1,12 @@ +package tethys.species; + +import java.io.Serializable; + +/** + * Species map for a specified data block + * @author dg50 + * + */ +public class DatablockSpeciesMap implements Serializable { + +} diff --git a/src/tethys/species/SpeciesMapItem.java b/src/tethys/species/SpeciesMapItem.java new file mode 100644 index 00000000..d586ddd1 --- /dev/null +++ b/src/tethys/species/SpeciesMapItem.java @@ -0,0 +1,19 @@ +package tethys.species; + +import java.io.Serializable; + +public class SpeciesMapItem implements Serializable, Cloneable { + + public static final long serialVersionUID = 1L; + + private int itisCode; + + private String pamguardName; + + private String latinName; + + private String commonName; + + private String callType; + +} diff --git a/src/tethys/species/SpeciesMapManager.java b/src/tethys/species/SpeciesMapManager.java new file mode 100644 index 00000000..26d8eda4 --- /dev/null +++ b/src/tethys/species/SpeciesMapManager.java @@ -0,0 +1,5 @@ +package tethys.species; + +public class SpeciesMapManager { + +} diff --git a/src/tethys/species/SpeciesTest.java b/src/tethys/species/SpeciesTest.java new file mode 100644 index 00000000..804abb6c --- /dev/null +++ b/src/tethys/species/SpeciesTest.java @@ -0,0 +1,110 @@ +package tethys.species; + +import dbxml.JerseyClient; +import dbxml.Queries; +import tethys.dbxml.DBQueryResult; +import tethys.dbxml.TethysQueryException; + +public class SpeciesTest extends SpeciesTypes { + + String uri = "http://localhost:9779"; + public static void main(String[] args) { + + SpeciesTest st = new SpeciesTest(); + st.runJson(); + +// int spermWhale = 180488; +// st.getCodeInfo(spermWhale); +// st.runXQuery(); + + } + private void getCodeInfo(int itisCode) { + System.out.println("Running getCodeInfo()"); + String jQBase = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"SPECIESTSN\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String jQ = jQBase.replace("SPECIESTSN", String.format("%d", itisCode)); + + + DBQueryResult result = null; + String queryResult = null; + String schemaPlan = null; + JerseyClient jerseyClient = new JerseyClient(uri); + long t1 = System.nanoTime(); + try { + queryResult = jerseyClient.queryJSON(jQ, 0); +// schemaPlan = jerseyClient.queryJSON(jQ, 1); + } catch (Exception e1) { + e1.printStackTrace(); + + } + long t2 = System.nanoTime(); + System.out.printf("Query time was %3.1fms\n" , (double) (t2-t1)/1e6); + System.out.println(queryResult); + + TethysITISResult itisResult = new TethysITISResult(queryResult); + } + /* + * + + + -10 + Other phenomena + + Other + Autre + Otro + + + + 555654 + Delphinus capensis + + Long-beaked Common Dolphin + + + */ + + private void runXQuery() { + System.out.println("Running runXQuery()"); + String queryBase = "count(collection(\"Detections\")/Detections[Id=\"ReplaceDocumentId\"]/OnEffort/Detection)"; + String xQ = "collection(\"ITIS_ranks\")/ty:ranks/ty:rank[dbxml:contains(ty:completename, \"Physeter\")]"; + + JerseyClient jerseyClient = new JerseyClient(uri); + Queries queries = new Queries(jerseyClient); + + String result = null; + try { + result = queries.QueryTethys(xQ); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + System.out.println(result); + + } + private void runJson() { +// String jQ = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; +// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"624908\"],\"optype\":\"binary\"}],\"enclose\":1}"; +// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/completename\",\"Mesoplodon\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"dbxml:contains\",\"operands\":[\"ranks/rank/completename\",\"Mesoplodon\"],\"optype\":\"function\"}],\"enclose\":1}"; + + System.out.println(jQ); + + DBQueryResult result = null; + String queryResult = null; + String schemaPlan = null; + JerseyClient jerseyClient = new JerseyClient(uri); + long t1 = System.nanoTime(); + try { + queryResult = jerseyClient.queryJSON(jQ, 0); +// schemaPlan = jerseyClient.queryJSON(jQ, 1); + } catch (Exception e1) { + System.out.println("epic fail"); + e1.printStackTrace(); + } + long t2 = System.nanoTime(); + System.out.printf("Query time was %3.1fms\n" , (double) (t2-t1)/1e6); + System.out.println(queryResult); + + } + +} diff --git a/src/tethys/species/SpeciesTypes.java b/src/tethys/species/SpeciesTypes.java new file mode 100644 index 00000000..84ca0152 --- /dev/null +++ b/src/tethys/species/SpeciesTypes.java @@ -0,0 +1,20 @@ +package tethys.species; + +import java.util.ArrayList; + +/** + * Class to return lists of species codes or names for a datablock. + * This information will then get incorporated into a more complicated translation table to + * provide PAMGuard data on it's way to Tethys with more rigid species code definitions. + * @author dg50 + * + */ +public class SpeciesTypes { + + + /** + * List of species names / codes associated with this data block. + */ + private ArrayList speciesNames; + +} diff --git a/src/tethys/species/TethysITISResult.java b/src/tethys/species/TethysITISResult.java new file mode 100644 index 00000000..9504d196 --- /dev/null +++ b/src/tethys/species/TethysITISResult.java @@ -0,0 +1,37 @@ +package tethys.species; + +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +/** + * Class to hold and unpack a XML string returned from the ITIS_ranks document + * in a Tethys database. + * @author dg50 + * + */ +public class TethysITISResult { + + /** + * Construct a ITIS object from XML data + * @param xmlData + */ + public TethysITISResult(String xmlData) { + + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + //API to obtain DOM Document instance + DocumentBuilder builder = null; + +// //Create DocumentBuilder with default configuration +// builder = factory.newDocumentBuilder(); +// +// //Parse the content to Document object +// Document doc = builder.parse(new InputSource(new StringReader(xmlData))); + + } +} From 5c969d888975a4f2f79c9011f25ef6bfd5352c01 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:56:39 +0100 Subject: [PATCH 39/65] work on species mapping --- src/PamguardMVC/PamDataBlock.java | 7 +- .../ClickBlockSpeciesManager.java | 35 +++++++ src/clickDetector/ClickDataBlock.java | 12 +++ .../species/DataBlockSpeciesManager.java | 58 +++++++++++ src/tethys/species/DataBlockSpeciesMap.java | 37 ++++++++ src/tethys/species/DataBlockSpeciesTypes.java | 43 +++++++++ src/tethys/species/DatablockSpeciesMap.java | 12 --- src/tethys/species/SpeciesMapItem.java | 83 ++++++++++++++++ src/tethys/species/SpeciesTest.java | 2 +- src/tethys/species/SpeciesTypes.java | 20 ---- .../species/swing/DataBlockSpeciesDialog.java | 47 +++++++++ .../species/swing/DataBlockSpeciesPanel.java | 65 +++++++++++++ src/tethys/species/swing/SpeciesSubPanel.java | 95 +++++++++++++++++++ src/tethys/swing/DatablockSynchPanel.java | 53 ++++++++++- 14 files changed, 530 insertions(+), 39 deletions(-) create mode 100644 src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java create mode 100644 src/tethys/species/DataBlockSpeciesManager.java create mode 100644 src/tethys/species/DataBlockSpeciesMap.java create mode 100644 src/tethys/species/DataBlockSpeciesTypes.java delete mode 100644 src/tethys/species/DatablockSpeciesMap.java delete mode 100644 src/tethys/species/SpeciesTypes.java create mode 100644 src/tethys/species/swing/DataBlockSpeciesDialog.java create mode 100644 src/tethys/species/swing/DataBlockSpeciesPanel.java create mode 100644 src/tethys/species/swing/SpeciesSubPanel.java diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index d22a8543..c3a3ba7b 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -52,6 +52,7 @@ import Acquisition.AcquisitionProcess; import pamScrollSystem.ViewLoadObserver; import tethys.pamdata.AutoTethysProvider; import tethys.pamdata.TethysDataProvider; +import tethys.species.DataBlockSpeciesManager; import dataGram.DatagramProvider; import dataMap.BespokeDataMapGraphic; import dataMap.OfflineDataMap; @@ -3103,13 +3104,13 @@ public class PamDataBlock extends PamObservable { this.tethysDataProvider = tethysDataProvider; } - /** * Get information about species types that may occur within this data - * block. + * block. Primarily for conversion into Tethys compatible data, but may + * prove to have other uses. * @return Types of species information available within this datablock. */ - public tethys.species.SpeciesTypes getSpeciesTypes() { + public DataBlockSpeciesManager getDatablockSpeciesManager() { return null; } diff --git a/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java b/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java new file mode 100644 index 00000000..971fe139 --- /dev/null +++ b/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java @@ -0,0 +1,35 @@ +package clickDetector.ClickClassifiers; + +import clickDetector.ClickControl; +import clickDetector.ClickDataBlock; +import clickDetector.ClickDetection; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.DataBlockSpeciesTypes; + +public class ClickBlockSpeciesManager extends DataBlockSpeciesManager { + + private ClickControl clickControl; + + public ClickBlockSpeciesManager(ClickControl clickControl, ClickDataBlock clickDataBlock) { + super(clickDataBlock); + this.clickControl = clickControl; + } + + @Override + public DataBlockSpeciesTypes getSpeciesTypes() { + ClickTypeMasterManager masterManager = clickControl.getClickTypeMasterManager(); + if (masterManager == null) { + return null; + } + String[] speciesList = masterManager.getSpeciesList(); + + return new DataBlockSpeciesTypes(speciesList); + } + + @Override + public String getSpeciesString(ClickDetection dataUnit) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/clickDetector/ClickDataBlock.java b/src/clickDetector/ClickDataBlock.java index 3e650b85..e00c42ce 100644 --- a/src/clickDetector/ClickDataBlock.java +++ b/src/clickDetector/ClickDataBlock.java @@ -3,6 +3,7 @@ package clickDetector; import java.util.ListIterator; import pamScrollSystem.ViewLoadObserver; +import tethys.species.DataBlockSpeciesManager; //import staticLocaliser.StaticLocaliserControl; //import staticLocaliser.StaticLocaliserProvider; //import staticLocaliser.panels.AbstractLocaliserControl; @@ -10,6 +11,7 @@ import pamScrollSystem.ViewLoadObserver; import alarm.AlarmCounterProvider; import alarm.AlarmDataSource; import binaryFileStorage.BinaryStore; +import clickDetector.ClickClassifiers.ClickBlockSpeciesManager; import clickDetector.dataSelector.ClickDataSelectCreator; import clickDetector.offlineFuncs.OfflineClickLogging; import clickDetector.toad.ClickTOADCalculator; @@ -41,6 +43,8 @@ public class ClickDataBlock extends AcousticDataBlock implement private boolean isViewer; + private ClickBlockSpeciesManager clickBlockSpeciesManager; + public ClickDataBlock(ClickControl clickControl, PamProcess parentProcess, int channelMap) { @@ -304,5 +308,13 @@ public class ClickDataBlock extends AcousticDataBlock implement } } + @Override + public DataBlockSpeciesManager getDatablockSpeciesManager() { + if (clickBlockSpeciesManager == null) { + clickBlockSpeciesManager = new ClickBlockSpeciesManager(clickControl, this); + } + return clickBlockSpeciesManager; + } + } diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java new file mode 100644 index 00000000..b44a23f1 --- /dev/null +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -0,0 +1,58 @@ +package tethys.species; + +import PamController.PamController; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import tethys.species.swing.DataBlockSpeciesDialog; + +/** + * Manage species conversion for a single datablock. + * @author dg50 + * + */ +abstract public class DataBlockSpeciesManager { + + private DataBlockSpeciesMap datablockSpeciesMap; + + private PamDataBlock dataBlock; + + public abstract DataBlockSpeciesTypes getSpeciesTypes(); + + public abstract String getSpeciesString(T dataUnit); + + public DataBlockSpeciesManager(PamDataBlock dataBlock) { + super(); + this.dataBlock = dataBlock; + } + + public SpeciesMapItem getSpeciesItem(T dataUnit) { + String speciesString = getSpeciesString(dataUnit); + if (speciesString == null) { + return null; + } + DataBlockSpeciesMap speciesMap = getDatablockSpeciesMap(); + if (speciesMap == null) { + return null; + } + return speciesMap.getItem(speciesString); + } + + public DataBlockSpeciesMap getDatablockSpeciesMap() { + return datablockSpeciesMap; + } + + public void setDatablockSpeciesMap(DataBlockSpeciesMap datablockSpeciesMap) { + this.datablockSpeciesMap = datablockSpeciesMap; + } + + public void showSpeciesDialog() { + DataBlockSpeciesDialog.showDialog(PamController.getMainFrame(), dataBlock); + } + + /** + * @return the dataBlock + */ + public PamDataBlock getDataBlock() { + return dataBlock; + } +} diff --git a/src/tethys/species/DataBlockSpeciesMap.java b/src/tethys/species/DataBlockSpeciesMap.java new file mode 100644 index 00000000..3827d20a --- /dev/null +++ b/src/tethys/species/DataBlockSpeciesMap.java @@ -0,0 +1,37 @@ +package tethys.species; + +import java.io.Serializable; +import java.util.HashMap; + +/** + * Species map for a specified data block + * @author dg50 + * + */ +public class DataBlockSpeciesMap implements Serializable { + + public static final long serialVersionUID = 1L; + + private HashMap speciesTable = new HashMap<>(); + + protected HashMap getSpeciesTable() { + return speciesTable; + } + + protected void setSpeciesTable(HashMap speciesTable) { + this.speciesTable = speciesTable; + } + + public void putItem(String key, SpeciesMapItem speciesMapItem) { + speciesTable.put(key, speciesMapItem); + } + + public SpeciesMapItem getItem(String key) { + return speciesTable.get(key); + } + + public void removeItem(String key) { + speciesTable.remove(key); + } + +} diff --git a/src/tethys/species/DataBlockSpeciesTypes.java b/src/tethys/species/DataBlockSpeciesTypes.java new file mode 100644 index 00000000..6b9a4496 --- /dev/null +++ b/src/tethys/species/DataBlockSpeciesTypes.java @@ -0,0 +1,43 @@ +package tethys.species; + +import java.util.ArrayList; + +/** + * Class to return lists of species codes or names for a datablock. + * This information will then get incorporated into a more complicated translation table to + * provide PAMGuard data on it's way to Tethys with more rigid species code definitions. + * @author dg50 + * + */ +public class DataBlockSpeciesTypes { + + /** + * List of species names / codes associated with this data block. + */ + private ArrayList speciesNames; + + /** + * constructor to use with a array of String names. + * @param speciesList + */ + public DataBlockSpeciesTypes(String[] speciesList) { + if (speciesList == null) { + speciesNames = new ArrayList<>(); + } + else { + speciesNames = new ArrayList(speciesList.length); + for (int i = 0; i < speciesList.length; i++) { + speciesNames.add(speciesList[i]); + } + } + } + + /** + * @return the speciesNames + */ + public ArrayList getSpeciesNames() { + return speciesNames; + } + + +} diff --git a/src/tethys/species/DatablockSpeciesMap.java b/src/tethys/species/DatablockSpeciesMap.java deleted file mode 100644 index 07239ab5..00000000 --- a/src/tethys/species/DatablockSpeciesMap.java +++ /dev/null @@ -1,12 +0,0 @@ -package tethys.species; - -import java.io.Serializable; - -/** - * Species map for a specified data block - * @author dg50 - * - */ -public class DatablockSpeciesMap implements Serializable { - -} diff --git a/src/tethys/species/SpeciesMapItem.java b/src/tethys/species/SpeciesMapItem.java index d586ddd1..f1595a7a 100644 --- a/src/tethys/species/SpeciesMapItem.java +++ b/src/tethys/species/SpeciesMapItem.java @@ -2,18 +2,101 @@ package tethys.species; import java.io.Serializable; +/** + * Information linking a name within a PAMGuard datablock to a ITIS species code and call type. + * Also contains common and latin names for display purposes, though these are not essential. + * @author dg50 + * + */ public class SpeciesMapItem implements Serializable, Cloneable { public static final long serialVersionUID = 1L; + /** + * ITIS code. May be a real ITIS code or one of the -ve species that we're defining ourselves. + */ private int itisCode; + /** + * Species code that was used within PAMGuard. Redundant information, but useful + * for bookkeeping. + */ private String pamguardName; + /** + * Latin name extracted from ITIS database. Can be null if unknown. + * Probably not written to Tethys. + */ private String latinName; + /** + * Common species name from ITIS database. Can be null if unknown. + * Probably not written to Tethys. + */ private String commonName; + /** + * Type of call. Descriptive name for type of sound, e.g. 'click', 'whistle', 'D-call', + * etc. to complement the itis species code. Will be written to Tethys. + */ private String callType; + + public SpeciesMapItem(int itisCode, String callType, String pamguardName, String latinName, String commonName) { + super(); + this.itisCode = itisCode; + this.callType = callType; + this.pamguardName = pamguardName; + this.latinName = latinName; + this.commonName = commonName; + } + + public SpeciesMapItem(int itisCode, String callType, String pamguardName) { + super(); + this.itisCode = itisCode; + this.callType = callType; + this.pamguardName = pamguardName; + this.latinName = null; + this.commonName = null; + } + + /** + * ITIS code. May be a real ITIS code or one of the -ve species that we're defining ourselves. + * @return the itisCode + */ + public int getItisCode() { + return itisCode; + } + + /** + * Species code that was used within PAMGuard. Redundant information, but useful + * @return the pamguardName + */ + public String getPamguardName() { + return pamguardName; + } + + /** + * Latin name extracted from ITIS database. Can be null if unknown. + * @return the latinName + */ + public String getLatinName() { + return latinName; + } + + /** + * Common species name from ITIS database. Can be null if unknown. + * @return the commonName + */ + public String getCommonName() { + return commonName; + } + + /** + * Type of call. Descriptive name for type of sound, e.g. 'click', 'whistle', 'D-call', + * @return the callType + */ + public String getCallType() { + return callType; + } } diff --git a/src/tethys/species/SpeciesTest.java b/src/tethys/species/SpeciesTest.java index 804abb6c..affdff22 100644 --- a/src/tethys/species/SpeciesTest.java +++ b/src/tethys/species/SpeciesTest.java @@ -5,7 +5,7 @@ import dbxml.Queries; import tethys.dbxml.DBQueryResult; import tethys.dbxml.TethysQueryException; -public class SpeciesTest extends SpeciesTypes { +public class SpeciesTest { String uri = "http://localhost:9779"; public static void main(String[] args) { diff --git a/src/tethys/species/SpeciesTypes.java b/src/tethys/species/SpeciesTypes.java deleted file mode 100644 index 84ca0152..00000000 --- a/src/tethys/species/SpeciesTypes.java +++ /dev/null @@ -1,20 +0,0 @@ -package tethys.species; - -import java.util.ArrayList; - -/** - * Class to return lists of species codes or names for a datablock. - * This information will then get incorporated into a more complicated translation table to - * provide PAMGuard data on it's way to Tethys with more rigid species code definitions. - * @author dg50 - * - */ -public class SpeciesTypes { - - - /** - * List of species names / codes associated with this data block. - */ - private ArrayList speciesNames; - -} diff --git a/src/tethys/species/swing/DataBlockSpeciesDialog.java b/src/tethys/species/swing/DataBlockSpeciesDialog.java new file mode 100644 index 00000000..ff29cb16 --- /dev/null +++ b/src/tethys/species/swing/DataBlockSpeciesDialog.java @@ -0,0 +1,47 @@ +package tethys.species.swing; + +import java.awt.Window; + +import PamView.dialog.PamDialog; +import PamguardMVC.PamDataBlock; + +public class DataBlockSpeciesDialog extends PamDialog { + + private static final long serialVersionUID = 1L; + + DataBlockSpeciesPanel speciesPanel; + + private DataBlockSpeciesDialog(Window parentFrame, PamDataBlock dataBlock) { + super(parentFrame, dataBlock.getDataName() + " species", false); + speciesPanel = new DataBlockSpeciesPanel(dataBlock); + setDialogComponent(speciesPanel.getDialogComponent()); + } + + public static void showDialog(Window parentFrame, PamDataBlock dataBlock) { + DataBlockSpeciesDialog speciesDialog = new DataBlockSpeciesDialog(parentFrame, dataBlock); + speciesDialog.setParams(); + speciesDialog.setVisible(true); + } + + private void setParams() { + speciesPanel.setParams(); + } + + @Override + public boolean getParams() { + return speciesPanel.getParams(); + } + + @Override + public void cancelButtonPressed() { + // TODO Auto-generated method stub + + } + + @Override + public void restoreDefaultSettings() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/tethys/species/swing/DataBlockSpeciesPanel.java b/src/tethys/species/swing/DataBlockSpeciesPanel.java new file mode 100644 index 00000000..b90b12bc --- /dev/null +++ b/src/tethys/species/swing/DataBlockSpeciesPanel.java @@ -0,0 +1,65 @@ +package tethys.species.swing; + +import java.awt.BorderLayout; +import java.util.ArrayList; + +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamDialogPanel; +import PamguardMVC.PamDataBlock; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.DataBlockSpeciesMap; +import tethys.species.DataBlockSpeciesTypes; +import tethys.species.SpeciesMapItem; + +public class DataBlockSpeciesPanel implements PamDialogPanel { + + private JPanel mainPanel; + + private PamDataBlock dataBlock; + + private JPanel speciesPanel; + + public DataBlockSpeciesPanel(PamDataBlock dataBlock) { + super(); + this.dataBlock = dataBlock; + mainPanel = new JPanel(new BorderLayout()); + speciesPanel = new JPanel(); + mainPanel.add(speciesPanel, BorderLayout.CENTER); + mainPanel.setBorder(new TitledBorder(dataBlock.getDataName())); + } + + @Override + public JComponent getDialogComponent() { + return mainPanel; + } + + @Override + public void setParams() { + speciesPanel.removeAll(); + speciesPanel.setLayout(new BoxLayout(speciesPanel, BoxLayout.Y_AXIS)); + + DataBlockSpeciesManager speciesManager = dataBlock.getDatablockSpeciesManager(); + DataBlockSpeciesTypes speciesTypes = speciesManager.getSpeciesTypes(); + ArrayList speciesNames = speciesTypes.getSpeciesNames(); + DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); + for (String aSpecies : speciesNames) { + SpeciesSubPanel subPanel = new SpeciesSubPanel(aSpecies); + speciesPanel.add(subPanel.getDialogComponent()); + if (speciesMap != null) { + SpeciesMapItem speciesInfo = speciesMap.getItem(aSpecies); + subPanel.setParams(speciesInfo); + } + } + } + + @Override + public boolean getParams() { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/tethys/species/swing/SpeciesSubPanel.java b/src/tethys/species/swing/SpeciesSubPanel.java new file mode 100644 index 00000000..ab9ea2c3 --- /dev/null +++ b/src/tethys/species/swing/SpeciesSubPanel.java @@ -0,0 +1,95 @@ +package tethys.species.swing; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.BevelBorder; + +import PamView.dialog.PamGridBagContraints; +import tethys.species.SpeciesMapItem; + +public class SpeciesSubPanel { + + private JPanel mainPanel; + + private JLabel pamguardName; + private JTextField itisCode, callType, latinName, commonName; + private JButton searchButton; + + public SpeciesSubPanel(String aSpecies) { + mainPanel = new JPanel(new GridBagLayout()); + mainPanel.setBorder(new BevelBorder(BevelBorder.RAISED)); + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(new JLabel("Name ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(pamguardName = new JLabel(aSpecies), c); + c.gridx++; + mainPanel.add(new JLabel(" ITIS code ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(itisCode = new JTextField(3), c); + c.gridx ++; + mainPanel.add(searchButton = new JButton("Find")); + + int w1 = 2; + int w2 = 3; + c.gridx = 0; + c.gridy++; + c.gridwidth = w1; + mainPanel.add(new JLabel("Call / sound type ", JLabel.RIGHT), c); + c.gridx+= c.gridwidth; + c.gridwidth = w2; + mainPanel.add(callType = new JTextField(15), c); + c.gridx = 0; + c.gridy++; + c.gridwidth = w1; + mainPanel.add(new JLabel("Scientific name ", JLabel.RIGHT), c); + c.gridx+= c.gridwidth; + c.gridwidth = w2; + mainPanel.add(latinName = new JTextField(15), c); + c.gridx = 0; + c.gridy++; + c.gridwidth = w1; + mainPanel.add(new JLabel("Common name ", JLabel.RIGHT), c); + c.gridx+= c.gridwidth; + c.gridwidth = w2; + mainPanel.add(commonName = new JTextField(15), c); + + pamguardName.setToolTipText("Internal name within PAMGuard module"); + itisCode.setToolTipText("ITIS species code"); + searchButton.setToolTipText("Search for species code"); + callType.setToolTipText("Descriptive name for call type or measurement"); + latinName.setToolTipText("Scientific name"); + commonName.setToolTipText("Common name"); + + } + + public JComponent getDialogComponent() { + return mainPanel; + } + + public void setParams(SpeciesMapItem speciesMapItem) { + if (speciesMapItem == null) { + itisCode.setText(null); + callType.setText(null); + latinName.setText(null); + commonName.setText(null); + return; + } + pamguardName.setText(speciesMapItem.getPamguardName()); + itisCode.setText(String.format("%d", speciesMapItem.getItisCode())); + callType.setText(speciesMapItem.getCallType()); + latinName.setText(speciesMapItem.getLatinName()); + commonName.setText(speciesMapItem.getCommonName()); + } + + public SpeciesMapItem getParams() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java index 8da66a90..212eadf2 100644 --- a/src/tethys/swing/DatablockSynchPanel.java +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -1,6 +1,8 @@ package tethys.swing; import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.KeyAdapter; @@ -10,7 +12,9 @@ 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.border.TitledBorder; @@ -25,6 +29,7 @@ import dataMap.OfflineDataMap; import tethys.TethysControl; import tethys.TethysState; import tethys.output.DatablockSynchInfo; +import tethys.species.DataBlockSpeciesManager; public class DatablockSynchPanel extends TethysGUIPanel { @@ -68,21 +73,63 @@ public class DatablockSynchPanel extends TethysGUIPanel { private class MouseActions extends MouseAdapter { @Override public void mouseClicked(MouseEvent e) { - selectRow(); + int row = selectRow(); + if (e.isPopupTrigger() && row >= 0) { + showPopup(e, row); + } + } + + @Override + public void mousePressed(MouseEvent e) { + int row = selectRow(); + if (e.isPopupTrigger() && row >= 0) { + showPopup(e, row); + } + } + + @Override + public void mouseReleased(MouseEvent e) { + int row = selectRow(); + if (e.isPopupTrigger() && row >= 0) { + showPopup(e, row); + } } } - private void selectRow() { + private int selectRow() { int row = synchTable.getSelectedRow(); if (row < 0) { - return; + return row; } DatablockSynchInfo synchInfo = dataBlockSynchInfo.get(row); // datablockDetectionsPanel.setDataBlock(synchInfo.getDataBlock()); notifyObservers(synchInfo.getDataBlock()); + return row; } + public void showPopup(MouseEvent e, int row) { + DatablockSynchInfo synchInfo = dataBlockSynchInfo.get(row); + if (synchInfo == null) { + return; + } + PamDataBlock dataBlock = synchInfo.getDataBlock(); + DataBlockSpeciesManager speciesManager = dataBlock.getDatablockSpeciesManager(); + if (speciesManager == null) { + return; + } + JPopupMenu popMenu = new JPopupMenu(); + JMenuItem menuItem = new JMenuItem("Species info ..."); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + speciesManager.showSpeciesDialog(); + } + }); + popMenu.add(menuItem); + popMenu.show(e.getComponent(), e.getX(), e.getY()); + } + @Override public void updateState(TethysState tethysState) { synchTableModel.fireTableDataChanged(); From 901817689910b6c39dcd3b780615f81527edc961 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 20 Jul 2023 19:06:42 +0100 Subject: [PATCH 40/65] ITIS Codes GUI Basic system in place for handling ITIS species codes. --- src/tethys/TethysControl.java | 13 +++ src/tethys/dbxml/DBXMLQueries.java | 7 +- .../species/DataBlockSpeciesManager.java | 4 + src/tethys/species/GlobalSpeciesMap.java | 32 ++++++ src/tethys/species/ITISFunctions.java | 84 +++++++++++++++ src/tethys/species/SpeciesMapManager.java | 101 +++++++++++++++++- src/tethys/species/SpeciesTest.java | 6 +- src/tethys/species/TethysITISResult.java | 59 ++++++---- .../species/swing/DataBlockSpeciesDialog.java | 26 ++++- .../species/swing/DataBlockSpeciesPanel.java | 19 +++- src/tethys/species/swing/SpeciesSubPanel.java | 66 +++++++++++- 11 files changed, 385 insertions(+), 32 deletions(-) create mode 100644 src/tethys/species/GlobalSpeciesMap.java create mode 100644 src/tethys/species/ITISFunctions.java diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 8722cecb..764612ee 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -50,6 +50,7 @@ import tethys.output.DatablockSynchInfo; import tethys.output.TethysExportParams; import tethys.output.TethysExporter; import tethys.output.swing.TethysExportDialog; +import tethys.species.ITISFunctions; import tethys.swing.ProjectDeploymentsDialog; import tethys.swing.TethysTabPanel; import tethys.swing.XMLStringView; @@ -85,6 +86,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet private DeploymentHandler deploymentHandler; private DetectionsHandler detectionsHandler; + + private ITISFunctions itisFunctions; public TethysControl(String unitName) { super(unitType, unitName); @@ -615,4 +618,14 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } } + /** + * @return the itisFunctions + */ + public ITISFunctions getItisFunctions() { + if (itisFunctions == null) { + itisFunctions = new ITISFunctions(this); + } + return itisFunctions; + } + } diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 6d2e9c47..343826cd 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -61,7 +61,7 @@ public class DBXMLQueries { * @return query result * @throws TethysQueryException */ - private DBQueryResult executeQuery(String jsonQueryString) throws TethysQueryException { + public DBQueryResult executeQuery(String jsonQueryString) throws TethysQueryException { long t1 = System.currentTimeMillis(); DBQueryResult result = null; TethysQueryException tException = null; @@ -636,7 +636,7 @@ public class DBXMLQueries { return blockCounts; } - private String getElementData(Element root, String elName) { + public String getElementData(Element root, String elName) { String[] tree = elName.split("\\."); for (String element : tree) { NodeList nodeList = root.getElementsByTagName(element); @@ -647,12 +647,13 @@ public class DBXMLQueries { Node firstNode = nodeList.item(0); if (firstNode instanceof Element) { root = (Element) firstNode; + break; } } return root.getTextContent(); } - private Document convertStringToXMLDocument(String xmlString) { + public Document convertStringToXMLDocument(String xmlString) { //Parser that produces DOM object trees from XML content DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java index b44a23f1..17f281a3 100644 --- a/src/tethys/species/DataBlockSpeciesManager.java +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -23,6 +23,7 @@ abstract public class DataBlockSpeciesManager { public DataBlockSpeciesManager(PamDataBlock dataBlock) { super(); this.dataBlock = dataBlock; + datablockSpeciesMap = SpeciesMapManager.getInstance().getSpeciesMap(dataBlock); } public SpeciesMapItem getSpeciesItem(T dataUnit) { @@ -38,6 +39,9 @@ abstract public class DataBlockSpeciesManager { } public DataBlockSpeciesMap getDatablockSpeciesMap() { + if (datablockSpeciesMap == null) { + datablockSpeciesMap = new DataBlockSpeciesMap(); + } return datablockSpeciesMap; } diff --git a/src/tethys/species/GlobalSpeciesMap.java b/src/tethys/species/GlobalSpeciesMap.java new file mode 100644 index 00000000..cc8c84f3 --- /dev/null +++ b/src/tethys/species/GlobalSpeciesMap.java @@ -0,0 +1,32 @@ +package tethys.species; + +import java.io.Serializable; +import java.util.HashMap; + +import PamguardMVC.PamDataBlock; + +public class GlobalSpeciesMap implements Serializable { + + public static final long serialVersionUID = 1L; + + private HashMap datablockMaps; + + /** + * @return the datablockMaps + */ + private synchronized HashMap getDatablockMaps() { + if (datablockMaps == null) { + datablockMaps = new HashMap<>(); + } + return datablockMaps; + } + + public void put(PamDataBlock pamDataBlock, DataBlockSpeciesMap dataBlockSpeciesMap) { + getDatablockMaps().put(pamDataBlock.getLongDataName(), dataBlockSpeciesMap); + } + + public DataBlockSpeciesMap get(PamDataBlock pamDataBlock) { + return getDatablockMaps().get(pamDataBlock.getLongDataName()); + } + +} diff --git a/src/tethys/species/ITISFunctions.java b/src/tethys/species/ITISFunctions.java new file mode 100644 index 00000000..5e3a0709 --- /dev/null +++ b/src/tethys/species/ITISFunctions.java @@ -0,0 +1,84 @@ +package tethys.species; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import PamController.settings.output.xml.PAMGuardXMLPreview; +import PamController.settings.output.xml.PamguardXMLWriter; +import tethys.TethysControl; +import tethys.dbxml.DBQueryResult; +import tethys.dbxml.DBXMLQueries; +import tethys.dbxml.TethysQueryException; + +/** + * Functions associated with pulling ITIS information from the databsae. + * @author dg50 + * + */ +public class ITISFunctions { + + private TethysControl tethysControl; + + public ITISFunctions(TethysControl tethysControl) { + super(); + this.tethysControl = tethysControl; + } + + public TethysITISResult getITISInformation(int itisCode) { + /* + * hope to get back something like + * + + + 180488 + Species + Physeter macrocephalus + + Sperm Whale + cachalot + + + + */ + + String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"SPECIESCODE\"],\"optype\":\"binary\"}],\"enclose\":1}"; + jQ = jQ.replace("SPECIESCODE", String.format("%d", itisCode)); + + DBXMLQueries dbQueries = tethysControl.getDbxmlQueries(); + DBQueryResult qResult = null; + try { + qResult = dbQueries.executeQuery(jQ); + } catch (TethysQueryException e) { + e.printStackTrace(); + return null; + } + + Document doc = dbQueries.convertStringToXMLDocument(qResult.queryResult); + Element docEl = doc.getDocumentElement(); +// doc. +// PAMGuardXMLPreview xmlPreview = new PAMGuardXMLPreview(null, "returned", qResult.queryResult) + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + String fDoc = pamXMLWriter.getAsString(doc, true); + System.out.println(fDoc); + + String tsn = dbQueries.getElementData(docEl, "tsn"); + if (tsn == null) { + return null; + } + String taxunit = dbQueries.getElementData(docEl, "taxon_unit"); + String latin = dbQueries.getElementData(docEl, "completename"); + // try to find a vernacular. + NodeList vEls = doc.getElementsByTagName("vernacular"); + String vernacular = null; + if (vEls.getLength() > 0) { + Node f = vEls.item(0); + if (f instanceof Element) { + vernacular = dbQueries.getElementData((Element) f, "name"); + } + } + + return new TethysITISResult(itisCode, taxunit, latin, vernacular); + } +} diff --git a/src/tethys/species/SpeciesMapManager.java b/src/tethys/species/SpeciesMapManager.java index 26d8eda4..670e1926 100644 --- a/src/tethys/species/SpeciesMapManager.java +++ b/src/tethys/species/SpeciesMapManager.java @@ -1,5 +1,104 @@ package tethys.species; -public class SpeciesMapManager { +import java.io.Serializable; +import java.util.ArrayList; +import PamController.PamControlledUnitSettings; +import PamController.PamController; +import PamController.PamSettingManager; +import PamController.PamSettings; +import PamguardMVC.PamDataBlock; + +/** + * Master manager for species maps which will eventually allow for export and import from XML + * documents, databases and other things ... + * @author dg50 + * + */ +public class SpeciesMapManager implements PamSettings { + + private static SpeciesMapManager singleInstance = null; + + private static Object synch = new Object(); + + private GlobalSpeciesMap globalSpeciesMap; + + private SpeciesMapManager() { + PamSettingManager.getInstance().registerSettings(this); + } + + /** + * Get an instance of the global species manager. This handles look up tables + * for each datablock to convert from internal PAMGuard names to ITIS species codes and + * usefully call types for output to Tethys. + * @return + */ + public static SpeciesMapManager getInstance() { + if (singleInstance == null) { + synchronized (synch) { + if (singleInstance == null) { + singleInstance = new SpeciesMapManager(); + } + } + } + return singleInstance; + } + + @Override + public String getUnitName() { + return "Global Species Codes"; + } + + @Override + public String getUnitType() { + return "Global Species Codes"; + } + + @Override + public Serializable getSettingsReference() { + gatherSpeciesMaps(); + return globalSpeciesMap; + } + + /** + * Get species maps from all PAMGuard datablocks which have such a map + */ + private void gatherSpeciesMaps() { + if (globalSpeciesMap == null) { + globalSpeciesMap = new GlobalSpeciesMap(); + } + ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); + for (PamDataBlock aBlock : allDataBlocks) { + DataBlockSpeciesManager spManager = aBlock.getDatablockSpeciesManager(); + if (spManager == null) { + continue; + } + DataBlockSpeciesMap speciesMap = spManager.getDatablockSpeciesMap(); + globalSpeciesMap.put(aBlock, speciesMap); + } + } + + public DataBlockSpeciesMap getSpeciesMap(PamDataBlock pamDataBlock) { + if (globalSpeciesMap == null) { + return null; + } + return globalSpeciesMap.get(pamDataBlock); + } + + @Override + public long getSettingsVersion() { + return GlobalSpeciesMap.serialVersionUID; + } + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + Object obj = pamControlledUnitSettings.getSettings(); + if (obj instanceof GlobalSpeciesMap) { + this.globalSpeciesMap = (GlobalSpeciesMap) obj; + return true; + } + else { + return false; + } + } } diff --git a/src/tethys/species/SpeciesTest.java b/src/tethys/species/SpeciesTest.java index affdff22..ec9e09bb 100644 --- a/src/tethys/species/SpeciesTest.java +++ b/src/tethys/species/SpeciesTest.java @@ -40,7 +40,7 @@ public class SpeciesTest { System.out.printf("Query time was %3.1fms\n" , (double) (t2-t1)/1e6); System.out.println(queryResult); - TethysITISResult itisResult = new TethysITISResult(queryResult); +// TethysITISResult itisResult = new TethysITISResult(queryResult); } /* * @@ -83,9 +83,9 @@ public class SpeciesTest { } private void runJson() { // String jQ = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; -// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"624908\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"180488\"],\"optype\":\"binary\"}],\"enclose\":1}"; // String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/completename\",\"Mesoplodon\"],\"optype\":\"binary\"}],\"enclose\":1}"; - String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"dbxml:contains\",\"operands\":[\"ranks/rank/completename\",\"Mesoplodon\"],\"optype\":\"function\"}],\"enclose\":1}"; +// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"dbxml:contains\",\"operands\":[\"ranks/rank/completename\",\"Mesoplodon\"],\"optype\":\"function\"}],\"enclose\":1}"; System.out.println(jQ); diff --git a/src/tethys/species/TethysITISResult.java b/src/tethys/species/TethysITISResult.java index 9504d196..7e14ff19 100644 --- a/src/tethys/species/TethysITISResult.java +++ b/src/tethys/species/TethysITISResult.java @@ -1,12 +1,5 @@ package tethys.species; -import java.io.StringReader; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import org.w3c.dom.Document; -import org.xml.sax.InputSource; /** * Class to hold and unpack a XML string returned from the ITIS_ranks document @@ -16,22 +9,52 @@ import org.xml.sax.InputSource; */ public class TethysITISResult { + private int itisCode; + private String taxon_unit; + private String latin; + private String vernacular; + /** * Construct a ITIS object from XML data + * @param itisCode * @param xmlData + * @param vernacular + * @param latin */ - public TethysITISResult(String xmlData) { + public TethysITISResult(int itisCode, String taxon_unit, String latin, String vernacular) { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - - //API to obtain DOM Document instance - DocumentBuilder builder = null; - -// //Create DocumentBuilder with default configuration -// builder = factory.newDocumentBuilder(); -// -// //Parse the content to Document object -// Document doc = builder.parse(new InputSource(new StringReader(xmlData))); + this.itisCode = itisCode; + this.taxon_unit = taxon_unit; + this.latin = latin; + this.vernacular = vernacular; } + + /** + * @return the itisCode + */ + public int getItisCode() { + return itisCode; + } + + /** + * @return the taxon_unit + */ + public String getTaxon_unit() { + return taxon_unit; + } + + /** + * @return the latin + */ + public String getLatin() { + return latin; + } + + /** + * @return the vernacular + */ + public String getVernacular() { + return vernacular; + } } diff --git a/src/tethys/species/swing/DataBlockSpeciesDialog.java b/src/tethys/species/swing/DataBlockSpeciesDialog.java index ff29cb16..903de20e 100644 --- a/src/tethys/species/swing/DataBlockSpeciesDialog.java +++ b/src/tethys/species/swing/DataBlockSpeciesDialog.java @@ -1,7 +1,15 @@ package tethys.species.swing; +import java.awt.BorderLayout; import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import javax.swing.JButton; +import javax.swing.JPanel; + +import PamController.PamController; +import PamView.PamGui; import PamView.dialog.PamDialog; import PamguardMVC.PamDataBlock; @@ -9,14 +17,28 @@ public class DataBlockSpeciesDialog extends PamDialog { private static final long serialVersionUID = 1L; - DataBlockSpeciesPanel speciesPanel; + private DataBlockSpeciesPanel speciesPanel; private DataBlockSpeciesDialog(Window parentFrame, PamDataBlock dataBlock) { super(parentFrame, dataBlock.getDataName() + " species", false); + JPanel mainPanel = new JPanel(new BorderLayout()); speciesPanel = new DataBlockSpeciesPanel(dataBlock); - setDialogComponent(speciesPanel.getDialogComponent()); + mainPanel.add(BorderLayout.CENTER, speciesPanel.getDialogComponent()); + JButton itisButton = new JButton("Go to ITIS web site"); + itisButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + gotoITIS(); + } + }); + mainPanel.add(BorderLayout.NORTH, itisButton); + setDialogComponent(mainPanel); } + protected void gotoITIS() { + PamGui.openURL("https://www.itis.gov"); + } + public static void showDialog(Window parentFrame, PamDataBlock dataBlock) { DataBlockSpeciesDialog speciesDialog = new DataBlockSpeciesDialog(parentFrame, dataBlock); speciesDialog.setParams(); diff --git a/src/tethys/species/swing/DataBlockSpeciesPanel.java b/src/tethys/species/swing/DataBlockSpeciesPanel.java index b90b12bc..b7b69370 100644 --- a/src/tethys/species/swing/DataBlockSpeciesPanel.java +++ b/src/tethys/species/swing/DataBlockSpeciesPanel.java @@ -22,6 +22,8 @@ public class DataBlockSpeciesPanel implements PamDialogPanel { private PamDataBlock dataBlock; private JPanel speciesPanel; + + private ArrayList subPanels = new ArrayList<>(); public DataBlockSpeciesPanel(PamDataBlock dataBlock) { super(); @@ -41,6 +43,7 @@ public class DataBlockSpeciesPanel implements PamDialogPanel { public void setParams() { speciesPanel.removeAll(); speciesPanel.setLayout(new BoxLayout(speciesPanel, BoxLayout.Y_AXIS)); + subPanels.clear(); DataBlockSpeciesManager speciesManager = dataBlock.getDatablockSpeciesManager(); DataBlockSpeciesTypes speciesTypes = speciesManager.getSpeciesTypes(); @@ -48,6 +51,7 @@ public class DataBlockSpeciesPanel implements PamDialogPanel { DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); for (String aSpecies : speciesNames) { SpeciesSubPanel subPanel = new SpeciesSubPanel(aSpecies); + subPanels.add(subPanel); speciesPanel.add(subPanel.getDialogComponent()); if (speciesMap != null) { SpeciesMapItem speciesInfo = speciesMap.getItem(aSpecies); @@ -58,8 +62,19 @@ public class DataBlockSpeciesPanel implements PamDialogPanel { @Override public boolean getParams() { - // TODO Auto-generated method stub - return false; + DataBlockSpeciesManager speciesManager = dataBlock.getDatablockSpeciesManager(); + DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); + int errors = 0; + for (SpeciesSubPanel subPanel : subPanels) { + SpeciesMapItem mapItem = subPanel.getParams(); + if (mapItem == null) { + errors++; + } + else { + speciesMap.putItem(mapItem.getPamguardName(), mapItem); + } + } + return errors == 0; } } diff --git a/src/tethys/species/swing/SpeciesSubPanel.java b/src/tethys/species/swing/SpeciesSubPanel.java index ab9ea2c3..2609fa9b 100644 --- a/src/tethys/species/swing/SpeciesSubPanel.java +++ b/src/tethys/species/swing/SpeciesSubPanel.java @@ -2,6 +2,8 @@ package tethys.species.swing; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JComponent; @@ -10,8 +12,13 @@ import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.BevelBorder; +import PamController.PamController; +import PamView.dialog.PamDialog; import PamView.dialog.PamGridBagContraints; +import tethys.TethysControl; +import tethys.species.ITISFunctions; import tethys.species.SpeciesMapItem; +import tethys.species.TethysITISResult; public class SpeciesSubPanel { @@ -31,7 +38,7 @@ public class SpeciesSubPanel { c.gridx++; mainPanel.add(new JLabel(" ITIS code ", JLabel.RIGHT), c); c.gridx++; - mainPanel.add(itisCode = new JTextField(3), c); + mainPanel.add(itisCode = new JTextField(6), c); c.gridx ++; mainPanel.add(searchButton = new JButton("Find")); @@ -66,6 +73,43 @@ public class SpeciesSubPanel { latinName.setToolTipText("Scientific name"); commonName.setToolTipText("Common name"); + searchButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + searchSpecies(e); + } + }); + + } + + /** + * Action when 'Find' button is pressed. + * @param e + */ + protected void searchSpecies(ActionEvent e) { + TethysControl tethysControl = (TethysControl) PamController.getInstance().findControlledUnit(TethysControl.unitType); + if (tethysControl == null) { + return; + } + ITISFunctions itisFunctions = tethysControl.getItisFunctions(); + int itisCode = 0; + try { + itisCode = Integer.valueOf(this.itisCode.getText()); + } + catch (NumberFormatException ex) { + PamDialog.showWarning(PamController.getMainFrame(), "No Code", "Enter avalid ITIS code"); + return; + } + TethysITISResult itisInfo = itisFunctions.getITISInformation(itisCode); + if (itisInfo != null) { + if (itisInfo.getLatin() != null) { + latinName.setText(itisInfo.getLatin()); + } + if (itisInfo.getVernacular() != null) { + commonName.setText(itisInfo.getVernacular()); + } + } +// System.out.println(itisInfo); } public JComponent getDialogComponent() { @@ -88,8 +132,24 @@ public class SpeciesSubPanel { } public SpeciesMapItem getParams() { - // TODO Auto-generated method stub - return null; + Integer tsn = null; + String vernacular = null; + String latin = null; + try { + tsn = Integer.valueOf(itisCode.getText()); + } + catch (NumberFormatException e) { + PamDialog.showWarning(PamController.getMainFrame(), pamguardName.getText(), "You must specified an ITIS taxanomic code"); + return null; + } + latin = latinName.getText(); + vernacular = commonName.getText(); + String callType = this.callType.getText(); + if (callType == null || callType.length() == 0) { + PamDialog.showWarning(PamController.getMainFrame(), pamguardName.getText(), "You must specified a call type"); + return null; + } + return new SpeciesMapItem(tsn, callType, pamguardName.getText(), latin, vernacular); } } From 74deebe446e3ccb1ac05b1834ed4e7bad6c2b49a Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 8 Aug 2023 18:29:45 +0100 Subject: [PATCH 41/65] Fixed queries Also added internal display of existing docuemtns (so they can be deleted) --- src/PamView/PamGui.java | 37 +++- src/RightWhaleEdgeDetector/RWEDataBlock.java | 14 +- .../species/RWSpeciesManager.java | 26 +++ .../species/RWSpeciesTypes.java | 15 ++ .../ClickBlockSpeciesManager.java | 23 +- src/tethys/TethysControl.java | 18 +- src/tethys/TethysTimeFuncs.java | 1 + src/tethys/dbxml/DBXMLConnect.java | 2 +- src/tethys/dbxml/DBXMLQueries.java | 82 +++++++- src/tethys/deployment/DeploymentHandler.java | 7 +- src/tethys/niluswraps/PDeployment.java | 12 +- src/tethys/output/TethysExportParams.java | 2 + .../species/DataBlockSpeciesManager.java | 42 +++- src/tethys/species/DataBlockSpeciesMap.java | 2 +- src/tethys/species/DataBlockSpeciesTypes.java | 68 +++++- src/tethys/species/ITISTypes.java | 22 ++ src/tethys/species/SpeciesTest.java | 81 +++++++- .../species/swing/DataBlockSpeciesDialog.java | 5 +- src/tethys/species/swing/SpeciesSubPanel.java | 2 +- src/tethys/swing/FancyClientButton.java | 64 ++++-- .../swing/documents/TethysDocumentTable.java | 196 ++++++++++++++++++ .../swing/documents/TethysDocumentsFrame.java | 49 +++++ .../swing/export/DescriptionTypePanel.java | 13 +- .../ConnectedRegionDataBlock.java | 13 +- .../species/WhistleSpeciesManager.java | 34 +++ 25 files changed, 783 insertions(+), 47 deletions(-) create mode 100644 src/RightWhaleEdgeDetector/species/RWSpeciesManager.java create mode 100644 src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java create mode 100644 src/tethys/species/ITISTypes.java create mode 100644 src/tethys/swing/documents/TethysDocumentTable.java create mode 100644 src/tethys/swing/documents/TethysDocumentsFrame.java create mode 100644 src/whistlesAndMoans/species/WhistleSpeciesManager.java diff --git a/src/PamView/PamGui.java b/src/PamView/PamGui.java index 98ef13e4..99980509 100644 --- a/src/PamView/PamGui.java +++ b/src/PamView/PamGui.java @@ -61,6 +61,7 @@ import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JRootPane; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; @@ -234,6 +235,14 @@ public class PamGui extends PamView implements WindowListener, PamSettings { boolean posOK = true; try { posOK = ScreenSize.isPointOnScreen(topCorner); + // some weird stuff going down whereby the screen position is + // given as -8 from the corner where it really it. this can return + // false and then push the display onto a different monitor, so alow for this. + if (posOK == false) { + topCorner.x += 10; + topCorner.y += 10; + posOK = ScreenSize.isPointOnScreen(topCorner); + } } catch (Exception e) { } if (!posOK) { @@ -1657,10 +1666,10 @@ public class PamGui extends PamView implements WindowListener, PamSettings { protected void getGuiParameters() { guiParameters.extendedState = frame.getExtendedState(); guiParameters.state = frame.getState(); - if (guiParameters.state != Frame.MAXIMIZED_BOTH) { +// if (guiParameters.state != Frame.MAXIMIZED_BOTH) { guiParameters.size = frame.getSize(); guiParameters.bounds = frame.getBounds(); - } +// } } /** @@ -1975,6 +1984,30 @@ public class PamGui extends PamView implements WindowListener, PamSettings { public PamTabbedPane getTabbedPane() { return this.mainTab; } + + /** + * find a parent window for a JComponent. This can be useful in + * finding windows to open child dialogs when the object holding + * the component may not have a direct reference back to it's dialog. + * @param component any Swing component + * @return parent Window (or frame) if it can be found + */ + public static Window findComponentWindow(JComponent component) { + if (component == null) { + return null; + } + JRootPane root = component.getRootPane(); + if (root == null) { + return null; + } + Container rootP = root.getParent(); + if (rootP instanceof Window) { + return (Window) rootP; + } + else { + return null; + } + } } \ No newline at end of file diff --git a/src/RightWhaleEdgeDetector/RWEDataBlock.java b/src/RightWhaleEdgeDetector/RWEDataBlock.java index 1969bca0..a603eafe 100644 --- a/src/RightWhaleEdgeDetector/RWEDataBlock.java +++ b/src/RightWhaleEdgeDetector/RWEDataBlock.java @@ -6,15 +6,19 @@ import PamguardMVC.PamProcess; import PamguardMVC.dataOffline.OfflineDataLoadInfo; import PamguardMVC.dataSelector.DataSelectorCreator; import RightWhaleEdgeDetector.datasel.RWDataSelCreator; +import RightWhaleEdgeDetector.species.RWSpeciesManager; import pamScrollSystem.ViewLoadObserver; +import tethys.species.DataBlockSpeciesManager; import whistlesAndMoans.AbstractWhistleDataBlock; -public class RWEDataBlock extends AbstractWhistleDataBlock implements GroupedDataSource { +public class RWEDataBlock extends AbstractWhistleDataBlock implements GroupedDataSource { private double[] rwFreqRange = {50., 250.}; private RWEControl rweControl; private RWEProcess rweProcess; private RWDataSelCreator dataSelCreator; + + private RWSpeciesManager rwSpeciesManager; public RWEDataBlock(RWEControl rweControl, String dataName, RWEProcess rweProcess, int channelMap) { @@ -53,4 +57,12 @@ public class RWEDataBlock extends AbstractWhistleDataBlock implements GroupedDat return dataSelCreator; } + @Override + public DataBlockSpeciesManager getDatablockSpeciesManager() { + if (rwSpeciesManager == null) { + rwSpeciesManager = new RWSpeciesManager(this); + } + return rwSpeciesManager; + } + } diff --git a/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java b/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java new file mode 100644 index 00000000..86f0bf49 --- /dev/null +++ b/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java @@ -0,0 +1,26 @@ +package RightWhaleEdgeDetector.species; + +import PamguardMVC.PamDataBlock; +import RightWhaleEdgeDetector.RWEDataUnit; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.DataBlockSpeciesTypes; + +public class RWSpeciesManager extends DataBlockSpeciesManager { + + private RWSpeciesTypes rwSpeciesTypes = new RWSpeciesTypes(); + + public RWSpeciesManager(PamDataBlock dataBlock) { + super(dataBlock); + } + + @Override + public DataBlockSpeciesTypes getSpeciesTypes() { + return rwSpeciesTypes; + } + + @Override + public String getSpeciesString(RWEDataUnit dataUnit) { + return RWSpeciesTypes.onlyType; + } + +} diff --git a/src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java b/src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java new file mode 100644 index 00000000..7a249a68 --- /dev/null +++ b/src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java @@ -0,0 +1,15 @@ +package RightWhaleEdgeDetector.species; + +import tethys.species.DataBlockSpeciesTypes; + +public class RWSpeciesTypes extends DataBlockSpeciesTypes { + + public static final String onlyType = "Up call"; + + private static final int glacialis = 180536; + + public RWSpeciesTypes() { + super(glacialis, onlyType); + } + +} diff --git a/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java b/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java index 971fe139..930ec8f0 100644 --- a/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java +++ b/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java @@ -5,6 +5,8 @@ import clickDetector.ClickDataBlock; import clickDetector.ClickDetection; import tethys.species.DataBlockSpeciesManager; import tethys.species.DataBlockSpeciesTypes; +import tethys.species.ITISTypes; +import tethys.species.SpeciesMapItem; public class ClickBlockSpeciesManager extends DataBlockSpeciesManager { @@ -13,6 +15,8 @@ public class ClickBlockSpeciesManager extends DataBlockSpeciesManager getCollectionDocumentList(String collection) { + if (collection == null) { + return null; + } + collection = checkCollectionPlural(collection); +// if (collection.endsWith("s")) { +// collection = collection.substring(0, collection.length()-1); +// } + String baseQuery = "{\"return\":[\"COLLECTIONNAME/Id\"],\"select\":[],\"enclose\":1}"; + baseQuery = baseQuery.replace("COLLECTIONNAME", collection); + String tagName = "Id"; + + if (collection.equals("SpeciesAbbreviations")) { + baseQuery = "{\"return\":[\"Abbreviations/Name\"],\"select\":[],\"enclose\":1}"; + tagName = "Name"; + } + + DBQueryResult result; + try { + result = executeQuery(baseQuery); + } catch (TethysQueryException e) { + System.out.println("Error with query: " + baseQuery); + tethysControl.showException(e); + return null; + } + + if (result == null || result.queryResult == null) { + return null; + } + Document doc = convertStringToXMLDocument(result.queryResult); + if (doc == null) { + return null; + } + NodeList returns = doc.getElementsByTagName(tagName); + ArrayList docIds = new ArrayList<>(); + int n = returns.getLength(); + for (int i = 0; i < n; i++) { + Node aNode = returns.item(i); + String docId = aNode.getTextContent(); + docIds.add(docId); + } + + return docIds; + } public ArrayList getProjectNames() { @@ -644,10 +715,13 @@ public class DBXMLQueries { if (nodeList == null || nodeList.getLength() == 0) { return null; } - Node firstNode = nodeList.item(0); - if (firstNode instanceof Element) { - root = (Element) firstNode; - break; + int count = nodeList.getLength(); + for (int i = 0; i < count; i++) { + Node firstNode = nodeList.item(i); + if (firstNode instanceof Element) { + root = (Element) firstNode; + break; + } } } return root.getTextContent(); diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 1c29d8b4..6cf5c13f 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -455,8 +455,11 @@ public class DeploymentHandler implements TethysStateObserver { public long getDeploymentOverlap(PDeployment aDeployment, RecordingPeriod aPeriod) { long start = aPeriod.getRecordStart(); // recording period. long stop = aPeriod.getRecordStop(); - long depStart = aDeployment.getAudioStart(); - long depStop = aDeployment.getAudioEnd(); + Long depStart = aDeployment.getAudioStart(); + Long depStop = aDeployment.getAudioEnd(); + if (depStart == null || depStop == null) { + return -1; + } long overlap = (Math.min(stop, depStop)-Math.max(start, depStart)); return overlap; } diff --git a/src/tethys/niluswraps/PDeployment.java b/src/tethys/niluswraps/PDeployment.java index 4652329c..5f9067db 100644 --- a/src/tethys/niluswraps/PDeployment.java +++ b/src/tethys/niluswraps/PDeployment.java @@ -23,7 +23,7 @@ public class PDeployment { public Long getAudioStart() { DeploymentRecoveryDetails detail = deployment.getDeploymentDetails(); - if (detail == null) { + if (detail == null || detail.getAudioTimeStamp() == null) { return null; } return TethysTimeFuncs.millisFromGregorianXML(detail.getAudioTimeStamp()); @@ -31,7 +31,7 @@ public class PDeployment { public Long getAudioEnd() { DeploymentRecoveryDetails detail = deployment.getRecoveryDetails(); - if (detail == null) { + if (detail == null || detail.getAudioTimeStamp() == null) { return null; } return TethysTimeFuncs.millisFromGregorianXML(detail.getAudioTimeStamp()); @@ -52,7 +52,13 @@ public class PDeployment { } public String getShortDescription() { - return String.format("%s %s", deployment.getId(), PamCalendar.formatDBDate(getAudioStart())); + Long audioStart = getAudioStart(); + if (audioStart == null) { + return String.format("%s %s", deployment.getId(), "unknown start"); + } + else { + return String.format("%s %s", deployment.getId(), PamCalendar.formatDBDate(getAudioStart())); + } } diff --git a/src/tethys/output/TethysExportParams.java b/src/tethys/output/TethysExportParams.java index 81ad0eb3..7e93fe16 100644 --- a/src/tethys/output/TethysExportParams.java +++ b/src/tethys/output/TethysExportParams.java @@ -38,6 +38,8 @@ public class TethysExportParams implements Serializable, Cloneable{ */ private String datasetName; + public boolean listDocsInPamguard; + /** * @return the datasetName */ diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java index 17f281a3..83582c61 100644 --- a/src/tethys/species/DataBlockSpeciesManager.java +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -15,6 +15,10 @@ abstract public class DataBlockSpeciesManager { private DataBlockSpeciesMap datablockSpeciesMap; private PamDataBlock dataBlock; + + private String defaultName = null; + + private SpeciesMapItem defaultDefaultSpecies = null; public abstract DataBlockSpeciesTypes getSpeciesTypes(); @@ -29,7 +33,7 @@ abstract public class DataBlockSpeciesManager { public SpeciesMapItem getSpeciesItem(T dataUnit) { String speciesString = getSpeciesString(dataUnit); if (speciesString == null) { - return null; + return getDefaultDefaultSpecies(); } DataBlockSpeciesMap speciesMap = getDatablockSpeciesMap(); if (speciesMap == null) { @@ -41,10 +45,18 @@ abstract public class DataBlockSpeciesManager { public DataBlockSpeciesMap getDatablockSpeciesMap() { if (datablockSpeciesMap == null) { datablockSpeciesMap = new DataBlockSpeciesMap(); + checkMapDefault(); } return datablockSpeciesMap; } + private void checkMapDefault() { + SpeciesMapItem defaultItem = datablockSpeciesMap.getItem(getDefaultName()); + if (defaultItem == null) { + datablockSpeciesMap.putItem(getDefaultName(), getDefaultDefaultSpecies()); + } + } + public void setDatablockSpeciesMap(DataBlockSpeciesMap datablockSpeciesMap) { this.datablockSpeciesMap = datablockSpeciesMap; } @@ -59,4 +71,32 @@ abstract public class DataBlockSpeciesManager { public PamDataBlock getDataBlock() { return dataBlock; } + + /** + * @return the defaultSpecies + */ + public SpeciesMapItem getDefaultDefaultSpecies() { + return defaultDefaultSpecies; + } + + /** + * @param defaultSpecies the defaultSpecies to set + */ + public void setDefaultDefaultSpecies(SpeciesMapItem defaultDefaultSpecies) { + this.defaultDefaultSpecies = defaultDefaultSpecies; + } + + /** + * @return the defaultName + */ + public String getDefaultName() { + return defaultName; + } + + /** + * @param defaultName the defaultName to set + */ + public void setDefaultName(String defaultName) { + this.defaultName = defaultName; + } } diff --git a/src/tethys/species/DataBlockSpeciesMap.java b/src/tethys/species/DataBlockSpeciesMap.java index 3827d20a..6d6b6857 100644 --- a/src/tethys/species/DataBlockSpeciesMap.java +++ b/src/tethys/species/DataBlockSpeciesMap.java @@ -11,7 +11,7 @@ import java.util.HashMap; public class DataBlockSpeciesMap implements Serializable { public static final long serialVersionUID = 1L; - + private HashMap speciesTable = new HashMap<>(); protected HashMap getSpeciesTable() { diff --git a/src/tethys/species/DataBlockSpeciesTypes.java b/src/tethys/species/DataBlockSpeciesTypes.java index 6b9a4496..3fb40201 100644 --- a/src/tethys/species/DataBlockSpeciesTypes.java +++ b/src/tethys/species/DataBlockSpeciesTypes.java @@ -12,15 +12,44 @@ import java.util.ArrayList; public class DataBlockSpeciesTypes { /** - * List of species names / codes associated with this data block. + * List of species names / codes associated with this data block. These can be translated, + * via a HashMap to more detailed objects which include an ITIS code. */ private ArrayList speciesNames; + /** + * Probably only to be used when there are no defined names, but helpful if it's set. + */ + private int itisDefault = ITISTypes.UNKNOWN; + + /** + * A default sound type, which can be used for all 'species', but can get + * overridden in other scenarios. e.g. 'Click', 'Whistle' + */ + private String defaultType; + + /** + * @param defaultType + */ + public DataBlockSpeciesTypes(String defaultType) { + this.defaultType = defaultType; + } + + /** + * @param itisDefault + * @param defaultType + */ + public DataBlockSpeciesTypes(int itisDefault, String defaultType) { + this.itisDefault = itisDefault; + this.defaultType = defaultType; + } + /** * constructor to use with a array of String names. * @param speciesList */ - public DataBlockSpeciesTypes(String[] speciesList) { + public DataBlockSpeciesTypes(String defaultType, String[] speciesList) { + this.defaultType = defaultType; if (speciesList == null) { speciesNames = new ArrayList<>(); } @@ -39,5 +68,40 @@ public class DataBlockSpeciesTypes { return speciesNames; } + /** + * @return the itisDefault + */ + public int getItisDefault() { + return itisDefault; + } + + /** + * @param itisDefault the itisDefault to set + */ + public void setItisDefault(int itisDefault) { + this.itisDefault = itisDefault; + } + + /** + * @return the defaultType + */ + public String getDefaultType() { + return defaultType; + } + + /** + * @param defaultType the defaultType to set + */ + public void setDefaultType(String defaultType) { + this.defaultType = defaultType; + } + + /** + * @param speciesNames the speciesNames to set + */ + public void setSpeciesNames(ArrayList speciesNames) { + this.speciesNames = speciesNames; + } + } diff --git a/src/tethys/species/ITISTypes.java b/src/tethys/species/ITISTypes.java new file mode 100644 index 00000000..409f74dd --- /dev/null +++ b/src/tethys/species/ITISTypes.java @@ -0,0 +1,22 @@ +package tethys.species; + +/** + * Some MR defined ITIS codes for non animal sounds. + * @author dg50 + * + */ +public class ITISTypes { + + public static final int UNKNOWN = 0; + public static final int ANTHROPOGENIC = -10; + + public static final String getName(int code) { + switch (code) { + case UNKNOWN: + return "Unknown"; + case ANTHROPOGENIC: + return "Anthropogenic"; + } + return null; + } +} diff --git a/src/tethys/species/SpeciesTest.java b/src/tethys/species/SpeciesTest.java index ec9e09bb..b476b3ba 100644 --- a/src/tethys/species/SpeciesTest.java +++ b/src/tethys/species/SpeciesTest.java @@ -1,5 +1,15 @@ package tethys.species; +import java.io.StringReader; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.InputSource; + +import PamController.settings.output.xml.PamguardXMLWriter; import dbxml.JerseyClient; import dbxml.Queries; import tethys.dbxml.DBQueryResult; @@ -66,26 +76,62 @@ public class SpeciesTest { private void runXQuery() { System.out.println("Running runXQuery()"); String queryBase = "count(collection(\"Detections\")/Detections[Id=\"ReplaceDocumentId\"]/OnEffort/Detection)"; - String xQ = "collection(\"ITIS_ranks\")/ty:ranks/ty:rank[dbxml:contains(ty:completename, \"Physeter\")]"; +// String xQ = "collection(\"ITIS_ranks\")/ty:ranks/ty:rank[dbxml:contains(ty:completename, \"Physeter\")]"; + +// String xQ = " {\r\n" +// + " for $Deployment0 in collection(\"Deployments\")/Deployment[Project = \"BM\"]\r\n" +// + " return\r\n" +// + " {\r\n" +// + " $Deployment0/Id\r\n" +// + " }\r\n" +// + "} "; + + String xQ = " {\r\n" + + " for $rank0 in collection(\"ITIS_ranks\")/rank[tsn = \"180488\"]\r\n" + + " return\r\n" + + " {\r\n" + + " $rank0/completename\r\n" + + " }\r\n" + + "} "; + JerseyClient jerseyClient = new JerseyClient(uri); Queries queries = new Queries(jerseyClient); - String result = null; + String queryResult = null; try { - result = queries.QueryTethys(xQ); + queryResult = queries.QueryTethys(xQ); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } - System.out.println(result); + //API to obtain DOM Document instance + DocumentBuilder builder = null; + Document doc = null; + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + try { + //Create DocumentBuilder with default configuration + builder = factory.newDocumentBuilder(); + + //Parse the content to Document object + doc = builder.parse(new InputSource(new StringReader(queryResult))); + } catch (Exception e) { +// e.printStackTrace(); + System.out.println(queryResult); + } +// PAMGuardXMLPreview xmlPreview = new PAMGuardXMLPreview(null, "returned", qResult.queryResult) + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + String fDoc = pamXMLWriter.getAsString(doc, true); + System.out.println(fDoc); +// System.out.println(queryResult); } private void runJson() { // String jQ = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; - String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"180488\"],\"optype\":\"binary\"}],\"enclose\":1}"; -// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/completename\",\"Mesoplodon\"],\"optype\":\"binary\"}],\"enclose\":1}"; -// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"dbxml:contains\",\"operands\":[\"ranks/rank/completename\",\"Mesoplodon\"],\"optype\":\"function\"}],\"enclose\":1}"; +// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"180488\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"180488\"],\"optype\":\"binary\"}],\"enclose\":1}"; +// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/completename\",\"Physeter macrocephalus\"],\"optype\":\"binary\"}],\"enclose\":1}"; +// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"dbxml:contains\",\"operands\":[\"ranks/rank/completename\",\"Sperm\"],\"optype\":\"function\"}],\"enclose\":1}"; System.out.println(jQ); @@ -103,7 +149,26 @@ public class SpeciesTest { } long t2 = System.nanoTime(); System.out.printf("Query time was %3.1fms\n" , (double) (t2-t1)/1e6); - System.out.println(queryResult); + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + //API to obtain DOM Document instance + DocumentBuilder builder = null; + Document doc = null; + try { + //Create DocumentBuilder with default configuration + builder = factory.newDocumentBuilder(); + + //Parse the content to Document object + doc = builder.parse(new InputSource(new StringReader(queryResult))); + } catch (Exception e) { +// e.printStackTrace(); + System.out.println(queryResult); + } +// PAMGuardXMLPreview xmlPreview = new PAMGuardXMLPreview(null, "returned", qResult.queryResult) + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + String fDoc = pamXMLWriter.getAsString(doc, true); + System.out.println(fDoc); +// System.out.println(queryResult); } diff --git a/src/tethys/species/swing/DataBlockSpeciesDialog.java b/src/tethys/species/swing/DataBlockSpeciesDialog.java index 903de20e..d0c44791 100644 --- a/src/tethys/species/swing/DataBlockSpeciesDialog.java +++ b/src/tethys/species/swing/DataBlockSpeciesDialog.java @@ -25,13 +25,16 @@ public class DataBlockSpeciesDialog extends PamDialog { speciesPanel = new DataBlockSpeciesPanel(dataBlock); mainPanel.add(BorderLayout.CENTER, speciesPanel.getDialogComponent()); JButton itisButton = new JButton("Go to ITIS web site"); + itisButton.setToolTipText("Go to ITIS website to search for species codes"); itisButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { gotoITIS(); } }); - mainPanel.add(BorderLayout.NORTH, itisButton); + JPanel nPanel = new JPanel(new BorderLayout()); + nPanel.add(BorderLayout.EAST, itisButton); + mainPanel.add(BorderLayout.NORTH, nPanel); setDialogComponent(mainPanel); } diff --git a/src/tethys/species/swing/SpeciesSubPanel.java b/src/tethys/species/swing/SpeciesSubPanel.java index 2609fa9b..0233cbc9 100644 --- a/src/tethys/species/swing/SpeciesSubPanel.java +++ b/src/tethys/species/swing/SpeciesSubPanel.java @@ -124,7 +124,7 @@ public class SpeciesSubPanel { commonName.setText(null); return; } - pamguardName.setText(speciesMapItem.getPamguardName()); + pamguardName.setText("\"" + speciesMapItem.getPamguardName() + "\""); itisCode.setText(String.format("%d", speciesMapItem.getItisCode())); callType.setText(speciesMapItem.getCallType()); latinName.setText(speciesMapItem.getLatinName()); diff --git a/src/tethys/swing/FancyClientButton.java b/src/tethys/swing/FancyClientButton.java index deb9a8b4..9a72b16a 100644 --- a/src/tethys/swing/FancyClientButton.java +++ b/src/tethys/swing/FancyClientButton.java @@ -8,9 +8,11 @@ import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import javax.swing.AbstractButton; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; @@ -31,11 +33,14 @@ public class FancyClientButton extends JPanel { private JButton dropButton; private JPopupMenu collectionsMenu; private TethysControl tethysControl; - + private JCheckBoxMenuItem showBrowser; + private AbstractButton showPAMGuard; + + public FancyClientButton(TethysControl tethysControl) { this.tethysControl = tethysControl; setLayout(new GridBagLayout()); -// setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + // setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); GridBagConstraints c = new GridBagConstraints(); c.ipadx = c.ipady = 0; c.insets = new Insets(0,0,0,0); @@ -44,8 +49,8 @@ public class FancyClientButton extends JPanel { clientButton.setToolTipText("Open Tethys web client in default browser"); ImageIcon arrowDown= null; try { - arrowDown = new ImageIcon(ClassLoader - .getSystemResource("Resources/SidePanelShowH.png")); + arrowDown = new ImageIcon(ClassLoader + .getSystemResource("Resources/SidePanelShowH.png")); } catch (Exception e) { } @@ -65,31 +70,66 @@ public class FancyClientButton extends JPanel { dInsets.left = dInsets.right = 4; dropButton.setBorder(new EmptyBorder(dInsets)); } - + String[] collections = DBXMLConnect.collections; collectionsMenu = new JPopupMenu(); + boolean isP = tethysControl.getTethysExportParams().listDocsInPamguard; + showBrowser = new JCheckBoxMenuItem("Show in Browser", isP == false); + showBrowser.setEnabled(isP); + showBrowser.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + tethysControl.getTethysExportParams().listDocsInPamguard = false; + enableItems(); + } + }); + showBrowser.setToolTipText("Show collection in default Web Browser"); + collectionsMenu.add(showBrowser); + showPAMGuard = new JCheckBoxMenuItem("Show in PAMGuard", isP); + showPAMGuard.setEnabled(isP == false); + showPAMGuard.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + tethysControl.getTethysExportParams().listDocsInPamguard = true; + enableItems(); + } + }); + showPAMGuard.setToolTipText("Show collection in PAMGuard window"); + collectionsMenu.add(showPAMGuard); + + collectionsMenu.addSeparator(); + for (int i = 0; i < collections.length; i++) { JMenuItem menuItem = new JMenuItem(collections[i]); menuItem.addActionListener(new OpenCollection(collections[i])); collectionsMenu.add(menuItem); } - + dropButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { collectionsMenu.show(dropButton, 0, 0); } }); + enableItems(); } - + + protected void enableItems() { + boolean isP = tethysControl.getTethysExportParams().listDocsInPamguard; + showBrowser.setSelected(!isP); + showBrowser.setEnabled(true); + showPAMGuard.setSelected(isP); + showPAMGuard.setEnabled(true); + } + public void addActionListener(ActionListener actionListener) { clientButton.addActionListener(actionListener); } - + private class OpenCollection implements ActionListener { private String collection; - + public OpenCollection(String collection) { super(); this.collection = collection; @@ -99,8 +139,8 @@ public class FancyClientButton extends JPanel { public void actionPerformed(ActionEvent e) { tethysControl.openTethysCollection(collection); } - + } - - + + } diff --git a/src/tethys/swing/documents/TethysDocumentTable.java b/src/tethys/swing/documents/TethysDocumentTable.java new file mode 100644 index 00000000..e1f8f623 --- /dev/null +++ b/src/tethys/swing/documents/TethysDocumentTable.java @@ -0,0 +1,196 @@ +package tethys.swing.documents; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collections; + +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.table.AbstractTableModel; + +import PamView.dialog.PamDialogPanel; +import PamView.tables.SwingTableColumnWidths; +import tethys.TethysControl; + +/** + * Table view of a collection of Tethys documents. + * @author dg50 + * + */ +public class TethysDocumentTable implements PamDialogPanel { + + private TethysControl tethysControl; + + private String collectionName; + + private JTable mainTable; + + private TableModel tableModel; + + private ArrayList documentNames; + + private JPanel mainPanel; + + private JScrollPane scrollPane; + + /** + * @param tethysControl + * @param collectionName + */ + public TethysDocumentTable(TethysControl tethysControl, String collectionName) { + this.tethysControl = tethysControl; + mainPanel = new JPanel(new BorderLayout()); + tableModel = new TableModel(); + mainTable = new JTable(tableModel); + scrollPane = new JScrollPane(mainTable); + mainPanel.add(BorderLayout.CENTER, scrollPane); + new SwingTableColumnWidths(tethysControl.getUnitName()+"TethysDocumentsTable", mainTable); + this.setCollectionName(collectionName); + mainTable.addMouseListener(new TableMouse()); + } + + public void updateTableData() { + documentNames = tethysControl.getDbxmlQueries().getCollectionDocumentList(collectionName); + if (documentNames != null) { + Collections.sort(documentNames); + } + tableModel.fireTableDataChanged(); + } + + 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); + } + } + + } + + public void showPopupMenu(MouseEvent e) { + if (documentNames == null) { + return; + } + int row = mainTable.getSelectedRow(); + if (row < 0|| row >= documentNames.size()) { + return; + } + String docName = documentNames.get(row); + JPopupMenu popMenu = new JPopupMenu(); + JMenuItem menuItem = new JMenuItem("Show document " + docName); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showDocument(docName); + } + }); + popMenu.add(menuItem); + menuItem = new JMenuItem("Delete document " + docName); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteDocument(docName); + } + }); + popMenu.add(menuItem); + + popMenu.show(e.getComponent(), e.getX(), e.getY()); + } + + private void showDocument(String docName) { + tethysControl.displayDocument(collectionName, docName); + } + + private void deleteDocument(String docName) { + // TODO Auto-generated method stub + + } + + private class TableModel extends AbstractTableModel { + + private String[] columnNames = {"", "Document Id/Name"}; + + @Override + public int getRowCount() { + if (documentNames == null) { + return 0; + } + return documentNames.size(); + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + if (documentNames == null) { + return null; + } + switch (columnIndex) { + case 0: + return rowIndex+1; + case 1: + return documentNames.get(rowIndex); + } + return null; + } + + @Override + public String getColumnName(int column) { + return columnNames[column]; + } + + } + + + + @Override + public JComponent getDialogComponent() { + return mainPanel; + } + + @Override + public void setParams() { + // TODO Auto-generated method stub + + } + + @Override + public boolean getParams() { + // TODO Auto-generated method stub + return false; + } + + /** + * @return the collectionName + */ + public String getCollectionName() { + return collectionName; + } + + /** + * @param collectionName the collectionName to set + */ + public void setCollectionName(String collectionName) { + this.collectionName = collectionName; + updateTableData(); + } +} diff --git a/src/tethys/swing/documents/TethysDocumentsFrame.java b/src/tethys/swing/documents/TethysDocumentsFrame.java new file mode 100644 index 00000000..b08ba36e --- /dev/null +++ b/src/tethys/swing/documents/TethysDocumentsFrame.java @@ -0,0 +1,49 @@ +package tethys.swing.documents; + +import java.awt.Window; + +import PamView.dialog.PamDialog; +import tethys.TethysControl; + +public class TethysDocumentsFrame extends PamDialog { + + private static final long serialVersionUID = 1L; + + private static TethysDocumentsFrame singleInstance; + + private TethysDocumentTable documentsTable; + + private TethysDocumentsFrame(Window parentFrame,TethysControl tethysControl) { + super(parentFrame, "Documents", false); + documentsTable = new TethysDocumentTable(tethysControl, null); + setDialogComponent(documentsTable.getDialogComponent()); + setModalityType(ModalityType.MODELESS); + setResizable(true); + getOkButton().setVisible(false); + getCancelButton().setText("Close"); + } + + public static void showTable(Window parentFrame, TethysControl tethysControl, String collectionName) { + if (singleInstance == null) { + singleInstance = new TethysDocumentsFrame(parentFrame, tethysControl); + } + singleInstance.documentsTable.setCollectionName(collectionName); + singleInstance.setTitle(collectionName + " Documents"); + singleInstance.setVisible(true); + } + + + @Override + public boolean getParams() { + return true; + } + + @Override + public void cancelButtonPressed() { + } + + @Override + public void restoreDefaultSettings() { + } + +} diff --git a/src/tethys/swing/export/DescriptionTypePanel.java b/src/tethys/swing/export/DescriptionTypePanel.java index ffa75651..362103ff 100644 --- a/src/tethys/swing/export/DescriptionTypePanel.java +++ b/src/tethys/swing/export/DescriptionTypePanel.java @@ -1,15 +1,21 @@ package tethys.swing.export; +import java.awt.Container; import java.awt.Dimension; +import java.awt.Frame; import java.awt.Label; +import java.awt.Window; import javax.swing.BoxLayout; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JRootPane; import javax.swing.JScrollPane; import javax.swing.JTextArea; +import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; +import PamView.PamGui; import PamView.dialog.PamDialog; import nilus.DescriptionType; import tethys.niluswraps.PDescriptionType; @@ -75,14 +81,15 @@ public class DescriptionTypePanel { } public boolean getParams(PDescriptionType description) { + Window f = PamGui.findComponentWindow(mainPanel); if (checkField(requireObjective, tObjectives) == false) { - return PamDialog.showWarning(null, "Objectives", "The objectives field must be competed"); + return PamDialog.showWarning(f, "Objectives", "The objectives field must be completed"); } if (checkField(requireAbstract, tAbstract) == false) { - return PamDialog.showWarning(null, "Abstract", "The abstract field must be competed"); + return PamDialog.showWarning(f, "Abstract", "The abstract field must be completed"); } if (checkField(requireMethod, tMethod) == false) { - return PamDialog.showWarning(null, "Method", "The method field must be competed"); + return PamDialog.showWarning(f, "Method", "The method field must be completed"); } description.setObjectives(tObjectives.getText()); diff --git a/src/whistlesAndMoans/ConnectedRegionDataBlock.java b/src/whistlesAndMoans/ConnectedRegionDataBlock.java index 017fafee..5787c098 100644 --- a/src/whistlesAndMoans/ConnectedRegionDataBlock.java +++ b/src/whistlesAndMoans/ConnectedRegionDataBlock.java @@ -2,6 +2,7 @@ package whistlesAndMoans; import whistlesAndMoans.alarm.WMAlarmCounterProvider; import whistlesAndMoans.dataSelector.WMDDataSelectCreator; +import whistlesAndMoans.species.WhistleSpeciesManager; import whistlesAndMoans.toad.WSLToadCalculator; import PamView.GroupedDataSource; import PamView.GroupedSourceParameters; @@ -10,14 +11,16 @@ import PamguardMVC.dataSelector.DataSelectorCreator; import PamguardMVC.toad.TOADCalculator; import alarm.AlarmCounterProvider; import alarm.AlarmDataSource; +import tethys.species.DataBlockSpeciesManager; -public class ConnectedRegionDataBlock extends AbstractWhistleDataBlock implements AlarmDataSource, GroupedDataSource, FFTDataHolderBlock { +public class ConnectedRegionDataBlock extends AbstractWhistleDataBlock implements AlarmDataSource, GroupedDataSource, FFTDataHolderBlock { private WhistleToneConnectProcess parentProcess; private WhistleMoanControl whistleMoanControl; private WMAlarmCounterProvider wmAlarmCounterProvider; private WMDDataSelectCreator dataSelectCreator; private WSLToadCalculator wslToadCalculator; + private WhistleSpeciesManager whistleSpeciesManager; public ConnectedRegionDataBlock(String dataName, WhistleMoanControl whistleMoanControl, WhistleToneConnectProcess parentProcess, int channelMap) { @@ -86,6 +89,14 @@ public class ConnectedRegionDataBlock extends AbstractWhistleDataBlock implement return fftParams; } + @Override + public DataBlockSpeciesManager getDatablockSpeciesManager() { + if (whistleSpeciesManager == null) { + whistleSpeciesManager = new WhistleSpeciesManager(this); + } + return whistleSpeciesManager; + } + diff --git a/src/whistlesAndMoans/species/WhistleSpeciesManager.java b/src/whistlesAndMoans/species/WhistleSpeciesManager.java new file mode 100644 index 00000000..8c4085b4 --- /dev/null +++ b/src/whistlesAndMoans/species/WhistleSpeciesManager.java @@ -0,0 +1,34 @@ +package whistlesAndMoans.species; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.DataBlockSpeciesTypes; +import tethys.species.ITISTypes; +import tethys.species.SpeciesMapItem; +import whistlesAndMoans.ConnectedRegionDataUnit; + +public class WhistleSpeciesManager extends DataBlockSpeciesManager { + + private String defaultName = "Tonal"; + + + public WhistleSpeciesManager(PamDataBlock dataBlock) { + super(dataBlock); + setDefaultDefaultSpecies(new SpeciesMapItem(180404, "Tonal", "Odontocete")); + } + + @Override + public DataBlockSpeciesTypes getSpeciesTypes() { + String spList[] = {"Unknown"}; + + DataBlockSpeciesTypes whistleSpeciesTypes = new DataBlockSpeciesTypes("Tonal", spList); + return whistleSpeciesTypes; + } + + @Override + public String getSpeciesString(ConnectedRegionDataUnit dataUnit) { + return defaultName; + } + +} From 07ced6ae6d8db75f95c7d3aec3a42eb8209fbe0f Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 29 Aug 2023 11:53:40 +0100 Subject: [PATCH 42/65] Species info Sort out of species info which hopefully now deals with all eventualities. --- .../species/RWSpeciesManager.java | 10 +- .../species/RWSpeciesTypes.java | 10 +- .../ClickBlockSpeciesManager.java | 12 +- src/pamMaths/PamVector.java | 2 +- ...sTypes.java => DataBlockSpeciesCodes.java} | 11 +- .../species/DataBlockSpeciesManager.java | 109 ++++++++++++++++-- src/tethys/species/DataBlockSpeciesMap.java | 9 +- src/tethys/species/SpeciesTest.java | 4 +- .../species/swing/DataBlockSpeciesPanel.java | 6 +- src/tethys/species/swing/SpeciesSubPanel.java | 5 +- .../species/WhistleSpeciesManager.java | 17 +-- 11 files changed, 150 insertions(+), 45 deletions(-) rename src/tethys/species/{DataBlockSpeciesTypes.java => DataBlockSpeciesCodes.java} (85%) diff --git a/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java b/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java index 86f0bf49..65c2afd7 100644 --- a/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java +++ b/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java @@ -3,7 +3,8 @@ package RightWhaleEdgeDetector.species; import PamguardMVC.PamDataBlock; import RightWhaleEdgeDetector.RWEDataUnit; import tethys.species.DataBlockSpeciesManager; -import tethys.species.DataBlockSpeciesTypes; +import tethys.species.DataBlockSpeciesCodes; +import tethys.species.SpeciesMapItem; public class RWSpeciesManager extends DataBlockSpeciesManager { @@ -11,15 +12,16 @@ public class RWSpeciesManager extends DataBlockSpeciesManager { public RWSpeciesManager(PamDataBlock dataBlock) { super(dataBlock); + setDefaultDefaultSpecies(new SpeciesMapItem(RWSpeciesTypes.eubalaena, RWSpeciesTypes.onlyType, RWSpeciesTypes.defaultName)); } @Override - public DataBlockSpeciesTypes getSpeciesTypes() { - return rwSpeciesTypes; + public DataBlockSpeciesCodes getSpeciesCodes() { + return null; } @Override - public String getSpeciesString(RWEDataUnit dataUnit) { + public String getSpeciesCode(RWEDataUnit dataUnit) { return RWSpeciesTypes.onlyType; } diff --git a/src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java b/src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java index 7a249a68..654fe24c 100644 --- a/src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java +++ b/src/RightWhaleEdgeDetector/species/RWSpeciesTypes.java @@ -1,15 +1,17 @@ package RightWhaleEdgeDetector.species; -import tethys.species.DataBlockSpeciesTypes; +import tethys.species.DataBlockSpeciesCodes; -public class RWSpeciesTypes extends DataBlockSpeciesTypes { +public class RWSpeciesTypes extends DataBlockSpeciesCodes { public static final String onlyType = "Up call"; - private static final int glacialis = 180536; + public static final int eubalaena = 180536; + + public static final String defaultName = "Right Whale"; public RWSpeciesTypes() { - super(glacialis, onlyType); + super(eubalaena, defaultName, onlyType); } } diff --git a/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java b/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java index 930ec8f0..89812008 100644 --- a/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java +++ b/src/clickDetector/ClickClassifiers/ClickBlockSpeciesManager.java @@ -4,7 +4,7 @@ import clickDetector.ClickControl; import clickDetector.ClickDataBlock; import clickDetector.ClickDetection; import tethys.species.DataBlockSpeciesManager; -import tethys.species.DataBlockSpeciesTypes; +import tethys.species.DataBlockSpeciesCodes; import tethys.species.ITISTypes; import tethys.species.SpeciesMapItem; @@ -16,11 +16,11 @@ public class ClickBlockSpeciesManager extends DataBlockSpeciesManager speciesNames; @@ -31,7 +32,7 @@ public class DataBlockSpeciesTypes { /** * @param defaultType */ - public DataBlockSpeciesTypes(String defaultType) { + public DataBlockSpeciesCodes(String defaultType) { this.defaultType = defaultType; } @@ -39,8 +40,10 @@ public class DataBlockSpeciesTypes { * @param itisDefault * @param defaultType */ - public DataBlockSpeciesTypes(int itisDefault, String defaultType) { + public DataBlockSpeciesCodes(int itisDefault, String defaultName, String defaultType) { this.itisDefault = itisDefault; + speciesNames = new ArrayList<>(); + speciesNames.add(defaultName); this.defaultType = defaultType; } @@ -48,7 +51,7 @@ public class DataBlockSpeciesTypes { * constructor to use with a array of String names. * @param speciesList */ - public DataBlockSpeciesTypes(String defaultType, String[] speciesList) { + public DataBlockSpeciesCodes(String defaultType, String[] speciesList) { this.defaultType = defaultType; if (speciesList == null) { speciesNames = new ArrayList<>(); diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java index 83582c61..bf94ffa9 100644 --- a/src/tethys/species/DataBlockSpeciesManager.java +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -1,5 +1,7 @@ package tethys.species; +import java.util.ArrayList; + import PamController.PamController; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -7,31 +9,86 @@ import tethys.species.swing.DataBlockSpeciesDialog; /** * Manage species conversion for a single datablock. + * + * there seem to be three types of manager:
+ * 1. Datablocks which have a totally free list of species codes, such as the click detector, or whistle classifier
+ * 1a. A slight variation on this is blocks which have a list and also a default for data units which aren't classified.
+ * 2. Datablocks which have a single type which may be unknown or partially known, but we want the possibility of overriding it. + * e.g. the whistle detector may default to odontocete, but may be overridden to a mystecete species.
+ * 3. Datablocks with no information, or where the list from (1.) is empty.

+ * In all cases, we need to handle this reasonably sensibly. The code list is always the start of this and must + * always return something, even if it's 'unknown'. + * * @author dg50 * */ abstract public class DataBlockSpeciesManager { + /** + * The serialised bit. Always exists (or should be created) even if there + * are no real species, via a defaultdefaultSpecies. + */ private DataBlockSpeciesMap datablockSpeciesMap; private PamDataBlock dataBlock; - private String defaultName = null; + /* + * Below are three ways of getting species codes. At least ONE of these + * must return something. + */ + + /** + * Object that contains a list of species codes. This may be fluid + * between configurations and may change during a session, e.g. through + * the addition of a new click type or changes to the whistle classifier settings. + * @return object containing a list of species types. + */ + public abstract DataBlockSpeciesCodes getSpeciesCodes(); + /** + * A default species code. Can be null, in which case it won't be used or + * can be set to something like 'Unknown' + */ + private String defaultSpeciesCode = null; + + /** + * For use in detectors that have a strong default species, but no + * real list of different species codes. Can be left null. + */ private SpeciesMapItem defaultDefaultSpecies = null; - public abstract DataBlockSpeciesTypes getSpeciesTypes(); - public abstract String getSpeciesString(T dataUnit); + /** + * Gets a species string for a specific data unit, This is abstracted + * since different detectors store this in non standard ways. The result of + * this should be within the set provided by getSpeciesCodes() which can then + * be used in the DataBlockSpeciesMap to look up an itis code. + * @param dataUnit + * @return A species code for a specific data unit. May be null (e.g. for an unclassified click) + */ + public abstract String getSpeciesCode(T dataUnit); public DataBlockSpeciesManager(PamDataBlock dataBlock) { super(); this.dataBlock = dataBlock; datablockSpeciesMap = SpeciesMapManager.getInstance().getSpeciesMap(dataBlock); +// datablockSpeciesMap.clearMap(); + clearMapNulls(); + checkMapDefault(); } + /** + * Clear up some old maps which have got a null null default. + */ + private void clearMapNulls() { + SpeciesMapItem nullVal = datablockSpeciesMap.getItem(null); + if (nullVal == null) { + datablockSpeciesMap.removeItem(null); + } + } + public SpeciesMapItem getSpeciesItem(T dataUnit) { - String speciesString = getSpeciesString(dataUnit); + String speciesString = getSpeciesCode(dataUnit); if (speciesString == null) { return getDefaultDefaultSpecies(); } @@ -42,6 +99,30 @@ abstract public class DataBlockSpeciesManager { return speciesMap.getItem(speciesString); } + /** + * Get all PAMGuard species codes, which may come from the DataBlockSpeciesCodes + * object, or the defaultSpeciesCode, or the defaultDefaultSpecies name. Ideally, + * at least one of these should have something, or we'll stick in an "Unknown" + * @return + */ + public ArrayList getAllSpeciesCodes() { + ArrayList allCodes = new ArrayList(); + if (defaultSpeciesCode != null) { + allCodes.add(defaultSpeciesCode); + } + if (defaultDefaultSpecies != null) { + allCodes.add(defaultDefaultSpecies.getPamguardName()); + } + DataBlockSpeciesCodes codeList = getSpeciesCodes(); + if (codeList != null) { + allCodes.addAll(codeList.getSpeciesNames()); + } + if (allCodes.size() == 0) { + allCodes.add("Unknown"); + } + return allCodes; + } + public DataBlockSpeciesMap getDatablockSpeciesMap() { if (datablockSpeciesMap == null) { datablockSpeciesMap = new DataBlockSpeciesMap(); @@ -51,9 +132,16 @@ abstract public class DataBlockSpeciesManager { } private void checkMapDefault() { - SpeciesMapItem defaultItem = datablockSpeciesMap.getItem(getDefaultName()); +// SpeciesMapItem defaultItem = datablockSpeciesMap.getItem(getDefaultSpeciesCode()); +// if (defaultItem == null) { +// datablockSpeciesMap.putItem(getDefaultSpeciesCode(), getDefaultDefaultSpecies()); +// } + if (defaultDefaultSpecies == null) { + return; + } + SpeciesMapItem defaultItem = datablockSpeciesMap.getItem(defaultDefaultSpecies.getPamguardName()); if (defaultItem == null) { - datablockSpeciesMap.putItem(getDefaultName(), getDefaultDefaultSpecies()); + datablockSpeciesMap.putItem(defaultDefaultSpecies.getPamguardName(), defaultDefaultSpecies); } } @@ -84,19 +172,20 @@ abstract public class DataBlockSpeciesManager { */ public void setDefaultDefaultSpecies(SpeciesMapItem defaultDefaultSpecies) { this.defaultDefaultSpecies = defaultDefaultSpecies; + checkMapDefault(); } /** * @return the defaultName */ - public String getDefaultName() { - return defaultName; + public String getDefaultSpeciesCode() { + return defaultSpeciesCode; } /** * @param defaultName the defaultName to set */ - public void setDefaultName(String defaultName) { - this.defaultName = defaultName; + public void setDefaultSpeciesCode(String defaultName) { + this.defaultSpeciesCode = defaultName; } } diff --git a/src/tethys/species/DataBlockSpeciesMap.java b/src/tethys/species/DataBlockSpeciesMap.java index 6d6b6857..a704d9ac 100644 --- a/src/tethys/species/DataBlockSpeciesMap.java +++ b/src/tethys/species/DataBlockSpeciesMap.java @@ -4,7 +4,10 @@ import java.io.Serializable; import java.util.HashMap; /** - * Species map for a specified data block + * Species map for a specified data block

+ * This is the bit that can be serialised, and is primarily (only) a hash table of + * SpeciesMapItems which relate String species codes for a detector to more + * details itis and scientific name information. * @author dg50 * */ @@ -33,5 +36,9 @@ public class DataBlockSpeciesMap implements Serializable { public void removeItem(String key) { speciesTable.remove(key); } + + public void clearMap() { + speciesTable.clear(); + } } diff --git a/src/tethys/species/SpeciesTest.java b/src/tethys/species/SpeciesTest.java index b476b3ba..13403f8d 100644 --- a/src/tethys/species/SpeciesTest.java +++ b/src/tethys/species/SpeciesTest.java @@ -129,9 +129,9 @@ public class SpeciesTest { private void runJson() { // String jQ = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"DCLDE2022\"],\"optype\":\"binary\"}],\"enclose\":1}"; // String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"180488\"],\"optype\":\"binary\"}],\"enclose\":1}"; - String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"180488\"],\"optype\":\"binary\"}],\"enclose\":1}"; +// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/tsn\",\"180488\"],\"optype\":\"binary\"}],\"enclose\":1}"; // String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"=\",\"operands\":[\"ranks/rank/completename\",\"Physeter macrocephalus\"],\"optype\":\"binary\"}],\"enclose\":1}"; -// String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"dbxml:contains\",\"operands\":[\"ranks/rank/completename\",\"Sperm\"],\"optype\":\"function\"}],\"enclose\":1}"; + String jQ = "{\"return\":[\"ranks/rank\"],\"select\":[{\"op\":\"dbxml:contains\",\"operands\":[\"ranks/rank/completename\",\"Sperm\"],\"optype\":\"function\"}],\"enclose\":1}"; System.out.println(jQ); diff --git a/src/tethys/species/swing/DataBlockSpeciesPanel.java b/src/tethys/species/swing/DataBlockSpeciesPanel.java index b7b69370..e2649f74 100644 --- a/src/tethys/species/swing/DataBlockSpeciesPanel.java +++ b/src/tethys/species/swing/DataBlockSpeciesPanel.java @@ -12,7 +12,7 @@ import PamView.dialog.PamDialogPanel; import PamguardMVC.PamDataBlock; import tethys.species.DataBlockSpeciesManager; import tethys.species.DataBlockSpeciesMap; -import tethys.species.DataBlockSpeciesTypes; +import tethys.species.DataBlockSpeciesCodes; import tethys.species.SpeciesMapItem; public class DataBlockSpeciesPanel implements PamDialogPanel { @@ -46,8 +46,8 @@ public class DataBlockSpeciesPanel implements PamDialogPanel { subPanels.clear(); DataBlockSpeciesManager speciesManager = dataBlock.getDatablockSpeciesManager(); - DataBlockSpeciesTypes speciesTypes = speciesManager.getSpeciesTypes(); - ArrayList speciesNames = speciesTypes.getSpeciesNames(); +// DataBlockSpeciesCodes speciesTypes = speciesManager.getSpeciesCodes(); + ArrayList speciesNames = speciesManager.getAllSpeciesCodes(); DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); for (String aSpecies : speciesNames) { SpeciesSubPanel subPanel = new SpeciesSubPanel(aSpecies); diff --git a/src/tethys/species/swing/SpeciesSubPanel.java b/src/tethys/species/swing/SpeciesSubPanel.java index 0233cbc9..548b23a1 100644 --- a/src/tethys/species/swing/SpeciesSubPanel.java +++ b/src/tethys/species/swing/SpeciesSubPanel.java @@ -139,7 +139,7 @@ public class SpeciesSubPanel { tsn = Integer.valueOf(itisCode.getText()); } catch (NumberFormatException e) { - PamDialog.showWarning(PamController.getMainFrame(), pamguardName.getText(), "You must specified an ITIS taxanomic code"); + PamDialog.showWarning(PamController.getMainFrame(), pamguardName.getText(), "You must specify an ITIS taxanomic code"); return null; } latin = latinName.getText(); @@ -149,7 +149,8 @@ public class SpeciesSubPanel { PamDialog.showWarning(PamController.getMainFrame(), pamguardName.getText(), "You must specified a call type"); return null; } - return new SpeciesMapItem(tsn, callType, pamguardName.getText(), latin, vernacular); + String pamName = pamguardName.getText().replace("\"", ""); + return new SpeciesMapItem(tsn, callType, pamName, latin, vernacular); } } diff --git a/src/whistlesAndMoans/species/WhistleSpeciesManager.java b/src/whistlesAndMoans/species/WhistleSpeciesManager.java index 8c4085b4..78827730 100644 --- a/src/whistlesAndMoans/species/WhistleSpeciesManager.java +++ b/src/whistlesAndMoans/species/WhistleSpeciesManager.java @@ -3,7 +3,7 @@ package whistlesAndMoans.species; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import tethys.species.DataBlockSpeciesManager; -import tethys.species.DataBlockSpeciesTypes; +import tethys.species.DataBlockSpeciesCodes; import tethys.species.ITISTypes; import tethys.species.SpeciesMapItem; import whistlesAndMoans.ConnectedRegionDataUnit; @@ -15,19 +15,20 @@ public class WhistleSpeciesManager extends DataBlockSpeciesManager dataBlock) { super(dataBlock); - setDefaultDefaultSpecies(new SpeciesMapItem(180404, "Tonal", "Odontocete")); + setDefaultDefaultSpecies(new SpeciesMapItem(180404, "Tonal", "Tonal")); } @Override - public DataBlockSpeciesTypes getSpeciesTypes() { - String spList[] = {"Unknown"}; - - DataBlockSpeciesTypes whistleSpeciesTypes = new DataBlockSpeciesTypes("Tonal", spList); - return whistleSpeciesTypes; + public DataBlockSpeciesCodes getSpeciesCodes() { +// String spList[] = {"Unknown"}; +// +// DataBlockSpeciesCodes whistleSpeciesTypes = new DataBlockSpeciesCodes("Tonal", spList); +// return whistleSpeciesTypes; + return null; } @Override - public String getSpeciesString(ConnectedRegionDataUnit dataUnit) { + public String getSpeciesCode(ConnectedRegionDataUnit dataUnit) { return defaultName; } From 3688719e25b89f41add4182fdd6ef1fcf4a16660 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 31 Aug 2023 17:06:51 +0100 Subject: [PATCH 43/65] Working parameter output Only just and not complete, but working. --- .../output/xml/PamguardXMLWriter.java | 8 + src/PamView/dialog/warn/WarnOnce.java | 1 + .../species/RWSpeciesManager.java | 2 +- src/tethys/dbxml/DBXMLConnect.java | 28 +++ src/tethys/detection/DetectionsHandler.java | 66 +---- src/tethys/pamdata/AutoTethysProvider.java | 170 ++++++++----- src/tethys/pamdata/TethysParameterPacker.java | 233 ++++++++++++++++++ .../species/DataBlockSpeciesManager.java | 8 +- .../swing/documents/TethysDocumentTable.java | 16 +- 9 files changed, 404 insertions(+), 128 deletions(-) create mode 100644 src/tethys/pamdata/TethysParameterPacker.java diff --git a/src/PamController/settings/output/xml/PamguardXMLWriter.java b/src/PamController/settings/output/xml/PamguardXMLWriter.java index af35b51e..58cf1bd7 100644 --- a/src/PamController/settings/output/xml/PamguardXMLWriter.java +++ b/src/PamController/settings/output/xml/PamguardXMLWriter.java @@ -903,6 +903,14 @@ public class PamguardXMLWriter implements PamSettings { return doc; } + /** + * Is this element a writable type ? Basically, this means + * that it's a primitive of some sort. Otherwise it's + * probably an object and may even be a list in which case + * it will need treating differently. + * @param clazz + * @return + */ public static boolean isWritableType(Class clazz) { if (clazz.isEnum()) return true; diff --git a/src/PamView/dialog/warn/WarnOnce.java b/src/PamView/dialog/warn/WarnOnce.java index 93ed4618..cd7ad08d 100644 --- a/src/PamView/dialog/warn/WarnOnce.java +++ b/src/PamView/dialog/warn/WarnOnce.java @@ -48,6 +48,7 @@ public class WarnOnce implements PamSettings { int ans = showWarning(parent, "Warning Messages", "Show all PAMGuard warning messages", WarnOnce.OK_CANCEL_OPTION); if (ans == WarnOnce.CANCEL_OPTION) return; singleInstance.warnOnceList.clearList(); + singleInstance.showThisSess.clear(); } @Override diff --git a/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java b/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java index 65c2afd7..e906bb5d 100644 --- a/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java +++ b/src/RightWhaleEdgeDetector/species/RWSpeciesManager.java @@ -22,7 +22,7 @@ public class RWSpeciesManager extends DataBlockSpeciesManager { @Override public String getSpeciesCode(RWEDataUnit dataUnit) { - return RWSpeciesTypes.onlyType; + return RWSpeciesTypes.defaultName; } } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index dd45b6d5..e6c603a5 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -239,6 +239,34 @@ An error will throw an exception. } return true; } + + /** + * Remove a document based on a collection name and a cdocument Id. + * @param collection + * @param docId + * @return + * @throws TethysException + */ + public boolean removeDocument(String collection, String docId) throws TethysException { + try { + Object result = jerseyClient.removeDocument(collection, docId ); + /** + * Return from a sucessful delete is something like + * + deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId++, recordPeriod); + + ['ECoastNARW0'] + +An error will throw an exception. + */ + } + catch (Exception e) { +// System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); + String msg = String.format("Error deleting %s:%s", collection, docId); + throw new TethysException(msg, e.getLocalizedMessage()); + } + return true; + } /** * check the return string from importFiles and if it's an diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 706dd7fc..7598f524 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -491,71 +491,15 @@ public class DetectionsHandler { // dataSource.setEnsembleId(""); ToDo detections.setDataSource(dataSource); AlgorithmType algorithm = detections.getAlgorithm(); - algorithm.setMethod(getMethodString(dataBlock)); - algorithm.setSoftware(getSoftwareString(dataBlock)); - algorithm.setVersion(getVersionString(dataBlock)); TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); if (dataProvider != null) { -// Parameters parameters = dataProvider.getAlgorithmParameters(); - Parameters parameters = algorithm.getParameters(); - if (parameters == null) { - parameters = new Parameters(); - algorithm.setParameters(parameters); - } - List paramList = parameters.getAny(); -// algorithm.setParameters(parameters); - // make a really simple parameter or two to see if it works with simpler xml than PG generates. - /** - * Parameters should look something like - * - - Analyst detections - Triton - unknown - - 0.75 - 0.0 - 5000.0 - 30.0 - - - */ - // this works. Can look at the source to see how it's done. - // may have fun making this work for more complex structures. - try { - Helper helper = new Helper(); - helper.AddAnyElement(paramList, "Threshold", "3.5"); - /* - * and see Matlab code for dbStruct2DOM for more complex structures - * This looks like it may be possible to rewrite my functions for - * writing structures to XML using the helper.AddAnyElement function as - * an example and I should be able to output my complex structures. - */ - } catch (JAXBException | ParserConfigurationException e) { - e.printStackTrace(); - } - - Document doc = XMLUtils.createBlankDoc(); -// PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); - Element dummyEl = doc.createElement("SomeParam"); -//// dummyEl.setNodeValue("nothing"); - dummyEl.setTextContent("3.0"); - /* - * xsl:stylesheet version=\"1.0\" \n" - + " xmlns:xsl=http://www.w3.org/1999/XSL/Transform\n" - + " xmlns:ns0=http://mydata.com/H2H/Automation\n" - */ - dummyEl.setAttribute("xmlns:ns0", TethysControl.xmlNameSpace); -// dummyEl.set -// paramList.add(dummyEl); - -// Element mainEl = doc.createElement("CONFIG"); -// mainEl.appendChild(dummyEl); -// doc.appendChild(mainEl); -// System.out.println(pamXMLWriter.getAsString(doc)); + algorithm = dataProvider.getAlgorithm(); +// detections.setAlgorithm(algorithm); } - + algorithm.setMethod(getMethodString(dataBlock)); + algorithm.setSoftware(getSoftwareString(dataBlock)); + algorithm.setVersion(getVersionString(dataBlock)); List supSoft = algorithm.getSupportSoftware(); SupportSoftware supportSoft = new SupportSoftware(); diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index ebe498fa..26bf3807 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -31,7 +31,12 @@ import tethys.TethysControl; import tethys.TethysTimeFuncs; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.ITISTypes; +import tethys.species.SpeciesMapItem; import whistleClassifier.WhistleContour; + +import javax.xml.bind.JAXBException; import javax.xml.transform.*; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; @@ -104,10 +109,13 @@ public class AutoTethysProvider implements TethysDataProvider { // TODO Auto-generated catch block e.printStackTrace(); } - algorithm.setMethod(this.getAlgorithmMethod()); - algorithm.setSoftware("PAMGuard"); - algorithm.setVersion(PamguardVersionInfo.version); - //algorithm.setParameters(this.getAlgorithmParameters()); +// algorithm.setMethod(this.getAlgorithmMethod()); +// algorithm.setSoftware("PAMGuard"); +// algorithm.setVersion(PamguardVersionInfo.version); + Parameters algoParameters = this.getAlgorithmParameters(); + if (algoParameters != null) { + algorithm.setParameters(algoParameters); + } return algorithm; } @@ -120,66 +128,80 @@ public class AutoTethysProvider implements TethysDataProvider { PamSettings pamSettings = (PamSettings) pamControlledUnit; Parameters parameters = new Parameters(); List paramList = parameters.getAny(); - Document doc = XMLUtils.createBlankDoc(); - PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); - Element dummyEl = doc.createElement("MODULES"); - doc.appendChild(dummyEl); - PamSettings[] settingsObjs = getSettingsObjects(); - if (settingsObjs == null) { + Object settings = pamSettings.getSettingsReference(); + TethysParameterPacker paramPacker = null; + try { + paramPacker = new TethysParameterPacker(); + } catch (JAXBException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + List genList = paramPacker.packParameters(settings); + if (genList == null || genList.size() == 0) { return null; } -// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); - Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); - if (settingsEl == null) { - return null; - } - -// settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); - - - dummyEl.appendChild(settingsEl); - NodeList childs = settingsEl.getChildNodes(); - for (int i = 0; i < childs.getLength(); i++) { - Node el = childs.item(i); - // System.out.println(el.getNodeName()); - if (el instanceof Element) { - paramList.add((Element) el); - } - } - - // Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); - // String moduleXML = null; - if (doc != null) { - // this string should be XML of all the settings for the module controlling this - // datablock. - // moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml - // System.out.printf("Module settings for datablock %s are:\n", moduleXML); - // System.out.println(moduleXML); - // Element pamguard = doc.get("PAMGUARD"); - // Element modules = (Element) pamguard.getElementsByTagName("MODULES"); - // doc.get - // NodeList childs = doc.getChildNodes(); - // for (int i = 0; i < childs.getLength(); i++) { - // Node el = childs.item(i); - // System.out.println(el.getNodeName()); - // if (el instanceof Element) { - // paramList.add((Element) el); - // } - // } - // String moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml - // System.out.printf("Module settings for datablock %s are:\n%s", this.pamDataBlock.getDataName(), moduleXML); - } - - // // try the old say - // Document doc2 = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); - // String moduleXML = null; - // if (doc2 != null) { - // // this string should be XML of all the settings for the module controlling this - // // datablock. - // moduleXML = pamXMLWriter.getAsString(doc2, true); // change to false to get smaller xml - // System.out.printf("Module settings for datablock %s are:\n%s", pamDataBlock.getDataName(),moduleXML); - // } - // + paramList.addAll(genList); + +// Document doc = XMLUtils.createBlankDoc(); +// PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); +// Element dummyEl = doc.createElement("MODULES"); +// doc.appendChild(dummyEl); +// PamSettings[] settingsObjs = getSettingsObjects(); +// if (settingsObjs == null) { +// return null; +// } +//// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); +// Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); +// if (settingsEl == null) { +// return null; +// } +// +//// settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); +// +// +// dummyEl.appendChild(settingsEl); +// NodeList childs = settingsEl.getChildNodes(); +// for (int i = 0; i < childs.getLength(); i++) { +// Node el = childs.item(i); +// // System.out.println(el.getNodeName()); +// if (el instanceof Element) { +// paramList.add((Element) el); +// } +// } +// +// // Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); +// // String moduleXML = null; +// if (doc != null) { +// // this string should be XML of all the settings for the module controlling this +// // datablock. +// // moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml +// // System.out.printf("Module settings for datablock %s are:\n", moduleXML); +// // System.out.println(moduleXML); +// // Element pamguard = doc.get("PAMGUARD"); +// // Element modules = (Element) pamguard.getElementsByTagName("MODULES"); +// // doc.get +// // NodeList childs = doc.getChildNodes(); +// // for (int i = 0; i < childs.getLength(); i++) { +// // Node el = childs.item(i); +// // System.out.println(el.getNodeName()); +// // if (el instanceof Element) { +// // paramList.add((Element) el); +// // } +// // } +// // String moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml +// // System.out.printf("Module settings for datablock %s are:\n%s", this.pamDataBlock.getDataName(), moduleXML); +// } +// +// // // try the old say +// // Document doc2 = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); +// // String moduleXML = null; +// // if (doc2 != null) { +// // // this string should be XML of all the settings for the module controlling this +// // // datablock. +// // moduleXML = pamXMLWriter.getAsString(doc2, true); // change to false to get smaller xml +// // System.out.printf("Module settings for datablock %s are:\n%s", pamDataBlock.getDataName(),moduleXML); +// // } +// // return parameters; } @@ -275,7 +297,29 @@ public class AutoTethysProvider implements TethysDataProvider { Detection detection = new Detection(); detection.setStart(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getTimeMilliseconds())); detection.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getEndTimeInMilliseconds())); - detection.setSpeciesId(getSpeciesIdType()); + + DataBlockSpeciesManager speciesManager = pamDataBlock.getDatablockSpeciesManager(); + SpeciesMapItem speciesItem = null; + if (speciesManager != null) { + speciesItem = speciesManager.getSpeciesItem(dataUnit); +// detection.setSpeciesId(new Species); +// detection.setSpeciesId(getSpeciesIdType()); + } + else { + } + SpeciesIDType species = new SpeciesIDType(); + List calls = detection.getCall(); + if (speciesItem != null) { + species.setValue(BigInteger.valueOf(speciesItem.getItisCode())); + if (speciesItem.getCallType() != null) { + calls.add(speciesItem.getCallType()); + } + } + else { + species.setValue(BigInteger.valueOf(ITISTypes.ANTHROPOGENIC)); + calls.add("unknown"); + } + detection.setSpeciesId(species); /* * NOTE: I use channel bitmaps throughout since detections are often made on multiple channels. */ diff --git a/src/tethys/pamdata/TethysParameterPacker.java b/src/tethys/pamdata/TethysParameterPacker.java new file mode 100644 index 00000000..a6ad0e01 --- /dev/null +++ b/src/tethys/pamdata/TethysParameterPacker.java @@ -0,0 +1,233 @@ +package tethys.pamdata; + +import java.io.File; +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.namespace.QName; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.dom.DOMResult; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import PamController.settings.output.xml.PamguardXMLWriter; +import PamModel.parametermanager.ManagedParameters; +import PamModel.parametermanager.PamParameterData; +import PamModel.parametermanager.PamParameterSet; +import nilus.MarshalXML; + +/** + * Functions to pack up a PAMGuard parameters object into the correct format + * for Tethys. This is very similar to functions in PamguardXMLWriter.writeUnitSettings + * but seems to have enough differences that it needs totally rewriting for Tethys. + * @author dg50 + * + */ +public class TethysParameterPacker { + +// /** +// * Parameters should look something like below. however, only packing them with a long +// schema name seems to work. +// * +// +//Analyst detections +//Triton +//unknown +// +//0.75 +//0.0 +//5000.0 +//30.0 +// +// + /* + * +// // this works. Can look at the source to see how it's done. +// // may have fun making this work for more complex structures. +// try { +// Helper helper = new Helper(); +// helper.AddAnyElement(paramList, "Threshold", "3.5"); +// +// * and see Matlab code for dbStruct2DOM at C:\Users\dg50\source\repos\TethysMatlab\db +// * for more complex structures +// * This looks like it may be possible to rewrite my functions for +// * writing structures to XML using the helper.AddAnyElement function as +// * an example and I should be able to output my complex structures. +// +// } catch (JAXBException | ParserConfigurationException e) { +// e.printStackTrace(); +// } + */ + + private MarshalXML marshaller; + + /** + * @throws JAXBException + * + */ + public TethysParameterPacker() throws JAXBException { + super(); + try { + marshaller = new MarshalXML(); + } catch (JAXBException e) { + } + } + + + public List packParameters(Object data) { + List elList = new ArrayList(); + + ArrayList objectHierarchy = new ArrayList<>(); + + PamParameterSet parameterSet; + if (data instanceof ManagedParameters) { + parameterSet = ((ManagedParameters) data).getParameterSet(); + } + else { + int exclusions = 0; + // if (writerSettings.includeConstants == false) { + exclusions = Modifier.STATIC | Modifier.FINAL; + // } + parameterSet = PamParameterSet.autoGenerate(data, exclusions); + } + if (parameterSet == null) { + return null; + } +// Document document = null; +// try { +// document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); +// } catch (ParserConfigurationException e1) { +// e1.printStackTrace(); +// } + QName qname = new QName(MarshalXML.schema, "parameters", "ty"); + JAXBElement jaxel = new JAXBElement( + qname, String.class, parameterSet.getParentObject().getClass().getCanonicalName()); + Document doc = null; + try { + doc = marshaller.marshalToDOM(jaxel); + } catch (JAXBException | ParserConfigurationException e1) { + e1.printStackTrace(); + } + Element el = doc.getDocumentElement(); + + for (PamParameterData pamParam : parameterSet.getParameterCollection()) { + try { + Object paramData = pamParam.getData(); + boolean ok = createElement(doc, el, paramData, pamParam, objectHierarchy); +// if (newEl != null) { +// elList.add(newEl); +// } + } catch (IllegalArgumentException | IllegalAccessException e) { + e.printStackTrace(); + } + + } + elList.add(el); + return elList; + } + + private boolean createElement(Document document, Element parentEl, Object paramData, PamParameterData pamParam, ArrayList objectHierarchy) { + Class javaClass = paramData.getClass(); + if (PamguardXMLWriter.isWritableType(javaClass)) { + String name = pamParam.getFieldName(); + String value = paramData.toString(); + Element el = document.createElement(name); +// el.setNodeValue(value); + el.setTextContent(value); + parentEl.appendChild(el); + + +// QName qname = new QName(MarshalXML.schema, name, "ty"); +// JAXBElement jaxel = new JAXBElement( +// qname, String.class, value); +// +// +// try { +// jxbm.marshal(jaxel, dom); +// } catch (JAXBException e) { +// e.printStackTrace(); +// } +// Document doc = null; +// try { +// doc = marshaller.marshalToDOM(jaxel); +// } catch (JAXBException e) { +// e.printStackTrace(); +// } catch (ParserConfigurationException e) { +// e.printStackTrace(); +// } +// Element el = doc.getDocumentElement(); +// return el; + return true; + } + if (javaClass.isArray()) { + return writeArray(document, parentEl, paramData, pamParam, objectHierarchy); + + } + /* + * + if (javaClass.isArray()) { + return writeArray(doc, el, data, pamParam, objectHierarchy); + } + if (List.class.isAssignableFrom(javaClass)){ + return writeList(doc, el, data, pamParam, objectHierarchy); + } + if (Map.class.isAssignableFrom(javaClass)){ + return writeMap(doc, el, data, pamParam, objectHierarchy); + } + if (File.class.isAssignableFrom(javaClass)) { + return writeFile(doc, el, data, pamParam); + } + + else { + Element e = makeElement(doc, pamParam.getFieldName(), data.getClass().getName()); + el.appendChild(e); + writeObjectData(doc, e, data, objectHierarchy); + return e; + } + */ + return false; + } + + + private boolean writeArray(Document document, Element parentEl, Object paramData, PamParameterData pamParam, + ArrayList objectHierarchy) { + if (paramData.getClass().isArray() == false) { + return false; + } + String name = pamParam.getFieldName(); + Element el = document.createElement(name); + parentEl.appendChild(el); + int n = Array.getLength(paramData); + boolean ok = true; + for (int i = 0; i < n; i++) { + Object arrayEl = Array.get(paramData, i); + ok &= createElement(document, el, arrayEl, pamParam, objectHierarchy); + } + // TODO Auto-generated method stub + return ok; + } + + +// private Element writeArray(PamParameterData pamParam, ArrayList objectHierarchy) { +// QName qname = new QName(MarshalXML.schema, pamParam.getFieldName(), "ty"); +// JAXBElement jaxel = new JAXBElement( +// qname, String.class, value); +// +// Document doc = null; +// try { +// doc = marshaller.marshalToDOM(jaxel); +// } catch (JAXBException e) { +// e.printStackTrace(); +// } catch (ParserConfigurationException e) { +// e.printStackTrace(); +// } +// return null; +// } +} diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java index bf94ffa9..6b08d0ce 100644 --- a/src/tethys/species/DataBlockSpeciesManager.java +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -72,6 +72,9 @@ abstract public class DataBlockSpeciesManager { super(); this.dataBlock = dataBlock; datablockSpeciesMap = SpeciesMapManager.getInstance().getSpeciesMap(dataBlock); + if (datablockSpeciesMap == null) { + datablockSpeciesMap = new DataBlockSpeciesMap(); + } // datablockSpeciesMap.clearMap(); clearMapNulls(); checkMapDefault(); @@ -81,6 +84,9 @@ abstract public class DataBlockSpeciesManager { * Clear up some old maps which have got a null null default. */ private void clearMapNulls() { + if (datablockSpeciesMap == null) { + return; + } SpeciesMapItem nullVal = datablockSpeciesMap.getItem(null); if (nullVal == null) { datablockSpeciesMap.removeItem(null); @@ -136,7 +142,7 @@ abstract public class DataBlockSpeciesManager { // if (defaultItem == null) { // datablockSpeciesMap.putItem(getDefaultSpeciesCode(), getDefaultDefaultSpecies()); // } - if (defaultDefaultSpecies == null) { + if (defaultDefaultSpecies == null || datablockSpeciesMap == null) { return; } SpeciesMapItem defaultItem = datablockSpeciesMap.getItem(defaultDefaultSpecies.getPamguardName()); diff --git a/src/tethys/swing/documents/TethysDocumentTable.java b/src/tethys/swing/documents/TethysDocumentTable.java index e1f8f623..dcdb6a0a 100644 --- a/src/tethys/swing/documents/TethysDocumentTable.java +++ b/src/tethys/swing/documents/TethysDocumentTable.java @@ -16,9 +16,12 @@ import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.AbstractTableModel; +import PamController.PamController; import PamView.dialog.PamDialogPanel; +import PamView.dialog.warn.WarnOnce; import PamView.tables.SwingTableColumnWidths; import tethys.TethysControl; +import tethys.dbxml.TethysException; /** * Table view of a collection of Tethys documents. @@ -118,8 +121,17 @@ public class TethysDocumentTable implements PamDialogPanel { } private void deleteDocument(String docName) { - // TODO Auto-generated method stub - + int ans = WarnOnce.showNamedWarning("deletedoc"+collectionName, PamController.getMainFrame(), "Delete document", + "Are you sure you want to delete the document " + docName, WarnOnce.OK_CANCEL_OPTION); + if (ans == WarnOnce.OK_OPTION) { + try { + tethysControl.getDbxmlConnect().removeDocument(collectionName, docName); + } catch (TethysException e) { + System.out.println("Failed to delete " + docName); + System.out.println(e.getMessage()); + } + } + updateTableData(); } private class TableModel extends AbstractTableModel { From fb00a757cd4e15b70dd87cdae759adea8b50f65b Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 7 Sep 2023 16:58:38 +0100 Subject: [PATCH 44/65] Data export A few fixes in Detections export. --- src/tethys/dbxml/DBXMLConnect.java | 3 +- src/tethys/detection/DetectionsHandler.java | 27 ++++------- src/tethys/pamdata/AutoTethysProvider.java | 47 ++++++++++++++++++- src/tethys/pamdata/TethysDataProvider.java | 19 ++++++++ src/tethys/pamdata/TethysParameterPacker.java | 26 +++++++--- .../swing/DatablockDetectionsPanel.java | 21 ++++++++- 6 files changed, 115 insertions(+), 28 deletions(-) diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index e6c603a5..7af700d5 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -163,8 +163,9 @@ public class DBXMLConnect { * Quite hard to see much common structure in this, so just look for * two words, and */ - boolean error = importReturn.contains(""); + boolean error = importReturn.contains(""); +// error = !success; might be a better options. if (error) { throw new TethysException("Error posting to Tethys", importReturn); } diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 7598f524..bc5992db 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -262,30 +262,19 @@ public class DetectionsHandler { // return false; // } - private DetectionEffort getDetectorEffort(Deployment deployment, long effortStart, long effortEnd) { + private DetectionEffort getDetectorEffort(PDeployment pDeployment, PamDataBlock dataBlock, StreamExportParams exportParams) { DetectionEffort effort = new DetectionEffort(); + Deployment deployment = pDeployment.deployment; + Long effortStart = pDeployment.getAudioStart(); + Long effortEnd = pDeployment.getAudioEnd(); effort.setStart(TethysTimeFuncs.xmlGregCalFromMillis(effortStart)); effort.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(effortEnd)); // effort.set // no setter for DetectionEffortKind List effortKinds = effort.getKind(); - DetectionEffortKind kind = new DetectionEffortKind(); - try { - nilus.Helper.createRequiredElements(kind); - } catch (IllegalArgumentException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IllegalAccessException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (InstantiationException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } + + TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); + dataProvider.getEffortKinds(pDeployment, effortKinds, exportParams); - kind.getSpeciesId().setValue(BigInteger.valueOf(180537)); - kind.getGranularity().setValue(nilus.GranularityEnumType.CALL); - - effortKinds.add(kind); return effort; } @@ -508,7 +497,7 @@ public class DetectionsHandler { supSoft.add(supportSoft); detections.setAlgorithm(algorithm); detections.setUserId("Unknown user"); - detections.setEffort(getDetectorEffort(deployment.deployment, deployment.getAudioStart(), deployment.getAudioEnd())); + detections.setEffort(getDetectorEffort(deployment, dataBlock, exportParams)); return detections; } diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 26bf3807..ba530ce0 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -1,6 +1,7 @@ package tethys.pamdata; import java.math.BigInteger; +import java.util.ArrayList; import java.util.List; import org.w3c.dom.Document; @@ -26,12 +27,15 @@ import nilus.AlgorithmType.Parameters; import nilus.Deployment; import nilus.DescriptionType; import nilus.Detection; +import nilus.DetectionEffortKind; import nilus.SpeciesIDType; import tethys.TethysControl; import tethys.TethysTimeFuncs; +import tethys.niluswraps.PDeployment; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.species.DataBlockSpeciesManager; +import tethys.species.DataBlockSpeciesMap; import tethys.species.ITISTypes; import tethys.species.SpeciesMapItem; import whistleClassifier.WhistleContour; @@ -136,7 +140,7 @@ public class AutoTethysProvider implements TethysDataProvider { // TODO Auto-generated catch block e.printStackTrace(); } - List genList = paramPacker.packParameters(settings); + List genList = paramPacker.packParameters(pamControlledUnit); if (genList == null || genList.size() == 0) { return null; } @@ -389,4 +393,45 @@ public class AutoTethysProvider implements TethysDataProvider { return species; } + @Override + public void getEffortKinds(PDeployment pDeployment, List effortKinds, StreamExportParams exportParams) { + + DataBlockSpeciesManager speciesManager = pamDataBlock.getDatablockSpeciesManager(); + if (speciesManager == null) { + return; + } + DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); + ArrayList speciesCodes = speciesManager.getAllSpeciesCodes(); + if (speciesCodes == null || speciesMap == null) { + return; + } + for (String speciesCode : speciesCodes) { + + SpeciesMapItem mapItem = speciesMap.getItem(speciesCode); + + DetectionEffortKind kind = new DetectionEffortKind(); + try { + nilus.Helper.createRequiredElements(kind); + } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IllegalAccessException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + kind.getSpeciesId().setValue(BigInteger.valueOf(mapItem.getItisCode())); + kind.getGranularity().setValue(exportParams.granularity); + kind.setCall(mapItem.getCallType()); + + + effortKinds.add(kind); + + } + + } + } diff --git a/src/tethys/pamdata/TethysDataProvider.java b/src/tethys/pamdata/TethysDataProvider.java index efbd47e5..adb5cbbc 100644 --- a/src/tethys/pamdata/TethysDataProvider.java +++ b/src/tethys/pamdata/TethysDataProvider.java @@ -1,11 +1,15 @@ package tethys.pamdata; +import java.util.List; + import PamguardMVC.PamDataUnit; import nilus.AlgorithmType; import nilus.AlgorithmType.Parameters; import nilus.Deployment; import nilus.DescriptionType; import nilus.Detection; +import nilus.DetectionEffortKind; +import tethys.niluswraps.PDeployment; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; @@ -65,7 +69,22 @@ public interface TethysDataProvider { StreamExportParams streamExportParams); + /** + * Get the algorithm parameters. + * @return + */ public Parameters getAlgorithmParameters(); + + + /** + * Fill in the effort kind list for the top of a Detections document. This must contain a list + * of every species that's going to be output within this effort period. Any species assigned + * to an actual detection must be in this list, or the document will be rejected. + * @param pDeployment + * @param effortKinds tethys object list to add to. + * @param exportParams + */ + public void getEffortKinds(PDeployment pDeployment, List effortKinds, StreamExportParams exportParams); } diff --git a/src/tethys/pamdata/TethysParameterPacker.java b/src/tethys/pamdata/TethysParameterPacker.java index a6ad0e01..9a076bf7 100644 --- a/src/tethys/pamdata/TethysParameterPacker.java +++ b/src/tethys/pamdata/TethysParameterPacker.java @@ -17,6 +17,8 @@ import javax.xml.transform.dom.DOMResult; import org.w3c.dom.Document; import org.w3c.dom.Element; +import PamController.PamControlledUnit; +import PamController.PamSettings; import PamController.settings.output.xml.PamguardXMLWriter; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterData; @@ -67,6 +69,8 @@ public class TethysParameterPacker { */ private MarshalXML marshaller; + + private PamguardXMLWriter xmlWriter; /** * @throws JAXBException @@ -78,11 +82,20 @@ public class TethysParameterPacker { marshaller = new MarshalXML(); } catch (JAXBException e) { } + xmlWriter = PamguardXMLWriter.getXMLWriter(); } - - public List packParameters(Object data) { + public List packParameters(PamControlledUnit pamControlledUnit) { + if (pamControlledUnit instanceof PamSettings == false) { + return null; + } + PamSettings pamSettings = (PamSettings) pamControlledUnit; +// return null; +// } +// +// public List packParameters(Object data) { List elList = new ArrayList(); + Object data = pamSettings.getSettingsReference(); ArrayList objectHierarchy = new ArrayList<>(); @@ -121,15 +134,16 @@ public class TethysParameterPacker { try { Object paramData = pamParam.getData(); boolean ok = createElement(doc, el, paramData, pamParam, objectHierarchy); -// if (newEl != null) { -// elList.add(newEl); -// } } catch (IllegalArgumentException | IllegalAccessException e) { e.printStackTrace(); } - } elList.add(el); + Element pgEl = xmlWriter.writeUnitSettings(doc, el, pamSettings); + if (pgEl != null) { + el.appendChild(pgEl); +// elList.add(pgEl); + } return elList; } diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index 99042e11..be174a5e 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -23,6 +23,7 @@ import tethys.TethysControl; import tethys.dbxml.TethysException; import tethys.detection.StreamDetectionsSummary; import tethys.niluswraps.PDetections; +import tethys.niluswraps.TethysCollections; /** * Table of Detections documents for a single PAMGuard datablock. @@ -103,6 +104,8 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa if (pDets == null) { return; } + + JPopupMenu popMenu = new JPopupMenu(); JMenuItem menuItem = new JMenuItem("Delete " + pDets.detections.getId()); menuItem.addActionListener(new ActionListener() { @@ -111,8 +114,19 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa deleteDocument(pDets); } }); - JPopupMenu popMenu = new JPopupMenu(); popMenu.add(menuItem); + + menuItem = new JMenuItem("Display " + pDets.detections.getId()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + displayDocument(pDets); + } + }); + popMenu.add(menuItem); + + + popMenu.show(e.getComponent(), e.getX(), e.getY()); } @@ -126,6 +140,11 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa selectDataBlock(dataBlock); // force table update. } + private void displayDocument(PDetections pDets) { + getTethysControl().displayDocument(TethysCollections.Detections.toString(), pDets.detections.getId()); + + } + private PDetections detectionsForRow(int iRow) { if (streamDetectionsSummary == null || streamDetectionsSummary.detectionsDocs == null) { return null; From baca001ed87f467512f5ff08f050d7ba106b1449 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:31:00 +0100 Subject: [PATCH 45/65] Param output only if not display --- src/AIS/AISParameters.java | 3 +- src/AIS/AISPositionReport.java | 3 +- src/AIS/AISStaticData.java | 3 +- src/Acquisition/AcquisitionParameters.java | 3 +- src/Acquisition/DaqStatusModuleHeader.java | 3 +- src/Acquisition/FileInputParameters.java | 3 +- src/Acquisition/SoundCardParameters.java | 3 +- .../filedate/StandardFileDateSettings.java | 3 +- src/Acquisition/gpstiming/PPSParameters.java | 3 +- src/AirgunDisplay/AirgunParameters.java | 3 +- src/Array/Hydrophone.java | 3 +- src/Array/HydrophoneLocator.java | 3 +- src/Array/PamArray.java | 3 +- src/Array/Preamplifier.java | 3 +- src/Array/Streamer.java | 3 +- src/Array/streamerOrigin/OriginSettings.java | 3 +- src/ArrayAccelerometer/ArrayAccelParams.java | 3 +- src/Azigram/AzigramParameters.java | 3 +- src/Filters/FilterParameters_2.java | 3 +- src/Filters/FilterParams.java | 3 +- src/GPS/GPSParameters.java | 3 +- src/IshmaelDetector/IshDetParams.java | 3 +- src/IshmaelDetector/IshDisplayParams.java | 3 +- .../KernelSmoothingParameters.java | 3 +- src/Localiser/DelayMeasurementParams.java | 3 +- .../genericLocaliser/MCMC/MCMCParams2.java | 3 +- .../genericLocaliser/MCMC/old/MCMCParams.java | 3 +- .../hyperbolic/HyperbolicParams.java | 3 +- src/Localiser/controls/RawOrFFTParams.java | 3 +- src/Map/MapDetectionsParameters.java | 3 +- src/Map/MapParameters.java | 3 +- src/Map/gridbaselayer/GridbaseParameters.java | 3 +- src/NMEA/NMEAParameters.java | 3 +- .../PamControlledUnitSettings.java | 3 +- src/PamController/UsedModuleInfo.java | 3 +- .../output/xml/PamguardXMLWriter.java | 60 ++++++++++++++++++- .../output/xml/XMLWriterSettings.java | 3 +- .../soundMedium/GlobalMediumParams.java | 3 +- src/PamModel/PamModelSettings.java | 3 +- .../parametermanager/PamParameterSet.java | 23 ++++++- src/PamUtils/Coordinate3d.java | 3 +- src/PamUtils/LatLong.java | 3 +- src/PamUtils/time/GlobalTimeParameters.java | 3 +- src/PamUtils/time/TimeDisplayParameters.java | 3 +- .../time/nmea/NMEATimeParameters.java | 3 +- src/PamUtils/time/ntp/NTPTimeParameters.java | 3 +- src/PamView/ColourArray.java | 3 +- src/PamView/GroupedSourceParameters.java | 3 +- src/PamView/GuiFrameSettings.java | 5 +- src/PamView/PamSymbol.java | 3 +- src/PamView/paneloverlay/OverlayDataInfo.java | 3 +- .../overlaymark/MarkDataSelectorParams.java | 3 +- src/PamView/symbol/ManagedSymbolData.java | 11 +++- src/PamguardMVC/RawDataDisplayOptions.java | 3 +- .../blockprocess/PamBlockParams.java | 3 +- .../dataSelector/DataSelectParams.java | 3 +- .../dataSelector/DataSelectorSettings.java | 3 +- .../datakeeper/DataKeeperSettings.java | 3 +- .../toad/GenericTOADSourceParams.java | 3 +- src/RightWhaleEdgeDetector/RWEParameters.java | 3 +- src/SoundRecorder/RecorderSettings.java | 3 +- .../trigger/RecorderTriggerData.java | 3 +- src/alarm/AlarmParameters.java | 3 +- .../actions/email/SendEmailSettings.java | 3 +- .../actions/serial/AlarmSerialSettings.java | 3 +- src/alarm/actions/sound/PlaySoundParams.java | 3 +- src/alarm/actions/udp/AlarmUDPParams.java | 3 +- src/amplifier/AmpParameters.java | 3 +- src/analogarraysensor/ArraySensorParams.java | 3 +- src/analoginput/AnalogDeviceParams.java | 3 +- src/analoginput/AnalogInputParams.java | 3 +- src/analoginput/AnalogRangeData.java | 3 +- .../brainboxes/BrainBoxParams.java | 3 +- .../calibration/CalibrationData.java | 3 +- .../measurementcomputing/MCCParameters.java | 3 +- .../AngleLoggingParameters.java | 3 +- src/angleMeasurement/AngleParameters.java | 3 +- src/angleVetoes/AngleVeto.java | 3 +- src/angleVetoes/AngleVetoParameters.java | 3 +- .../calcs/snr/SNRAnnotationParameters.java | 3 +- src/annotation/handler/AnnotationChoices.java | 3 +- src/annotation/handler/AnnotationOptions.java | 3 +- .../spectrogram/SpectrogramMarkParams.java | 3 +- src/backupmanager/FileLocation.java | 3 +- .../settings/BackupSettings.java | 3 +- src/beamformer/BeamAlgorithmParams.java | 3 +- src/beamformer/BeamFormerParams.java | 3 +- .../BearingLocaliserParams.java | 3 +- .../algorithms/BearingAlgorithmParams.java | 3 +- src/binaryFileStorage/BinaryFooter.java | 3 +- src/binaryFileStorage/BinaryHeader.java | 3 +- .../BinaryStoreSettings.java | 3 +- src/cepstrum/CepstrumParams.java | 3 +- src/clickDetector/BTDisplayParameters.java | 3 +- src/clickDetector/BasicClickIdParameters.java | 3 +- src/clickDetector/ClickAlarm.java | 3 +- .../ClickBinaryModuleFooter.java | 3 +- .../ClickBinaryModuleHeader.java | 3 +- .../ClickTypeCommonParams.java | 3 +- .../basicSweep/SweepClassifierParameters.java | 3 +- src/clickDetector/ClickDisplayManager.java | 3 +- .../ClickDisplayManagerParameters2.java | 3 +- src/clickDetector/ClickParameters.java | 3 +- src/clickDetector/ClickSpectrumParams.java | 3 +- .../ClickSpectrumTemplateParams.java | 3 +- .../ConcatenatedSpectParams.java | 3 +- src/clickDetector/IDI_DisplayParams.java | 3 +- src/clickDetector/WignerPlotOptions.java | 3 +- .../alarm/ClickAlarmParameters.java | 3 +- .../clicktrains/ClickTrainIdParams.java | 3 +- .../ClickTrainDataSelect2Params.java | 3 +- .../ClickTrainSelectParameters.java | 3 +- .../echoDetection/JamieEchoParams.java | 3 +- .../echoDetection/SimpleEchoParams.java | 3 +- .../localisation/ClickLocParams.java | 3 +- .../offlineFuncs/OfflineParameters.java | 3 +- src/clickTrainDetector/ClickTrainParams.java | 3 +- .../classification/CTClassifierParams.java | 3 +- .../SpectrumTemplateParams.java | 3 +- .../mht/MHTChi2Params.java | 3 +- .../mht/MHTKernelParams.java | 3 +- .../clickTrainAlgorithms/mht/MHTParams.java | 3 +- .../SimpleElectricalNoiseParams.java | 3 +- .../mht/mhtvar/SimpleChi2VarParams.java | 3 +- .../dataselector/CTSelectParams.java | 3 +- src/clipgenerator/ClipGenSetting.java | 3 +- src/clipgenerator/ClipSettings.java | 3 +- .../clipDisplay/ClipDisplayParameters.java | 3 +- .../CBLocaliserSettngs.java | 3 +- src/d3/D3DataMapPoint.java | 3 +- src/dataGram/Datagram.java | 3 +- src/dataGram/DatagramDataPoint.java | 3 +- src/dataGram/DatagramSettings.java | 3 +- src/dataMap/DataMapParameters.java | 3 +- src/dataMap/OfflineDataMapPoint.java | 3 +- .../filemaps/OfflineFileParameters.java | 3 +- src/dataPlots/TDParameters.java | 3 +- src/dataPlots/layout/DataListInfo.java | 3 +- src/dataPlots/layout/GraphParameters.java | 3 +- src/dataPlotsFX/TDGraphParametersFX.java | 3 +- src/dataPlotsFX/TDParametersFX.java | 3 +- src/dataPlotsFX/data/TDScaleInfoData.java | 3 +- .../rawClipDataPlot/FFTPlotSettings.java | 3 +- .../scroller/TDAcousticScrollerParams.java | 3 +- .../scrollingPlot2D/PlotParams2D.java | 3 +- .../SpectrogramParamsFX.java | 3 +- src/dbht/DbHtDisplayParams.java | 3 +- src/dbht/DbHtParameters.java | 3 +- src/dbht/alarm/DbHtAlarmParameters.java | 3 +- src/dbht/offline/DbHtSummaryParams.java | 3 +- src/decimator/DecimatorParams.java | 3 +- src/depthReadout/DepthParameters.java | 3 +- src/depthReadout/MccDepthParameters.java | 5 +- src/detectionPlotFX/plots/FFTPlotParams.java | 3 +- .../plots/SpectrumPlotParams.java | 3 +- .../plots/WaveformPlotParams.java | 3 +- .../plots/WignerPlotParams.java | 3 +- .../DetectionGroupSettings.java | 3 +- src/difar/DifarParameters.java | 7 ++- src/difar/beamforming/BeamformParameters.java | 4 +- .../dataSelector/DifarSelectParameters.java | 3 +- src/difar/demux/GreenridgeParams.java | 3 +- src/effortmonitor/EffortParams.java | 3 +- src/envelopeTracer/EnvelopeParams.java | 3 +- src/fftFilter/FFTFilterParams.java | 3 +- src/fftManager/Complex.java | 3 +- src/fftManager/FFTDataDisplayOptions.java | 3 +- src/fileOfflineData/OfflineFileParams.java | 3 +- src/generalDatabase/DBParameters.java | 3 +- src/generalDatabase/MySQLParameters.java | 3 +- .../dataExport/ValueFilterParams.java | 3 +- .../lookupTables/LookupItem.java | 3 +- .../lookupTables/LookupList.java | 3 +- src/gpl/GPLParameters.java | 3 +- src/group3dlocaliser/Group3DParams.java | 3 +- .../gridsearch/MFPGridSearchParams.java | 3 +- .../algorithm/gridsearch/TOADGridParams.java | 3 +- .../algorithm/toadbase/TOADBaseParams.java | 3 +- .../dataselector/Group3DDataSelectParams.java | 3 +- .../grids/SphericalGridParams.java | 3 +- .../grouper/DetectionGrouperParams.java | 3 +- src/landMarks/LandmarkData.java | 3 +- src/landMarks/LandmarkDatas.java | 3 +- src/levelMeter/LevelMeterParams.java | 3 +- .../AcquisitionSettings.java | 3 +- .../ConfigurationDialogSettings.java | 3 +- src/likelihoodDetectionModule/GuardBand.java | 3 +- .../LikelihoodDetectionParameters.java | 3 +- .../LikelihoodFFTParameters.java | 3 +- src/likelihoodDetectionModule/SignalBand.java | 3 +- .../TargetConfiguration.java | 3 +- src/loggerForms/FormPlotOptions.java | 3 +- src/loggerForms/FormSettings.java | 3 +- .../monitor/FormsSelectorParams.java | 3 +- src/ltsa/LtsaModuleHeader.java | 3 +- src/ltsa/LtsaParameters.java | 3 +- src/mapgrouplocaliser/MapGrouperSettings.java | 3 +- .../MTClassifier.java | 3 +- .../MatchTemplate.java | 3 +- .../MatchedTemplateParams.java | 3 +- src/mcc/mccacquisition/MCCDaqParams.java | 3 +- src/metadata/deployment/DeploymentData.java | 3 +- src/metadata/deployment/QAData.java | 3 +- .../emulator/EmulatorParams.java | 3 +- .../receive/BuoyStatusData.java | 3 +- .../receive/BuoyStatusValue.java | 3 +- .../receive/NetworkReceiveParams.java | 3 +- .../send/NetworkSendParams.java | 3 +- .../networkdaq/NINetworkDaqParams.java | 3 +- src/nmeaEmulator/NMEAEmulatorParams.java | 3 +- src/noiseBandMonitor/NoiseBandSettings.java | 3 +- src/noiseMonitor/NoiseDisplaySettings.java | 3 +- src/noiseMonitor/NoiseMeasurementBand.java | 3 +- src/noiseMonitor/NoiseSettings.java | 3 +- .../alarm/NoiseAlarmParameters.java | 3 +- src/noiseOneBand/OneBandAlarmParameters.java | 3 +- src/noiseOneBand/OneBandDisplayParams.java | 3 +- src/noiseOneBand/OneBandParameters.java | 3 +- .../offline/OneBandSummaryParams.java | 3 +- src/offlineProcessing/TaskGroupParams.java | 3 +- .../QuickAnnotationParameters.java | 3 +- .../dataPlotFX/DLPredDisplayParams.java | 3 +- src/rocca/RoccaParameters.java | 3 +- src/seismicVeto/VetoParameters.java | 3 +- src/serialComms/SerialPortParameters.java | 3 +- src/simulatedAcquisition/SimObject.java | 3 +- .../movement/CircularMovementParams.java | 3 +- .../movement/GridMovementParams.java | 3 +- src/soundPlayback/PlaybackParameters.java | 3 +- .../preprocess/EnvelopeParams.java | 3 +- src/soundtrap/STToolsParams.java | 3 +- .../SpectrogramNoiseSettings.java | 3 +- .../AverageSubtractionParameters.java | 3 +- .../medianFilter/MedianFilterParams.java | 3 +- .../threshold/ThresholdParams.java | 3 +- src/tethys/pamdata/AutoTethysProvider.java | 2 +- src/tethys/pamdata/TethysParameterPacker.java | 47 +++++++++++++-- .../DisplayProviderParameters.java | 3 +- src/userDisplay/UserDisplayParameters.java | 3 +- src/userDisplay/UserFrameParameters.java | 3 +- .../FragmentClassifierParams.java | 3 +- src/whistleClassifier/TrainingContour.java | 3 +- src/whistleClassifier/TrainingDataSet.java | 8 +-- .../WhistleClassificationParameters.java | 3 +- .../training/BatchTrainingParams.java | 3 +- .../training/TrainingContour.java | 3 +- .../training/TrainingDataSet.java | 3 +- .../WhistleBinaryModuleHeader.java | 5 +- .../WhistleToneParameters.java | 3 +- .../alarm/WMAlarmParameters.java | 3 +- 250 files changed, 625 insertions(+), 269 deletions(-) diff --git a/src/AIS/AISParameters.java b/src/AIS/AISParameters.java index 982b5763..2f902b44 100644 --- a/src/AIS/AISParameters.java +++ b/src/AIS/AISParameters.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class AISParameters implements Serializable, Cloneable, ManagedParameters { @@ -51,7 +52,7 @@ public class AISParameters implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("nmeaSource"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/AIS/AISPositionReport.java b/src/AIS/AISPositionReport.java index fda5b10f..ca23ad50 100644 --- a/src/AIS/AISPositionReport.java +++ b/src/AIS/AISPositionReport.java @@ -5,6 +5,7 @@ import java.io.Serializable; import NMEA.NMEABitArray; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.LatLong; /** @@ -160,7 +161,7 @@ sensor. @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/AIS/AISStaticData.java b/src/AIS/AISStaticData.java index 7ca318c5..cbf891dc 100644 --- a/src/AIS/AISStaticData.java +++ b/src/AIS/AISStaticData.java @@ -6,6 +6,7 @@ import java.util.Calendar; import NMEA.NMEABitArray; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamCalendar; public class AISStaticData extends AISReport implements Serializable, ManagedParameters{ @@ -393,7 +394,7 @@ public class AISStaticData extends AISReport implements Serializable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/Acquisition/AcquisitionParameters.java b/src/Acquisition/AcquisitionParameters.java index fd79799e..a9c02698 100644 --- a/src/Acquisition/AcquisitionParameters.java +++ b/src/Acquisition/AcquisitionParameters.java @@ -10,6 +10,7 @@ import Array.Preamplifier; import PamController.PamController; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamguardMVC.PamConstants; @@ -320,7 +321,7 @@ public class AcquisitionParameters implements Serializable, Cloneable, ManagedPa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("channelList"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/Acquisition/DaqStatusModuleHeader.java b/src/Acquisition/DaqStatusModuleHeader.java index 5af44a54..5a548a6a 100644 --- a/src/Acquisition/DaqStatusModuleHeader.java +++ b/src/Acquisition/DaqStatusModuleHeader.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import binaryFileStorage.BinaryHeader; import binaryFileStorage.BinaryObjectData; @@ -30,7 +31,7 @@ class DaqStatusModuleHeader extends ModuleHeader implements Serializable, Manage @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("daqName"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/Acquisition/FileInputParameters.java b/src/Acquisition/FileInputParameters.java index 6453b2f1..9a3f114d 100644 --- a/src/Acquisition/FileInputParameters.java +++ b/src/Acquisition/FileInputParameters.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Used by FileInputSystem @@ -78,7 +79,7 @@ public class FileInputParameters implements Serializable, Cloneable, ManagedPara return null; } - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } } diff --git a/src/Acquisition/SoundCardParameters.java b/src/Acquisition/SoundCardParameters.java index e962db72..a54b62b4 100644 --- a/src/Acquisition/SoundCardParameters.java +++ b/src/Acquisition/SoundCardParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import simulatedAcquisition.SimProcess; /** @@ -47,7 +48,7 @@ public class SoundCardParameters implements Serializable, Cloneable, ManagedPara return null; } - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } } diff --git a/src/Acquisition/filedate/StandardFileDateSettings.java b/src/Acquisition/filedate/StandardFileDateSettings.java index 074540f1..b699ca9a 100644 --- a/src/Acquisition/filedate/StandardFileDateSettings.java +++ b/src/Acquisition/filedate/StandardFileDateSettings.java @@ -6,6 +6,7 @@ import java.util.TimeZone; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import soundtrap.STXMLFile; public class StandardFileDateSettings implements Serializable, Cloneable, ManagedParameters { @@ -150,7 +151,7 @@ public class StandardFileDateSettings implements Serializable, Cloneable, Manage @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } } diff --git a/src/Acquisition/gpstiming/PPSParameters.java b/src/Acquisition/gpstiming/PPSParameters.java index 76bdd012..478d7728 100644 --- a/src/Acquisition/gpstiming/PPSParameters.java +++ b/src/Acquisition/gpstiming/PPSParameters.java @@ -6,6 +6,7 @@ import Acquisition.AcquisitionControl; import Acquisition.AcquisitionDialog; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class PPSParameters implements Cloneable, Serializable, ManagedParameters { @@ -36,7 +37,7 @@ public class PPSParameters implements Cloneable, Serializable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/AirgunDisplay/AirgunParameters.java b/src/AirgunDisplay/AirgunParameters.java index fe779657..5fae8317 100644 --- a/src/AirgunDisplay/AirgunParameters.java +++ b/src/AirgunDisplay/AirgunParameters.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamUtils.LatLong; @@ -95,7 +96,7 @@ public class AirgunParameters implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("dimE"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/Array/Hydrophone.java b/src/Array/Hydrophone.java index 2ec068d7..c2f82bd5 100644 --- a/src/Array/Hydrophone.java +++ b/src/Array/Hydrophone.java @@ -28,6 +28,7 @@ import java.util.Arrays; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import pamMaths.PamVector; import PamView.PamSymbol; @@ -478,7 +479,7 @@ public class Hydrophone implements Serializable, Cloneable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet pps = PamParameterSet.autoGenerate(this); + PamParameterSet pps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); Field f; try { f = this.getClass().getDeclaredField("coordinate"); diff --git a/src/Array/HydrophoneLocator.java b/src/Array/HydrophoneLocator.java index d51fafbb..5d6c9d7c 100644 --- a/src/Array/HydrophoneLocator.java +++ b/src/Array/HydrophoneLocator.java @@ -7,6 +7,7 @@ import Array.streamerOrigin.StreamerDataIterator; import GPS.GpsData; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamUtils.LatLong; import PamguardMVC.PamDataBlock; @@ -225,7 +226,7 @@ abstract public class HydrophoneLocator implements Serializable, Cloneable, Mana */ @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("pamArray"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/Array/PamArray.java b/src/Array/PamArray.java index b91f3f50..43593464 100644 --- a/src/Array/PamArray.java +++ b/src/Array/PamArray.java @@ -35,6 +35,7 @@ import Array.streamerOrigin.StreamerDataIterator; import GPS.GpsData; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamUtils.LatLong; import PamUtils.PamArrayUtils; @@ -1600,7 +1601,7 @@ public class PamArray implements Serializable, Cloneable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("streamers"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/Array/Preamplifier.java b/src/Array/Preamplifier.java index 37e77cfe..20a7dc18 100644 --- a/src/Array/Preamplifier.java +++ b/src/Array/Preamplifier.java @@ -25,6 +25,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * @@ -79,7 +80,7 @@ public class Preamplifier implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Array/Streamer.java b/src/Array/Streamer.java index 90f69f10..7910da0f 100644 --- a/src/Array/Streamer.java +++ b/src/Array/Streamer.java @@ -17,6 +17,7 @@ import PamController.PamController; import PamController.masterReference.MasterReferencePoint; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamUtils.LatLong; import PamUtils.PamCalendar; @@ -767,7 +768,7 @@ public class Streamer implements Serializable, Cloneable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("coordinate"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/Array/streamerOrigin/OriginSettings.java b/src/Array/streamerOrigin/OriginSettings.java index 1beab0f7..9565ea57 100644 --- a/src/Array/streamerOrigin/OriginSettings.java +++ b/src/Array/streamerOrigin/OriginSettings.java @@ -3,6 +3,7 @@ package Array.streamerOrigin; import PamController.SettingsObject; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Class to hold settings for the different origin methods. @@ -47,7 +48,7 @@ public abstract class OriginSettings implements SettingsObject, Cloneable, Manag @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/ArrayAccelerometer/ArrayAccelParams.java b/src/ArrayAccelerometer/ArrayAccelParams.java index fdf107d5..7ce91290 100644 --- a/src/ArrayAccelerometer/ArrayAccelParams.java +++ b/src/ArrayAccelerometer/ArrayAccelParams.java @@ -5,6 +5,7 @@ import java.util.Arrays; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import mcc.MccJniInterface; import mcc.mccjna.MCCConstants; @@ -67,7 +68,7 @@ public class ArrayAccelParams implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Azigram/AzigramParameters.java b/src/Azigram/AzigramParameters.java index 94cf50c7..f0f88c71 100644 --- a/src/Azigram/AzigramParameters.java +++ b/src/Azigram/AzigramParameters.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamView.GroupedSourceParameters; import PamguardMVC.PamConstants; @@ -44,7 +45,7 @@ public class AzigramParameters implements Serializable, ManagedParameters, Clone @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("name"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/Filters/FilterParameters_2.java b/src/Filters/FilterParameters_2.java index 0169f6d1..788db4d8 100644 --- a/src/Filters/FilterParameters_2.java +++ b/src/Filters/FilterParameters_2.java @@ -7,6 +7,7 @@ import org.w3c.dom.Element; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * FilterPArameters for use when the filter is on it's own (within a FilterController) @@ -39,7 +40,7 @@ public class FilterParameters_2 implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Filters/FilterParams.java b/src/Filters/FilterParams.java index 0b8f487c..f8260f44 100644 --- a/src/Filters/FilterParams.java +++ b/src/Filters/FilterParams.java @@ -30,6 +30,7 @@ import org.w3c.dom.Element; import PamController.PamControlledUnit; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.FrequencyFormat; /** @@ -358,7 +359,7 @@ public class FilterParams implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/GPS/GPSParameters.java b/src/GPS/GPSParameters.java index 719a59ca..d907ba75 100644 --- a/src/GPS/GPSParameters.java +++ b/src/GPS/GPSParameters.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class GPSParameters implements Serializable, Cloneable, ManagedParameters { @@ -175,7 +176,7 @@ public class GPSParameters implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("nmeaSource"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/IshmaelDetector/IshDetParams.java b/src/IshmaelDetector/IshDetParams.java index d79b8d60..f8d4c2a9 100644 --- a/src/IshmaelDetector/IshDetParams.java +++ b/src/IshmaelDetector/IshDetParams.java @@ -17,6 +17,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.GroupedSourceParameters; public class IshDetParams implements Serializable, Cloneable, ManagedParameters { @@ -96,7 +97,7 @@ public class IshDetParams implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("inputDataSource"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/IshmaelDetector/IshDisplayParams.java b/src/IshmaelDetector/IshDisplayParams.java index 609f9c2b..a0dea6aa 100644 --- a/src/IshmaelDetector/IshDisplayParams.java +++ b/src/IshmaelDetector/IshDisplayParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Ishamel display parameters for the Spectrogram plug in. @@ -42,7 +43,7 @@ public class IshDisplayParams implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/KernelSmoothing/KernelSmoothingParameters.java b/src/KernelSmoothing/KernelSmoothingParameters.java index 8b5f010c..6ddd593b 100644 --- a/src/KernelSmoothing/KernelSmoothingParameters.java +++ b/src/KernelSmoothing/KernelSmoothingParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class KernelSmoothingParameters implements Serializable, Cloneable, ManagedParameters { @@ -25,7 +26,7 @@ public class KernelSmoothingParameters implements Serializable, Cloneable, Manag @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Localiser/DelayMeasurementParams.java b/src/Localiser/DelayMeasurementParams.java index dc92235d..4a232970 100644 --- a/src/Localiser/DelayMeasurementParams.java +++ b/src/Localiser/DelayMeasurementParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import fftFilter.FFTFilterParams; /** @@ -157,7 +158,7 @@ public class DelayMeasurementParams implements Serializable, Cloneable, ManagedP @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Localiser/algorithms/genericLocaliser/MCMC/MCMCParams2.java b/src/Localiser/algorithms/genericLocaliser/MCMC/MCMCParams2.java index 1a170d0b..de0c0280 100644 --- a/src/Localiser/algorithms/genericLocaliser/MCMC/MCMCParams2.java +++ b/src/Localiser/algorithms/genericLocaliser/MCMC/MCMCParams2.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters { @@ -126,7 +127,7 @@ public class MCMCParams2 implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Localiser/algorithms/genericLocaliser/MCMC/old/MCMCParams.java b/src/Localiser/algorithms/genericLocaliser/MCMC/old/MCMCParams.java index bc3983b6..214f337c 100644 --- a/src/Localiser/algorithms/genericLocaliser/MCMC/old/MCMCParams.java +++ b/src/Localiser/algorithms/genericLocaliser/MCMC/old/MCMCParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class MCMCParams implements Serializable, Cloneable, ManagedParameters { @@ -64,7 +65,7 @@ public class MCMCParams implements Serializable, Cloneable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/HyperbolicParams.java b/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/HyperbolicParams.java index d7cdc11f..d012bfdb 100644 --- a/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/HyperbolicParams.java +++ b/src/Localiser/algorithms/timeDelayLocalisers/hyperbolic/HyperbolicParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** @@ -69,7 +70,7 @@ public class HyperbolicParams implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Localiser/controls/RawOrFFTParams.java b/src/Localiser/controls/RawOrFFTParams.java index 0545ea90..9df83a54 100644 --- a/src/Localiser/controls/RawOrFFTParams.java +++ b/src/Localiser/controls/RawOrFFTParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import Spectrogram.WindowFunction; /** @@ -104,7 +105,7 @@ public class RawOrFFTParams implements Serializable, Cloneable, RawOrFFTParamsIn @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/Map/MapDetectionsParameters.java b/src/Map/MapDetectionsParameters.java index a52aff72..bfc72e9b 100644 --- a/src/Map/MapDetectionsParameters.java +++ b/src/Map/MapDetectionsParameters.java @@ -7,6 +7,7 @@ import java.util.ListIterator; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * PArameters for MapDetectionsManager which @@ -65,7 +66,7 @@ public class MapDetectionsParameters implements Serializable, Cloneable, Managed @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/Map/MapParameters.java b/src/Map/MapParameters.java index b0eee649..11baeb07 100644 --- a/src/Map/MapParameters.java +++ b/src/Map/MapParameters.java @@ -27,6 +27,7 @@ import java.lang.reflect.Field; import Array.Hydrophone; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class MapParameters implements Serializable, Cloneable, ManagedParameters { @@ -301,7 +302,7 @@ public class MapParameters implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/Map/gridbaselayer/GridbaseParameters.java b/src/Map/gridbaselayer/GridbaseParameters.java index 2af62e75..fe608b8d 100644 --- a/src/Map/gridbaselayer/GridbaseParameters.java +++ b/src/Map/gridbaselayer/GridbaseParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class GridbaseParameters implements Cloneable, Serializable, ManagedParameters { @@ -25,7 +26,7 @@ public class GridbaseParameters implements Cloneable, Serializable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/NMEA/NMEAParameters.java b/src/NMEA/NMEAParameters.java index 42393be3..9da8c22c 100644 --- a/src/NMEA/NMEAParameters.java +++ b/src/NMEA/NMEAParameters.java @@ -24,6 +24,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import serialComms.jserialcomm.PJSerialComm; public class NMEAParameters implements Serializable, Cloneable, ManagedParameters { @@ -149,7 +150,7 @@ public class NMEAParameters implements Serializable, Cloneable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamController/PamControlledUnitSettings.java b/src/PamController/PamControlledUnitSettings.java index ccdc18bf..6522884f 100644 --- a/src/PamController/PamControlledUnitSettings.java +++ b/src/PamController/PamControlledUnitSettings.java @@ -36,6 +36,7 @@ import org.apache.commons.io.input.ClassLoaderObjectInputStream; import PamModel.PamModel; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.dialog.warn.WarnOnce; @@ -398,7 +399,7 @@ public class PamControlledUnitSettings implements Serializable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamController/UsedModuleInfo.java b/src/PamController/UsedModuleInfo.java index 7c70ef4d..05d222d1 100644 --- a/src/PamController/UsedModuleInfo.java +++ b/src/PamController/UsedModuleInfo.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Very simple class used in an ArrayList of used modules that @@ -53,7 +54,7 @@ public class UsedModuleInfo implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamController/settings/output/xml/PamguardXMLWriter.java b/src/PamController/settings/output/xml/PamguardXMLWriter.java index 58cf1bd7..37530402 100644 --- a/src/PamController/settings/output/xml/PamguardXMLWriter.java +++ b/src/PamController/settings/output/xml/PamguardXMLWriter.java @@ -43,6 +43,7 @@ import PamController.PamguardVersionInfo; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterData; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamCalendar; import PamUtils.XMLUtils; import PamguardMVC.PamDataBlock; @@ -64,6 +65,7 @@ public class PamguardXMLWriter implements PamSettings { private static final Set> WRAPPER_TYPES = getWrapperTypes(); private XMLWriterSettings writerSettings = new XMLWriterSettings(); + private boolean excludeDisplaySettings; // private String xmlNameSpace; private static PamguardXMLWriter singleInstance; @@ -454,7 +456,6 @@ public class PamguardXMLWriter implements PamSettings { * @return xml element */ public Element writeUnitSettings(Document doc, Element parent, PamSettings pamSettingsUnit) { - int[] settingInds = findSettings(null, pamSettingsUnit.getUnitName()); PamSettings[] settingsObjects = null; if (settingInds != null) { @@ -499,6 +500,9 @@ public class PamguardXMLWriter implements PamSettings { Element settingEl = doc.createElement("CONFIGURATION"); moduleData.appendChild(settingEl); for (int i = 0; i < toWrite.length; i++) { + if (wantObject(toWrite[i]) == false) { + continue; + } Element setEl = writeSettings(doc, toWrite[i], new ArrayList()); if (setEl != null) { settingEl.appendChild(setEl); @@ -509,6 +513,32 @@ public class PamguardXMLWriter implements PamSettings { return moduleData; } + /** + * USed by the Tethys writer to avoid writing display settings. + * @param pamSettings + * @return + */ + private boolean wantObject(PamSettings pamSettings) { + if (excludeDisplaySettings == false) { + return true; + } + Object obj = pamSettings.getSettingsReference(); + if (obj == null) { + return false; + } + if (obj instanceof ManagedParameters) { + ManagedParameters managedParams = (ManagedParameters) obj; + PamParameterSet paramSet = managedParams.getParameterSet(); + if (paramSet == null) { + return false; + } + if (paramSet.getParameterSetType() == ParameterSetType.DISPLAY && excludeDisplaySettings) { + return false; + } + } + return true; + } + /** * Write settings for a settings object, using the standard retreived object * from the settings. @@ -539,6 +569,7 @@ public class PamguardXMLWriter implements PamSettings { * @return */ private Element writeSettings(Document doc, PamSettings pamSettings, Object data, ArrayList objectHierarchy) { + Element el = doc.createElement("SETTINGS"); el.setAttribute("Type", pamSettings.getUnitType()); el.setAttribute("Name", pamSettings.getUnitName()); @@ -851,7 +882,16 @@ public class PamguardXMLWriter implements PamSettings { */ private int[] findSettings(String type, String name) { if (settingsSets == null) { - return null; + makeSettingsList(); + if (settingsSets == null) { + return null; + } + } + if (usedSettingsSets == null) { + usedSettingsSets = new boolean[settingsSets.size()]; + } + else if (usedSettingsSets.length < settingsSets.size()) { + usedSettingsSets = Arrays.copyOf(usedSettingsSets, settingsSets.size()); } int[] found = new int[settingsSets.size()]; int nFound = 0; @@ -871,7 +911,7 @@ public class PamguardXMLWriter implements PamSettings { return Arrays.copyOf(found, nFound); } - private ArrayList makeSettingsList() { + public ArrayList makeSettingsList() { PamSettingManager settingsManager = PamSettingManager.getInstance(); settingsSets = settingsManager.getOwners(); if (settingsSets == null) { @@ -1001,6 +1041,20 @@ public class PamguardXMLWriter implements PamSettings { return true; } + /** + * @return the excludeDisplaySettings + */ + public boolean isExcludeDisplaySettings() { + return excludeDisplaySettings; + } + + /** + * @param excludeDisplaySettings the excludeDisplaySettings to set + */ + public void setExcludeDisplaySettings(boolean excludeDisplaySettings) { + this.excludeDisplaySettings = excludeDisplaySettings; + } + // public void setStaticNameSpace(String xmlNameSpace) { // this.xmlNameSpace = xmlNameSpace; // } diff --git a/src/PamController/settings/output/xml/XMLWriterSettings.java b/src/PamController/settings/output/xml/XMLWriterSettings.java index 8e5d0969..dcd60b83 100644 --- a/src/PamController/settings/output/xml/XMLWriterSettings.java +++ b/src/PamController/settings/output/xml/XMLWriterSettings.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class XMLWriterSettings implements Serializable, Cloneable, ManagedParameters { @@ -34,7 +35,7 @@ public class XMLWriterSettings implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/PamController/soundMedium/GlobalMediumParams.java b/src/PamController/soundMedium/GlobalMediumParams.java index 0a52ac83..8bf10f27 100644 --- a/src/PamController/soundMedium/GlobalMediumParams.java +++ b/src/PamController/soundMedium/GlobalMediumParams.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamController.soundMedium.GlobalMedium.SoundMedium; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Stores parameters for the current medium. @@ -41,7 +42,7 @@ public class GlobalMediumParams implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/PamModel/PamModelSettings.java b/src/PamModel/PamModelSettings.java index 9e4b0632..667747b8 100644 --- a/src/PamModel/PamModelSettings.java +++ b/src/PamModel/PamModelSettings.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class PamModelSettings implements Cloneable, Serializable, ManagedParameters { @@ -74,7 +75,7 @@ public class PamModelSettings implements Cloneable, Serializable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamModel/parametermanager/PamParameterSet.java b/src/PamModel/parametermanager/PamParameterSet.java index e4c37c24..c7108e0a 100644 --- a/src/PamModel/parametermanager/PamParameterSet.java +++ b/src/PamModel/parametermanager/PamParameterSet.java @@ -29,6 +29,9 @@ public class PamParameterSet { private static boolean printDebug = false; + public enum ParameterSetType {DETECTOR, DISPLAY}; + + private ParameterSetType parameterSetType; /** * Standard modifiers to exclude. This is important for many classes which will tend to * do crazy things such as incorporate ALL of their final fields, e.g. when a Color @@ -55,8 +58,10 @@ public class PamParameterSet { * in the STANDARD_MODIFIER_EXCLUSIONS list (FINAL or STATIC). * @return Created parameter set. */ - public static PamParameterSet autoGenerate(Object parentObject) { - return autoGenerate(parentObject, STANDARD_MODIFIER_EXCLUSIONS); + public static PamParameterSet autoGenerate(Object parentObject, ParameterSetType parameterSetType) { + PamParameterSet paramSet = autoGenerate(parentObject, STANDARD_MODIFIER_EXCLUSIONS); + paramSet.setParameterSetType(parameterSetType); + return paramSet; } /** @@ -287,4 +292,18 @@ public class PamParameterSet { return parameterDatas.remove(paramName); } + /** + * @return the parameterSetType + */ + public ParameterSetType getParameterSetType() { + return parameterSetType; + } + + /** + * @param parameterSetType the parameterSetType to set + */ + public void setParameterSetType(ParameterSetType parameterSetType) { + this.parameterSetType = parameterSetType; + } + } diff --git a/src/PamUtils/Coordinate3d.java b/src/PamUtils/Coordinate3d.java index 57a859f4..a8b6d156 100644 --- a/src/PamUtils/Coordinate3d.java +++ b/src/PamUtils/Coordinate3d.java @@ -26,6 +26,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Class definition for a x,y coordinate number type. @@ -168,7 +169,7 @@ public class Coordinate3d implements Serializable , Cloneable, PamCoordinate, Ma @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamUtils/LatLong.java b/src/PamUtils/LatLong.java index 6b8f9df9..6d465ee6 100644 --- a/src/PamUtils/LatLong.java +++ b/src/PamUtils/LatLong.java @@ -15,6 +15,7 @@ import java.text.NumberFormat; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.PamConstants; import net.sf.geographiclib.Geodesic; import net.sf.geographiclib.PolygonArea; @@ -833,7 +834,7 @@ public class LatLong implements Serializable, Cloneable, Transferable, PamCoordi */ @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("height"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/PamUtils/time/GlobalTimeParameters.java b/src/PamUtils/time/GlobalTimeParameters.java index 08c602f0..9f2a6650 100644 --- a/src/PamUtils/time/GlobalTimeParameters.java +++ b/src/PamUtils/time/GlobalTimeParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class GlobalTimeParameters implements Serializable, Cloneable, ManagedParameters { @@ -91,7 +92,7 @@ public class GlobalTimeParameters implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamUtils/time/TimeDisplayParameters.java b/src/PamUtils/time/TimeDisplayParameters.java index 9f039f54..8bfc7318 100644 --- a/src/PamUtils/time/TimeDisplayParameters.java +++ b/src/PamUtils/time/TimeDisplayParameters.java @@ -5,6 +5,7 @@ import java.util.TimeZone; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class TimeDisplayParameters implements Serializable, Cloneable, ManagedParameters { @@ -34,7 +35,7 @@ public class TimeDisplayParameters implements Serializable, Cloneable, ManagedPa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/PamUtils/time/nmea/NMEATimeParameters.java b/src/PamUtils/time/nmea/NMEATimeParameters.java index 881733a4..1ec4b77f 100644 --- a/src/PamUtils/time/nmea/NMEATimeParameters.java +++ b/src/PamUtils/time/nmea/NMEATimeParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NMEATimeParameters implements Serializable, Cloneable, ManagedParameters { @@ -26,7 +27,7 @@ public class NMEATimeParameters implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamUtils/time/ntp/NTPTimeParameters.java b/src/PamUtils/time/ntp/NTPTimeParameters.java index 56322ca3..fea8ae07 100644 --- a/src/PamUtils/time/ntp/NTPTimeParameters.java +++ b/src/PamUtils/time/ntp/NTPTimeParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NTPTimeParameters implements Serializable, Cloneable, ManagedParameters { @@ -32,7 +33,7 @@ public class NTPTimeParameters implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamView/ColourArray.java b/src/PamView/ColourArray.java index edbdb61d..51f6c531 100644 --- a/src/PamView/ColourArray.java +++ b/src/PamView/ColourArray.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * A series of functions for creating arrays of colours @@ -410,7 +411,7 @@ public class ColourArray implements Cloneable, Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/PamView/GroupedSourceParameters.java b/src/PamView/GroupedSourceParameters.java index f26cdafd..fb0b7509 100644 --- a/src/PamView/GroupedSourceParameters.java +++ b/src/PamView/GroupedSourceParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamUtils; import PamView.dialog.GroupedSourcePanel; @@ -210,7 +211,7 @@ public class GroupedSourceParameters implements Serializable, Cloneable, Managed */ @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamView/GuiFrameSettings.java b/src/PamView/GuiFrameSettings.java index d367c1e3..3445c790 100644 --- a/src/PamView/GuiFrameSettings.java +++ b/src/PamView/GuiFrameSettings.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import PamController.PamControlledUnit; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; /** @@ -55,7 +56,7 @@ public class GuiFrameSettings implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("unitFrameInfo"); ps.put(new PrivatePamParameterData(this, field) { @@ -89,7 +90,7 @@ public class GuiFrameSettings implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("guiFrame"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/PamView/PamSymbol.java b/src/PamView/PamSymbol.java index 00c471f6..2a4f4061 100644 --- a/src/PamView/PamSymbol.java +++ b/src/PamView/PamSymbol.java @@ -38,6 +38,7 @@ import javax.swing.JPanel; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.PamColors.PamColor; import PamView.symbol.SymbolData; @@ -808,7 +809,7 @@ public class PamSymbol extends PamSymbolBase implements Serializable, Icon, Clon @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/PamView/paneloverlay/OverlayDataInfo.java b/src/PamView/paneloverlay/OverlayDataInfo.java index 3c3ed86b..fd590d82 100644 --- a/src/PamView/paneloverlay/OverlayDataInfo.java +++ b/src/PamView/paneloverlay/OverlayDataInfo.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class OverlayDataInfo implements Serializable, Cloneable, ManagedParameters { @@ -31,7 +32,7 @@ public class OverlayDataInfo implements Serializable, Cloneable, ManagedParamete */ @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/PamView/paneloverlay/overlaymark/MarkDataSelectorParams.java b/src/PamView/paneloverlay/overlaymark/MarkDataSelectorParams.java index f6c8d446..ae081fc1 100644 --- a/src/PamView/paneloverlay/overlaymark/MarkDataSelectorParams.java +++ b/src/PamView/paneloverlay/overlaymark/MarkDataSelectorParams.java @@ -6,6 +6,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamguardMVC.PamDataBlock; @@ -73,7 +74,7 @@ public class MarkDataSelectorParams implements Serializable, Cloneable, ManagedP @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("overlayChoices"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/PamView/symbol/ManagedSymbolData.java b/src/PamView/symbol/ManagedSymbolData.java index 23bb555d..19789f61 100644 --- a/src/PamView/symbol/ManagedSymbolData.java +++ b/src/PamView/symbol/ManagedSymbolData.java @@ -3,7 +3,11 @@ package PamView.symbol; import java.io.Serializable; import java.util.Hashtable; -public class ManagedSymbolData implements Cloneable, Serializable { +import PamModel.parametermanager.ManagedParameters; +import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; + +public class ManagedSymbolData implements Cloneable, Serializable, ManagedParameters { public static final long serialVersionUID = 1L; @@ -34,5 +38,10 @@ public class ManagedSymbolData implements Cloneable, Serializable { return symbolOptions; } + @Override + public PamParameterSet getParameterSet() { + return PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); + } + } diff --git a/src/PamguardMVC/RawDataDisplayOptions.java b/src/PamguardMVC/RawDataDisplayOptions.java index 9ed8355c..6ff49562 100644 --- a/src/PamguardMVC/RawDataDisplayOptions.java +++ b/src/PamguardMVC/RawDataDisplayOptions.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class RawDataDisplayOptions implements Serializable, Cloneable, ManagedParameters { @@ -13,7 +14,7 @@ public class RawDataDisplayOptions implements Serializable, Cloneable, ManagedPa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/PamguardMVC/blockprocess/PamBlockParams.java b/src/PamguardMVC/blockprocess/PamBlockParams.java index 249d98e1..aaaed713 100644 --- a/src/PamguardMVC/blockprocess/PamBlockParams.java +++ b/src/PamguardMVC/blockprocess/PamBlockParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import gpl.GPLParameters; /** @@ -44,7 +45,7 @@ public class PamBlockParams implements Cloneable, Serializable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamguardMVC/dataSelector/DataSelectParams.java b/src/PamguardMVC/dataSelector/DataSelectParams.java index da0f5e74..0d4af3fb 100644 --- a/src/PamguardMVC/dataSelector/DataSelectParams.java +++ b/src/PamguardMVC/dataSelector/DataSelectParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Data select parameters. @@ -53,7 +54,7 @@ abstract public class DataSelectParams implements Serializable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/PamguardMVC/dataSelector/DataSelectorSettings.java b/src/PamguardMVC/dataSelector/DataSelectorSettings.java index f29c8103..df66d20e 100644 --- a/src/PamguardMVC/dataSelector/DataSelectorSettings.java +++ b/src/PamguardMVC/dataSelector/DataSelectorSettings.java @@ -7,6 +7,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class DataSelectorSettings implements Serializable, ManagedParameters { @@ -42,7 +43,7 @@ public class DataSelectorSettings implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("selectorParams"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/PamguardMVC/datakeeper/DataKeeperSettings.java b/src/PamguardMVC/datakeeper/DataKeeperSettings.java index 446669c6..3765ef72 100644 --- a/src/PamguardMVC/datakeeper/DataKeeperSettings.java +++ b/src/PamguardMVC/datakeeper/DataKeeperSettings.java @@ -7,6 +7,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class DataKeeperSettings implements Serializable, Cloneable, ManagedParameters { @@ -51,7 +52,7 @@ public class DataKeeperSettings implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("keepTimeData"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/PamguardMVC/toad/GenericTOADSourceParams.java b/src/PamguardMVC/toad/GenericTOADSourceParams.java index e0c2237c..cd12140a 100644 --- a/src/PamguardMVC/toad/GenericTOADSourceParams.java +++ b/src/PamguardMVC/toad/GenericTOADSourceParams.java @@ -6,6 +6,7 @@ import Localiser.DelayMeasurementParams; import Localiser.controls.RawOrFFTParams; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * General parameters for detection TOAD measurement. Is split @@ -67,7 +68,7 @@ public class GenericTOADSourceParams implements Cloneable, Serializable, Managed @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/RightWhaleEdgeDetector/RWEParameters.java b/src/RightWhaleEdgeDetector/RWEParameters.java index 8bb661a4..4b179378 100644 --- a/src/RightWhaleEdgeDetector/RWEParameters.java +++ b/src/RightWhaleEdgeDetector/RWEParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class RWEParameters implements Serializable, Cloneable, ManagedParameters { @@ -41,7 +42,7 @@ public class RWEParameters implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/SoundRecorder/RecorderSettings.java b/src/SoundRecorder/RecorderSettings.java index 72c9576d..4fcd6eec 100644 --- a/src/SoundRecorder/RecorderSettings.java +++ b/src/SoundRecorder/RecorderSettings.java @@ -16,6 +16,7 @@ import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterData; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamUtils; import PamguardMVC.PamRawDataBlock; import SoundRecorder.trigger.RecorderTrigger; @@ -443,7 +444,7 @@ public class RecorderSettings implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("channelBitmap"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/SoundRecorder/trigger/RecorderTriggerData.java b/src/SoundRecorder/trigger/RecorderTriggerData.java index 2192afc9..9b0cb32c 100644 --- a/src/SoundRecorder/trigger/RecorderTriggerData.java +++ b/src/SoundRecorder/trigger/RecorderTriggerData.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Information for triggered recordings to tell each recorder how long @@ -271,7 +272,7 @@ public class RecorderTriggerData implements Serializable, Cloneable, ManagedPara */ @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("lastTriggerStart"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/alarm/AlarmParameters.java b/src/alarm/AlarmParameters.java index df89b940..0694adfc 100644 --- a/src/alarm/AlarmParameters.java +++ b/src/alarm/AlarmParameters.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class AlarmParameters implements Serializable, Cloneable, ManagedParameters { @@ -103,7 +104,7 @@ public class AlarmParameters implements Serializable, Cloneable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("hadHold"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/alarm/actions/email/SendEmailSettings.java b/src/alarm/actions/email/SendEmailSettings.java index dcd4dc0b..da558c85 100644 --- a/src/alarm/actions/email/SendEmailSettings.java +++ b/src/alarm/actions/email/SendEmailSettings.java @@ -28,6 +28,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import alarm.actions.serial.AlarmSerialSettings; /** @@ -158,7 +159,7 @@ public class SendEmailSettings implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/alarm/actions/serial/AlarmSerialSettings.java b/src/alarm/actions/serial/AlarmSerialSettings.java index b14e785c..801cc765 100644 --- a/src/alarm/actions/serial/AlarmSerialSettings.java +++ b/src/alarm/actions/serial/AlarmSerialSettings.java @@ -8,6 +8,7 @@ import com.fazecast.jSerialComm.SerialPort; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import serialComms.SerialPortConstants; import serialComms.jserialcomm.PJSerialComm; @@ -68,7 +69,7 @@ public class AlarmSerialSettings implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/alarm/actions/sound/PlaySoundParams.java b/src/alarm/actions/sound/PlaySoundParams.java index c9f97970..6481e41d 100644 --- a/src/alarm/actions/sound/PlaySoundParams.java +++ b/src/alarm/actions/sound/PlaySoundParams.java @@ -6,6 +6,7 @@ import java.util.Arrays; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import alarm.AlarmParameters; public class PlaySoundParams implements Cloneable, Serializable, ManagedParameters { @@ -33,7 +34,7 @@ public class PlaySoundParams implements Cloneable, Serializable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/alarm/actions/udp/AlarmUDPParams.java b/src/alarm/actions/udp/AlarmUDPParams.java index 10728bb2..7a7bfa61 100644 --- a/src/alarm/actions/udp/AlarmUDPParams.java +++ b/src/alarm/actions/udp/AlarmUDPParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class AlarmUDPParams implements Serializable, Cloneable, ManagedParameters { @@ -25,7 +26,7 @@ public class AlarmUDPParams implements Serializable, Cloneable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/amplifier/AmpParameters.java b/src/amplifier/AmpParameters.java index 98e52a63..89aac38c 100644 --- a/src/amplifier/AmpParameters.java +++ b/src/amplifier/AmpParameters.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamguardMVC.PamConstants; @@ -41,7 +42,7 @@ public class AmpParameters implements Cloneable, Serializable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("rawDataSource"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/analogarraysensor/ArraySensorParams.java b/src/analogarraysensor/ArraySensorParams.java index 637bf789..bd63bbaa 100644 --- a/src/analogarraysensor/ArraySensorParams.java +++ b/src/analogarraysensor/ArraySensorParams.java @@ -5,6 +5,7 @@ import java.io.Serializable; import Array.sensors.ArrayDisplayParameters; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class ArraySensorParams implements Serializable, Cloneable, ManagedParameters { @@ -47,7 +48,7 @@ public class ArraySensorParams implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/analoginput/AnalogDeviceParams.java b/src/analoginput/AnalogDeviceParams.java index 1b43c7b9..cae97232 100644 --- a/src/analoginput/AnalogDeviceParams.java +++ b/src/analoginput/AnalogDeviceParams.java @@ -6,6 +6,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import analoginput.calibration.CalibrationData; @@ -63,7 +64,7 @@ public class AnalogDeviceParams implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("calibrationTable"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/analoginput/AnalogInputParams.java b/src/analoginput/AnalogInputParams.java index acd041e4..a64d51ba 100644 --- a/src/analoginput/AnalogInputParams.java +++ b/src/analoginput/AnalogInputParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class AnalogInputParams implements Serializable, Cloneable, ManagedParameters { @@ -13,7 +14,7 @@ public class AnalogInputParams implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/analoginput/AnalogRangeData.java b/src/analoginput/AnalogRangeData.java index 00043994..7a7d7109 100644 --- a/src/analoginput/AnalogRangeData.java +++ b/src/analoginput/AnalogRangeData.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class AnalogRangeData implements Serializable, Cloneable, Comparable, ManagedParameters { @@ -119,7 +120,7 @@ public class AnalogRangeData implements Serializable, Cloneable, Comparable, Serializable, Cloneab @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("fileIsLoaded"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/ClickBinaryModuleFooter.java b/src/clickDetector/ClickBinaryModuleFooter.java index 35718e17..f24ed53c 100644 --- a/src/clickDetector/ClickBinaryModuleFooter.java +++ b/src/clickDetector/ClickBinaryModuleFooter.java @@ -12,6 +12,7 @@ import PamController.PamController; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import binaryFileStorage.BinaryHeader; import binaryFileStorage.BinaryObjectData; import binaryFileStorage.ModuleFooter; @@ -128,7 +129,7 @@ public class ClickBinaryModuleFooter extends ModuleFooter implements ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("clickDetectorName"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/ClickBinaryModuleHeader.java b/src/clickDetector/ClickBinaryModuleHeader.java index 1cb7f8d8..f36f7854 100644 --- a/src/clickDetector/ClickBinaryModuleHeader.java +++ b/src/clickDetector/ClickBinaryModuleHeader.java @@ -2,6 +2,7 @@ package clickDetector; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import binaryFileStorage.BinaryHeader; import binaryFileStorage.BinaryObjectData; import binaryFileStorage.ModuleHeader; @@ -28,7 +29,7 @@ public class ClickBinaryModuleHeader extends ModuleHeader implements ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } } diff --git a/src/clickDetector/ClickClassifiers/ClickTypeCommonParams.java b/src/clickDetector/ClickClassifiers/ClickTypeCommonParams.java index d937b164..78b56316 100644 --- a/src/clickDetector/ClickClassifiers/ClickTypeCommonParams.java +++ b/src/clickDetector/ClickClassifiers/ClickTypeCommonParams.java @@ -28,6 +28,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Superclass for click parameters, including the ClickTypeParms and @@ -191,7 +192,7 @@ abstract public class ClickTypeCommonParams implements Cloneable, Serializable, @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierParameters.java b/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierParameters.java index 9c925607..6cc4f1b5 100644 --- a/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierParameters.java +++ b/src/clickDetector/ClickClassifiers/basicSweep/SweepClassifierParameters.java @@ -7,6 +7,7 @@ import java.util.Vector; import PamModel.SMRUEnable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class SweepClassifierParameters implements Serializable, Cloneable, ManagedParameters { @@ -72,7 +73,7 @@ public class SweepClassifierParameters implements Serializable, Cloneable, Manag @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("classifierSets"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/ClickDisplayManager.java b/src/clickDetector/ClickDisplayManager.java index f46aeca8..f992ef2d 100644 --- a/src/clickDetector/ClickDisplayManager.java +++ b/src/clickDetector/ClickDisplayManager.java @@ -28,6 +28,7 @@ import PamController.PamSettingManager; import PamController.PamSettings; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamView.MenuItemEnabler; @@ -302,7 +303,7 @@ public class ClickDisplayManager implements PamSettings { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("className"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/ClickDisplayManagerParameters2.java b/src/clickDetector/ClickDisplayManagerParameters2.java index 86ccbca6..d8fd18cf 100644 --- a/src/clickDetector/ClickDisplayManagerParameters2.java +++ b/src/clickDetector/ClickDisplayManagerParameters2.java @@ -9,6 +9,7 @@ import PamController.PamController; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class ClickDisplayManagerParameters2 implements Cloneable, Serializable, ManagedParameters { @@ -181,7 +182,7 @@ public class ClickDisplayManagerParameters2 implements Cloneable, Serializable, @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("initialised"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/ClickParameters.java b/src/clickDetector/ClickParameters.java index 3146b0e4..118eaf21 100644 --- a/src/clickDetector/ClickParameters.java +++ b/src/clickDetector/ClickParameters.java @@ -38,6 +38,7 @@ import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterData; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.GroupedSourceParameters; import PamView.dialog.GroupedSourcePanel; import PamView.paneloverlay.overlaymark.MarkDataSelectorParams; @@ -441,7 +442,7 @@ public class ClickParameters implements Serializable, Cloneable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { PamParameterData param = ps.findParameterData("dbThreshold"); param.setShortName("Detection Threshold"); diff --git a/src/clickDetector/ClickSpectrumParams.java b/src/clickDetector/ClickSpectrumParams.java index 6784c9fa..d18927e3 100644 --- a/src/clickDetector/ClickSpectrumParams.java +++ b/src/clickDetector/ClickSpectrumParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class ClickSpectrumParams implements Serializable, Cloneable, ManagedParameters { @@ -38,7 +39,7 @@ public class ClickSpectrumParams implements Serializable, Cloneable, ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/clickDetector/ClickSpectrumTemplateParams.java b/src/clickDetector/ClickSpectrumTemplateParams.java index ff156b0e..caad2b6e 100644 --- a/src/clickDetector/ClickSpectrumTemplateParams.java +++ b/src/clickDetector/ClickSpectrumTemplateParams.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; @@ -39,7 +40,7 @@ public class ClickSpectrumTemplateParams implements Serializable, Cloneable, Man @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("clickTemplateArray"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/ConcatenatedSpectParams.java b/src/clickDetector/ConcatenatedSpectParams.java index 6bf67108..db3f6a0b 100644 --- a/src/clickDetector/ConcatenatedSpectParams.java +++ b/src/clickDetector/ConcatenatedSpectParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.ColourArray.ColourArrayType; public class ConcatenatedSpectParams implements Serializable, Cloneable, ManagedParameters { @@ -44,7 +45,7 @@ public class ConcatenatedSpectParams implements Serializable, Cloneable, Manage @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/clickDetector/IDI_DisplayParams.java b/src/clickDetector/IDI_DisplayParams.java index 0198fe92..e7641768 100644 --- a/src/clickDetector/IDI_DisplayParams.java +++ b/src/clickDetector/IDI_DisplayParams.java @@ -29,6 +29,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; /** @@ -270,7 +271,7 @@ public class IDI_DisplayParams implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("saveOutput"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/WignerPlotOptions.java b/src/clickDetector/WignerPlotOptions.java index 0ef46f2e..e05640a6 100644 --- a/src/clickDetector/WignerPlotOptions.java +++ b/src/clickDetector/WignerPlotOptions.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class WignerPlotOptions implements Serializable, Cloneable, ManagedParameters { @@ -25,7 +26,7 @@ public class WignerPlotOptions implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/clickDetector/alarm/ClickAlarmParameters.java b/src/clickDetector/alarm/ClickAlarmParameters.java index 3cc714b2..8bed53c6 100644 --- a/src/clickDetector/alarm/ClickAlarmParameters.java +++ b/src/clickDetector/alarm/ClickAlarmParameters.java @@ -7,6 +7,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamguardMVC.dataSelector.DataSelectParams; @@ -127,7 +128,7 @@ public class ClickAlarmParameters extends DataSelectParams implements Cloneable, @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("eventTypes"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/clicktrains/ClickTrainIdParams.java b/src/clickDetector/clicktrains/ClickTrainIdParams.java index 9f9b03c2..c6d4746f 100644 --- a/src/clickDetector/clicktrains/ClickTrainIdParams.java +++ b/src/clickDetector/clicktrains/ClickTrainIdParams.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; /** @@ -59,7 +60,7 @@ public class ClickTrainIdParams implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("dataVersion"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/dataSelector/ClickTrainDataSelect2Params.java b/src/clickDetector/dataSelector/ClickTrainDataSelect2Params.java index 668fec3e..a8cc198c 100644 --- a/src/clickDetector/dataSelector/ClickTrainDataSelect2Params.java +++ b/src/clickDetector/dataSelector/ClickTrainDataSelect2Params.java @@ -8,6 +8,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamguardMVC.dataSelector.DataSelectParams; @@ -91,7 +92,7 @@ public class ClickTrainDataSelect2Params extends DataSelectParams implements Clo @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("wantType"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/dataSelector/ClickTrainSelectParameters.java b/src/clickDetector/dataSelector/ClickTrainSelectParameters.java index 3cfc5189..75462778 100644 --- a/src/clickDetector/dataSelector/ClickTrainSelectParameters.java +++ b/src/clickDetector/dataSelector/ClickTrainSelectParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.dataSelector.DataSelectParams; import clickDetector.ClickParameters; @@ -32,7 +33,7 @@ public class ClickTrainSelectParameters extends DataSelectParams implements Seri @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickDetector/echoDetection/JamieEchoParams.java b/src/clickDetector/echoDetection/JamieEchoParams.java index ec00df42..e3cbadb5 100644 --- a/src/clickDetector/echoDetection/JamieEchoParams.java +++ b/src/clickDetector/echoDetection/JamieEchoParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class JamieEchoParams implements Serializable, Cloneable, ManagedParameters { @@ -31,7 +32,7 @@ public static final long serialVersionUID = 3L; @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickDetector/echoDetection/SimpleEchoParams.java b/src/clickDetector/echoDetection/SimpleEchoParams.java index fe5eae8e..eb6095a1 100644 --- a/src/clickDetector/echoDetection/SimpleEchoParams.java +++ b/src/clickDetector/echoDetection/SimpleEchoParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class SimpleEchoParams implements Serializable, Cloneable, ManagedParameters { @@ -26,7 +27,7 @@ public class SimpleEchoParams implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickDetector/localisation/ClickLocParams.java b/src/clickDetector/localisation/ClickLocParams.java index fcfaaff6..6a597258 100644 --- a/src/clickDetector/localisation/ClickLocParams.java +++ b/src/clickDetector/localisation/ClickLocParams.java @@ -7,6 +7,7 @@ import java.util.Arrays; import Localiser.detectionGroupLocaliser.DetectionGroupOptions; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class ClickLocParams implements Serializable, Cloneable, DetectionGroupOptions, ManagedParameters { @@ -110,7 +111,7 @@ public class ClickLocParams implements Serializable, Cloneable, DetectionGroupOp @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("internalVersion"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clickDetector/offlineFuncs/OfflineParameters.java b/src/clickDetector/offlineFuncs/OfflineParameters.java index 2ab82962..0c4b2c71 100644 --- a/src/clickDetector/offlineFuncs/OfflineParameters.java +++ b/src/clickDetector/offlineFuncs/OfflineParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class OfflineParameters implements Serializable, Cloneable, ManagedParameters { @@ -28,7 +29,7 @@ public class OfflineParameters implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/ClickTrainParams.java b/src/clickTrainDetector/ClickTrainParams.java index 1200883d..0a3663b9 100644 --- a/src/clickTrainDetector/ClickTrainParams.java +++ b/src/clickTrainDetector/ClickTrainParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamUtils; import clickTrainDetector.classification.CTClassifierParams; import clickTrainDetector.classification.simplechi2classifier.Chi2ThresholdParams; @@ -127,7 +128,7 @@ public class ClickTrainParams implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/classification/CTClassifierParams.java b/src/clickTrainDetector/classification/CTClassifierParams.java index 91d4e8a7..64fa96e9 100644 --- a/src/clickTrainDetector/classification/CTClassifierParams.java +++ b/src/clickTrainDetector/classification/CTClassifierParams.java @@ -5,6 +5,7 @@ import java.util.UUID; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; @@ -68,7 +69,7 @@ public class CTClassifierParams implements Cloneable, Serializable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/classification/templateClassifier/SpectrumTemplateParams.java b/src/clickTrainDetector/classification/templateClassifier/SpectrumTemplateParams.java index 83a326c8..b3e26d12 100644 --- a/src/clickTrainDetector/classification/templateClassifier/SpectrumTemplateParams.java +++ b/src/clickTrainDetector/classification/templateClassifier/SpectrumTemplateParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * @@ -53,7 +54,7 @@ public class SpectrumTemplateParams implements Serializable, Cloneable, ManagedP @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTChi2Params.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTChi2Params.java index 499fee9f..046c6478 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTChi2Params.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTChi2Params.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameters class must extend this. @@ -38,7 +39,7 @@ public class MHTChi2Params implements Cloneable, Serializable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTKernelParams.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTKernelParams.java index 964073dc..d40d95e0 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTKernelParams.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTKernelParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Options for the MHT algorithm @@ -63,7 +64,7 @@ public class MHTKernelParams implements Cloneable, Serializable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTParams.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTParams.java index 06f6e6f4..cd75fc67 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTParams.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/MHTParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameters for the MHT algorithm, contains two serializable parameter @@ -47,7 +48,7 @@ public class MHTParams implements Serializable, Cloneable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/electricalNoiseFilter/SimpleElectricalNoiseParams.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/electricalNoiseFilter/SimpleElectricalNoiseParams.java index 6a6c3611..8773e368 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/electricalNoiseFilter/SimpleElectricalNoiseParams.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/electricalNoiseFilter/SimpleElectricalNoiseParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * @@ -29,7 +30,7 @@ public class SimpleElectricalNoiseParams implements Serializable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/SimpleChi2VarParams.java b/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/SimpleChi2VarParams.java index 94a578df..01b877d5 100644 --- a/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/SimpleChi2VarParams.java +++ b/src/clickTrainDetector/clickTrainAlgorithms/mht/mhtvar/SimpleChi2VarParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameters class for a simple chi2 variable. Contains the expected error in the @@ -194,7 +195,7 @@ public class SimpleChi2VarParams implements Serializable, Cloneable, ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clickTrainDetector/dataselector/CTSelectParams.java b/src/clickTrainDetector/dataselector/CTSelectParams.java index 5343f020..fa6f26dd 100644 --- a/src/clickTrainDetector/dataselector/CTSelectParams.java +++ b/src/clickTrainDetector/dataselector/CTSelectParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.dataSelector.DataSelectParams; import clickDetector.dataSelector.ClickTrainSelectParameters; @@ -93,7 +94,7 @@ public class CTSelectParams extends DataSelectParams implements Serializable, Cl @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/clipgenerator/ClipGenSetting.java b/src/clipgenerator/ClipGenSetting.java index ee834658..eb0dd0e0 100644 --- a/src/clipgenerator/ClipGenSetting.java +++ b/src/clipgenerator/ClipGenSetting.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; /** @@ -115,7 +116,7 @@ public class ClipGenSetting implements Serializable, Cloneable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("hadMapLine"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clipgenerator/ClipSettings.java b/src/clipgenerator/ClipSettings.java index 8636600a..aada971c 100644 --- a/src/clipgenerator/ClipSettings.java +++ b/src/clipgenerator/ClipSettings.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; /** @@ -135,7 +136,7 @@ public class ClipSettings implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("clipGenSettings"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/clipgenerator/clipDisplay/ClipDisplayParameters.java b/src/clipgenerator/clipDisplay/ClipDisplayParameters.java index 15571e2d..15858716 100644 --- a/src/clipgenerator/clipDisplay/ClipDisplayParameters.java +++ b/src/clipgenerator/clipDisplay/ClipDisplayParameters.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamView.ColourArray.ColourArrayType; @@ -88,7 +89,7 @@ public class ClipDisplayParameters implements Cloneable, Serializable, ManagedPa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("maxClips"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/crossedbearinglocaliser/CBLocaliserSettngs.java b/src/crossedbearinglocaliser/CBLocaliserSettngs.java index e296f276..a323eee3 100644 --- a/src/crossedbearinglocaliser/CBLocaliserSettngs.java +++ b/src/crossedbearinglocaliser/CBLocaliserSettngs.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.PamDataBlock; import annotation.localise.targetmotion.TMAnnotationOptions; @@ -78,7 +79,7 @@ public class CBLocaliserSettngs implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/d3/D3DataMapPoint.java b/src/d3/D3DataMapPoint.java index c42595f2..78962aa1 100644 --- a/src/d3/D3DataMapPoint.java +++ b/src/d3/D3DataMapPoint.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import fileOfflineData.OfflineFileMapPoint; @@ -30,7 +31,7 @@ public class D3DataMapPoint extends OfflineFileMapPoint implements ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("fileOffsetStart"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/dataGram/Datagram.java b/src/dataGram/Datagram.java index 33b72d42..eaafcf3f 100644 --- a/src/dataGram/Datagram.java +++ b/src/dataGram/Datagram.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import binaryFileStorage.BinaryTypes; import dataMap.OfflineDataMapPoint; @@ -175,7 +176,7 @@ public class Datagram implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/dataGram/DatagramDataPoint.java b/src/dataGram/DatagramDataPoint.java index 92da4033..dc27f4c6 100644 --- a/src/dataGram/DatagramDataPoint.java +++ b/src/dataGram/DatagramDataPoint.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class DatagramDataPoint implements Serializable, ManagedParameters { @@ -89,7 +90,7 @@ public class DatagramDataPoint implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("nDataUnits"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/dataGram/DatagramSettings.java b/src/dataGram/DatagramSettings.java index e466d55d..a3b5a4df 100644 --- a/src/dataGram/DatagramSettings.java +++ b/src/dataGram/DatagramSettings.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class DatagramSettings implements Serializable, Cloneable, ManagedParameters { @@ -33,7 +34,7 @@ public class DatagramSettings implements Serializable, Cloneable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/dataMap/DataMapParameters.java b/src/dataMap/DataMapParameters.java index 22eed747..cd714199 100644 --- a/src/dataMap/DataMapParameters.java +++ b/src/dataMap/DataMapParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class DataMapParameters implements Cloneable, Serializable, ManagedParameters { @@ -36,7 +37,7 @@ public class DataMapParameters implements Cloneable, Serializable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/dataMap/OfflineDataMapPoint.java b/src/dataMap/OfflineDataMapPoint.java index af9e1910..d1e8917c 100644 --- a/src/dataMap/OfflineDataMapPoint.java +++ b/src/dataMap/OfflineDataMapPoint.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamCalendar; /** @@ -251,7 +252,7 @@ abstract public class OfflineDataMapPoint implements Comparable, Ma @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/fftManager/FFTDataDisplayOptions.java b/src/fftManager/FFTDataDisplayOptions.java index a6d17619..969a7a81 100644 --- a/src/fftManager/FFTDataDisplayOptions.java +++ b/src/fftManager/FFTDataDisplayOptions.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class FFTDataDisplayOptions implements Serializable, Cloneable, ManagedParameters { @@ -45,7 +46,7 @@ public class FFTDataDisplayOptions implements Serializable, Cloneable, ManagedPa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("maxVal"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/fileOfflineData/OfflineFileParams.java b/src/fileOfflineData/OfflineFileParams.java index b31d547b..82e4d61a 100644 --- a/src/fileOfflineData/OfflineFileParams.java +++ b/src/fileOfflineData/OfflineFileParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class OfflineFileParams implements Serializable, Cloneable, ManagedParameters { @@ -28,7 +29,7 @@ public class OfflineFileParams implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/generalDatabase/DBParameters.java b/src/generalDatabase/DBParameters.java index ce8103d7..ab06c212 100644 --- a/src/generalDatabase/DBParameters.java +++ b/src/generalDatabase/DBParameters.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class DBParameters implements Cloneable, Serializable, ManagedParameters { @@ -75,7 +76,7 @@ public class DBParameters implements Cloneable, Serializable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); if (databaseName != null) { try { Field field = this.getClass().getDeclaredField("databaseName"); diff --git a/src/generalDatabase/MySQLParameters.java b/src/generalDatabase/MySQLParameters.java index 9491a6da..cadd498f 100644 --- a/src/generalDatabase/MySQLParameters.java +++ b/src/generalDatabase/MySQLParameters.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class MySQLParameters implements Cloneable, Serializable, ManagedParameters { @@ -37,7 +38,7 @@ public class MySQLParameters implements Cloneable, Serializable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("databaseName"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/generalDatabase/dataExport/ValueFilterParams.java b/src/generalDatabase/dataExport/ValueFilterParams.java index 15ffdfbd..52c57cfd 100644 --- a/src/generalDatabase/dataExport/ValueFilterParams.java +++ b/src/generalDatabase/dataExport/ValueFilterParams.java @@ -6,6 +6,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Abstract class for ValueFilters for filtering database data tables. @@ -110,7 +111,7 @@ public abstract class ValueFilterParams implements Cloneable, Serializable, Mana @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/generalDatabase/lookupTables/LookupItem.java b/src/generalDatabase/lookupTables/LookupItem.java index 7115406e..9ee05094 100644 --- a/src/generalDatabase/lookupTables/LookupItem.java +++ b/src/generalDatabase/lookupTables/LookupItem.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.PamSymbol; import PamView.PamSymbolType; @@ -232,7 +233,7 @@ public class LookupItem implements Cloneable, Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/generalDatabase/lookupTables/LookupList.java b/src/generalDatabase/lookupTables/LookupList.java index 8e0a0e0c..7714cdd3 100644 --- a/src/generalDatabase/lookupTables/LookupList.java +++ b/src/generalDatabase/lookupTables/LookupList.java @@ -7,6 +7,7 @@ import java.util.Vector; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Handles information for a single list from the look up table @@ -253,7 +254,7 @@ public class LookupList implements Cloneable, Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/gpl/GPLParameters.java b/src/gpl/GPLParameters.java index 666c559e..fcda34d9 100644 --- a/src/gpl/GPLParameters.java +++ b/src/gpl/GPLParameters.java @@ -6,6 +6,7 @@ import java.io.Serializable; import PamModel.parametermanager.FieldNotFoundException; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.blockprocess.PamBlockParams; import gpl.contour.ContourMerge; @@ -198,7 +199,7 @@ public class GPLParameters implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { ps.findParameterData("minPeakGap").setInfo("Minimum gap", "bins", "Minimum gap between peaks (FFT time bins)"); ps.findParameterData("minCallLengthSeconds").setInfo("Minimum length", "bins", "Minimum length of a detection in seconds"); diff --git a/src/group3dlocaliser/Group3DParams.java b/src/group3dlocaliser/Group3DParams.java index fbf75743..6467a9ad 100644 --- a/src/group3dlocaliser/Group3DParams.java +++ b/src/group3dlocaliser/Group3DParams.java @@ -6,6 +6,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import PamView.GroupedSourceParameters; import group3dlocaliser.algorithm.LocaliserAlgorithm3D; @@ -126,7 +127,7 @@ public class Group3DParams implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("algorithmSpecificParams"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/group3dlocaliser/algorithm/gridsearch/MFPGridSearchParams.java b/src/group3dlocaliser/algorithm/gridsearch/MFPGridSearchParams.java index 7f7e1371..735d70f1 100644 --- a/src/group3dlocaliser/algorithm/gridsearch/MFPGridSearchParams.java +++ b/src/group3dlocaliser/algorithm/gridsearch/MFPGridSearchParams.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class MFPGridSearchParams implements Serializable, Cloneable, ManagedParameters { @@ -25,7 +26,7 @@ public class MFPGridSearchParams implements Serializable, Cloneable, ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("fftLength"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/group3dlocaliser/algorithm/gridsearch/TOADGridParams.java b/src/group3dlocaliser/algorithm/gridsearch/TOADGridParams.java index 6f2a0fb2..8c845ffe 100644 --- a/src/group3dlocaliser/algorithm/gridsearch/TOADGridParams.java +++ b/src/group3dlocaliser/algorithm/gridsearch/TOADGridParams.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import group3dlocaliser.grids.SphericalGrid; import pamMaths.PamVector; @@ -49,7 +50,7 @@ public class TOADGridParams implements Serializable, Cloneable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("gridType"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/group3dlocaliser/algorithm/toadbase/TOADBaseParams.java b/src/group3dlocaliser/algorithm/toadbase/TOADBaseParams.java index 1aa70f8a..b0e11190 100644 --- a/src/group3dlocaliser/algorithm/toadbase/TOADBaseParams.java +++ b/src/group3dlocaliser/algorithm/toadbase/TOADBaseParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameters that apply to all TOAD based localisers. @@ -117,7 +118,7 @@ public class TOADBaseParams implements Cloneable, Serializable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/group3dlocaliser/dataselector/Group3DDataSelectParams.java b/src/group3dlocaliser/dataselector/Group3DDataSelectParams.java index 879eae8b..86e2397d 100644 --- a/src/group3dlocaliser/dataselector/Group3DDataSelectParams.java +++ b/src/group3dlocaliser/dataselector/Group3DDataSelectParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.dataSelector.DataSelectParams; public class Group3DDataSelectParams extends DataSelectParams implements Serializable, Cloneable, ManagedParameters { @@ -28,7 +29,7 @@ public class Group3DDataSelectParams extends DataSelectParams implements Seriali @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/group3dlocaliser/grids/SphericalGridParams.java b/src/group3dlocaliser/grids/SphericalGridParams.java index f323ee53..5ece7b39 100644 --- a/src/group3dlocaliser/grids/SphericalGridParams.java +++ b/src/group3dlocaliser/grids/SphericalGridParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class SphericalGridParams implements Serializable, Cloneable, ManagedParameters { @@ -88,7 +89,7 @@ public class SphericalGridParams implements Serializable, Cloneable, ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/group3dlocaliser/grouper/DetectionGrouperParams.java b/src/group3dlocaliser/grouper/DetectionGrouperParams.java index 5d92afd1..d789b054 100644 --- a/src/group3dlocaliser/grouper/DetectionGrouperParams.java +++ b/src/group3dlocaliser/grouper/DetectionGrouperParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class DetectionGrouperParams implements Serializable, Cloneable, ManagedParameters { @@ -38,7 +39,7 @@ public class DetectionGrouperParams implements Serializable, Cloneable, ManagedP @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/landMarks/LandmarkData.java b/src/landMarks/LandmarkData.java index 2be20e55..ef0cb561 100644 --- a/src/landMarks/LandmarkData.java +++ b/src/landMarks/LandmarkData.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamController.masterReference.MasterReferencePoint; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.LatLong; import PamView.PamSymbol; @@ -49,7 +50,7 @@ public class LandmarkData extends Object implements Serializable, Cloneable, Man @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/landMarks/LandmarkDatas.java b/src/landMarks/LandmarkDatas.java index d4cd386d..73fb4785 100644 --- a/src/landMarks/LandmarkDatas.java +++ b/src/landMarks/LandmarkDatas.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class LandmarkDatas implements Serializable, Cloneable, ManagedParameters { @@ -76,7 +77,7 @@ public class LandmarkDatas implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/levelMeter/LevelMeterParams.java b/src/levelMeter/LevelMeterParams.java index dd1b6752..c25860dc 100644 --- a/src/levelMeter/LevelMeterParams.java +++ b/src/levelMeter/LevelMeterParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class LevelMeterParams implements Cloneable, Serializable, ManagedParameters { @@ -35,7 +36,7 @@ public class LevelMeterParams implements Cloneable, Serializable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/likelihoodDetectionModule/AcquisitionSettings.java b/src/likelihoodDetectionModule/AcquisitionSettings.java index abf61481..10033395 100644 --- a/src/likelihoodDetectionModule/AcquisitionSettings.java +++ b/src/likelihoodDetectionModule/AcquisitionSettings.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * The AcquisitionSettings class provides a module-local storage object for holding @@ -48,7 +49,7 @@ public class AcquisitionSettings implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/likelihoodDetectionModule/ConfigurationDialogSettings.java b/src/likelihoodDetectionModule/ConfigurationDialogSettings.java index 6856e501..9011ffbd 100644 --- a/src/likelihoodDetectionModule/ConfigurationDialogSettings.java +++ b/src/likelihoodDetectionModule/ConfigurationDialogSettings.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * The Class ConfigurationDialogSettings holds parameters about the @@ -28,7 +29,7 @@ public class ConfigurationDialogSettings implements Serializable, ManagedParamet @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("expandedState"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/likelihoodDetectionModule/GuardBand.java b/src/likelihoodDetectionModule/GuardBand.java index ccd9a6c0..cb07df7b 100644 --- a/src/likelihoodDetectionModule/GuardBand.java +++ b/src/likelihoodDetectionModule/GuardBand.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Represents the parameters that make up a guard band, used as @@ -106,7 +107,7 @@ public class GuardBand implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/likelihoodDetectionModule/LikelihoodDetectionParameters.java b/src/likelihoodDetectionModule/LikelihoodDetectionParameters.java index 9c405cdd..9481c26c 100644 --- a/src/likelihoodDetectionModule/LikelihoodDetectionParameters.java +++ b/src/likelihoodDetectionModule/LikelihoodDetectionParameters.java @@ -11,6 +11,7 @@ import java.util.Iterator; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * The Class LikelihoodDetectionParameters provides the standard PamGuard @@ -271,7 +272,7 @@ public class LikelihoodDetectionParameters implements Serializable, Cloneable, M @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/likelihoodDetectionModule/LikelihoodFFTParameters.java b/src/likelihoodDetectionModule/LikelihoodFFTParameters.java index e90e51c8..13b85fda 100644 --- a/src/likelihoodDetectionModule/LikelihoodFFTParameters.java +++ b/src/likelihoodDetectionModule/LikelihoodFFTParameters.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import fftManager.FFTParameters; import Spectrogram.WindowFunction; @@ -270,7 +271,7 @@ public class LikelihoodFFTParameters implements Serializable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("sourceNumber"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/likelihoodDetectionModule/SignalBand.java b/src/likelihoodDetectionModule/SignalBand.java index 00e7af96..3b3150d6 100644 --- a/src/likelihoodDetectionModule/SignalBand.java +++ b/src/likelihoodDetectionModule/SignalBand.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Represents the parameters that make up a signal band, used as @@ -151,7 +152,7 @@ public class SignalBand implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } } diff --git a/src/likelihoodDetectionModule/TargetConfiguration.java b/src/likelihoodDetectionModule/TargetConfiguration.java index 29c82890..0cc0e398 100644 --- a/src/likelihoodDetectionModule/TargetConfiguration.java +++ b/src/likelihoodDetectionModule/TargetConfiguration.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import likelihoodDetectionModule.normalizer.NormalizerProcess.NormalizerAlgorithm; /** @@ -453,7 +454,7 @@ public class TargetConfiguration implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/loggerForms/FormPlotOptions.java b/src/loggerForms/FormPlotOptions.java index 8f55f828..9763d8ba 100644 --- a/src/loggerForms/FormPlotOptions.java +++ b/src/loggerForms/FormPlotOptions.java @@ -6,6 +6,7 @@ import java.util.Arrays; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; /** @@ -95,7 +96,7 @@ public class FormPlotOptions implements Serializable, Cloneable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("controlChoices"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/loggerForms/FormSettings.java b/src/loggerForms/FormSettings.java index 199edfe4..f7553e13 100644 --- a/src/loggerForms/FormSettings.java +++ b/src/loggerForms/FormSettings.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Manage a bit of persistent data for a single Logger form description.

@@ -32,7 +33,7 @@ public class FormSettings implements Cloneable, Serializable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/loggerForms/monitor/FormsSelectorParams.java b/src/loggerForms/monitor/FormsSelectorParams.java index d691af0c..d1797798 100644 --- a/src/loggerForms/monitor/FormsSelectorParams.java +++ b/src/loggerForms/monitor/FormsSelectorParams.java @@ -7,6 +7,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.dataSelector.DataSelectParams; public class FormsSelectorParams extends DataSelectParams implements Cloneable, Serializable, ManagedParameters { @@ -30,7 +31,7 @@ public class FormsSelectorParams extends DataSelectParams implements Cloneable, @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("formSelection"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/ltsa/LtsaModuleHeader.java b/src/ltsa/LtsaModuleHeader.java index 0b5d888e..50c1034d 100644 --- a/src/ltsa/LtsaModuleHeader.java +++ b/src/ltsa/LtsaModuleHeader.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import binaryFileStorage.BinaryHeader; import binaryFileStorage.BinaryObjectData; import binaryFileStorage.ModuleHeader; @@ -37,7 +38,7 @@ public class LtsaModuleHeader extends ModuleHeader implements ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("fftLength"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/ltsa/LtsaParameters.java b/src/ltsa/LtsaParameters.java index 2dfd9a62..dbf62ec0 100644 --- a/src/ltsa/LtsaParameters.java +++ b/src/ltsa/LtsaParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class LtsaParameters implements Cloneable, Serializable, ManagedParameters { @@ -35,7 +36,7 @@ public class LtsaParameters implements Cloneable, Serializable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } } diff --git a/src/mapgrouplocaliser/MapGrouperSettings.java b/src/mapgrouplocaliser/MapGrouperSettings.java index 955542dc..440700b7 100644 --- a/src/mapgrouplocaliser/MapGrouperSettings.java +++ b/src/mapgrouplocaliser/MapGrouperSettings.java @@ -7,6 +7,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.paneloverlay.OverlayDataInfo; import PamView.paneloverlay.overlaymark.MarkDataSelectorParams; import PamView.paneloverlay.overlaymark.OverlayMarkDataInfo; @@ -64,7 +65,7 @@ public class MapGrouperSettings implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("markDataSelectorParams"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/matchedTemplateClassifer/MTClassifier.java b/src/matchedTemplateClassifer/MTClassifier.java index 476d4735..377fc135 100644 --- a/src/matchedTemplateClassifer/MTClassifier.java +++ b/src/matchedTemplateClassifer/MTClassifier.java @@ -15,6 +15,7 @@ import Localiser.DelayMeasurementParams; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamArrayUtils; import PamUtils.PamInterp; import PamUtils.complex.ComplexArray; @@ -583,7 +584,7 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters */ @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("inteprWaveformReject"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/matchedTemplateClassifer/MatchTemplate.java b/src/matchedTemplateClassifer/MatchTemplate.java index 7f758ddf..0990a22d 100644 --- a/src/matchedTemplateClassifer/MatchTemplate.java +++ b/src/matchedTemplateClassifer/MatchTemplate.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.RawDataHolder; import PamguardMVC.RawDataTransforms; @@ -51,7 +52,7 @@ public class MatchTemplate implements RawDataHolder, Serializable, Cloneable, Ma @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/matchedTemplateClassifer/MatchedTemplateParams.java b/src/matchedTemplateClassifer/MatchedTemplateParams.java index 05f8b107..edd2b912 100644 --- a/src/matchedTemplateClassifer/MatchedTemplateParams.java +++ b/src/matchedTemplateClassifer/MatchedTemplateParams.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamView.PamSymbolType; import PamView.symbol.SymbolData; import fftFilter.FFTFilterParams; @@ -150,7 +151,7 @@ public class MatchedTemplateParams implements Serializable, Cloneable, ManagedPa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("fftFilterParams"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/mcc/mccacquisition/MCCDaqParams.java b/src/mcc/mccacquisition/MCCDaqParams.java index f1fc3450..ba44c99e 100644 --- a/src/mcc/mccacquisition/MCCDaqParams.java +++ b/src/mcc/mccacquisition/MCCDaqParams.java @@ -5,6 +5,7 @@ import java.io.Serializable; import Acquisition.DaqSystemXMLManager; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import analoginput.AnalogRangeData; import simulatedAcquisition.SimProcess; @@ -51,7 +52,7 @@ public class MCCDaqParams implements Serializable, Cloneable, ManagedParameters return null; } - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/metadata/deployment/DeploymentData.java b/src/metadata/deployment/DeploymentData.java index d9aaaf19..4cf50403 100644 --- a/src/metadata/deployment/DeploymentData.java +++ b/src/metadata/deployment/DeploymentData.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamModel.parametermanager.FieldNotFoundException; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.LatLong; /** @@ -132,7 +133,7 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { ps.findParameterData("id").setInfo("Unique Id", null, "String that uniquely identifies this deployment", 128); // ps.setOrder("id", 0); diff --git a/src/metadata/deployment/QAData.java b/src/metadata/deployment/QAData.java index 368db614..22a7bd66 100644 --- a/src/metadata/deployment/QAData.java +++ b/src/metadata/deployment/QAData.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Largely the content of the Tethys QualityAssurance schema @@ -33,7 +34,7 @@ public class QAData implements Serializable, Cloneable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/networkTransfer/emulator/EmulatorParams.java b/src/networkTransfer/emulator/EmulatorParams.java index e4fe0f74..3f38266a 100644 --- a/src/networkTransfer/emulator/EmulatorParams.java +++ b/src/networkTransfer/emulator/EmulatorParams.java @@ -8,6 +8,7 @@ import PamController.PamControlledUnitSettings; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.LatLong; public class EmulatorParams implements Cloneable, Serializable, ManagedParameters { @@ -50,7 +51,7 @@ public class EmulatorParams implements Cloneable, Serializable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("circleRadius"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/networkTransfer/receive/BuoyStatusData.java b/src/networkTransfer/receive/BuoyStatusData.java index 6b17a6ad..8138f4bf 100644 --- a/src/networkTransfer/receive/BuoyStatusData.java +++ b/src/networkTransfer/receive/BuoyStatusData.java @@ -5,6 +5,7 @@ import java.util.Hashtable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamCalendar; /** @@ -140,7 +141,7 @@ public class BuoyStatusData implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/networkTransfer/receive/BuoyStatusValue.java b/src/networkTransfer/receive/BuoyStatusValue.java index d73a5acd..a42ea7ff 100644 --- a/src/networkTransfer/receive/BuoyStatusValue.java +++ b/src/networkTransfer/receive/BuoyStatusValue.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamUtils.PamCalendar; public class BuoyStatusValue implements Serializable, ManagedParameters { @@ -53,7 +54,7 @@ public class BuoyStatusValue implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/networkTransfer/receive/NetworkReceiveParams.java b/src/networkTransfer/receive/NetworkReceiveParams.java index f067c789..488899c5 100644 --- a/src/networkTransfer/receive/NetworkReceiveParams.java +++ b/src/networkTransfer/receive/NetworkReceiveParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NetworkReceiveParams implements Cloneable, Serializable, ManagedParameters { @@ -40,7 +41,7 @@ public class NetworkReceiveParams implements Cloneable, Serializable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/networkTransfer/send/NetworkSendParams.java b/src/networkTransfer/send/NetworkSendParams.java index 9e0024d8..87e25b0c 100644 --- a/src/networkTransfer/send/NetworkSendParams.java +++ b/src/networkTransfer/send/NetworkSendParams.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.PamDataBlock; public class NetworkSendParams implements Serializable, Cloneable, ManagedParameters { @@ -108,7 +109,7 @@ public class NetworkSendParams implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("selectedDataBlocks"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/nidaqdev/networkdaq/NINetworkDaqParams.java b/src/nidaqdev/networkdaq/NINetworkDaqParams.java index 7458739e..a7f67845 100644 --- a/src/nidaqdev/networkdaq/NINetworkDaqParams.java +++ b/src/nidaqdev/networkdaq/NINetworkDaqParams.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import Acquisition.DaqSystemXMLManager; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NINetworkDaqParams implements Serializable, Cloneable, ManagedParameters { @@ -169,7 +170,7 @@ public class NINetworkDaqParams implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { if (DaqSystemXMLManager.isSelected(NINetworkDaq.systemName)) { - return PamParameterSet.autoGenerate(this); + return PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); } return null; } diff --git a/src/nmeaEmulator/NMEAEmulatorParams.java b/src/nmeaEmulator/NMEAEmulatorParams.java index 1889ca17..d7b89529 100644 --- a/src/nmeaEmulator/NMEAEmulatorParams.java +++ b/src/nmeaEmulator/NMEAEmulatorParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NMEAEmulatorParams implements Serializable, Cloneable, ManagedParameters { @@ -23,7 +24,7 @@ public class NMEAEmulatorParams implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/noiseBandMonitor/NoiseBandSettings.java b/src/noiseBandMonitor/NoiseBandSettings.java index d905c536..8f00571c 100644 --- a/src/noiseBandMonitor/NoiseBandSettings.java +++ b/src/noiseBandMonitor/NoiseBandSettings.java @@ -7,6 +7,7 @@ import Filters.FilterType; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NoiseBandSettings implements Serializable, Cloneable, ManagedParameters { @@ -59,7 +60,7 @@ public class NoiseBandSettings implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("showStandard"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/noiseMonitor/NoiseDisplaySettings.java b/src/noiseMonitor/NoiseDisplaySettings.java index 7ef4f5de..b70db3ac 100644 --- a/src/noiseMonitor/NoiseDisplaySettings.java +++ b/src/noiseMonitor/NoiseDisplaySettings.java @@ -6,6 +6,7 @@ import java.util.Arrays; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class NoiseDisplaySettings implements Serializable, Cloneable, ManagedParameters { @@ -70,7 +71,7 @@ public class NoiseDisplaySettings implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("displayLengthSeconds"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/noiseMonitor/NoiseMeasurementBand.java b/src/noiseMonitor/NoiseMeasurementBand.java index 9540f9fb..8f042684 100644 --- a/src/noiseMonitor/NoiseMeasurementBand.java +++ b/src/noiseMonitor/NoiseMeasurementBand.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NoiseMeasurementBand implements Serializable, Cloneable, ManagedParameters { @@ -102,7 +103,7 @@ public class NoiseMeasurementBand implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } } diff --git a/src/noiseMonitor/NoiseSettings.java b/src/noiseMonitor/NoiseSettings.java index b997c22c..a1f301fc 100644 --- a/src/noiseMonitor/NoiseSettings.java +++ b/src/noiseMonitor/NoiseSettings.java @@ -8,6 +8,7 @@ import java.util.ListIterator; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NoiseSettings implements Serializable, Cloneable, ManagedParameters { @@ -160,7 +161,7 @@ public class NoiseSettings implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("measurementBands"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/noiseMonitor/alarm/NoiseAlarmParameters.java b/src/noiseMonitor/alarm/NoiseAlarmParameters.java index d2f7ec60..551d430c 100644 --- a/src/noiseMonitor/alarm/NoiseAlarmParameters.java +++ b/src/noiseMonitor/alarm/NoiseAlarmParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class NoiseAlarmParameters implements Serializable, Cloneable, ManagedParameters { @@ -26,7 +27,7 @@ public class NoiseAlarmParameters implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } } diff --git a/src/noiseOneBand/OneBandAlarmParameters.java b/src/noiseOneBand/OneBandAlarmParameters.java index 90a1659c..3e4e5a69 100644 --- a/src/noiseOneBand/OneBandAlarmParameters.java +++ b/src/noiseOneBand/OneBandAlarmParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class OneBandAlarmParameters implements Serializable, Cloneable, ManagedParameters { @@ -37,7 +38,7 @@ public class OneBandAlarmParameters implements Serializable, Cloneable, ManagedP @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/noiseOneBand/OneBandDisplayParams.java b/src/noiseOneBand/OneBandDisplayParams.java index 04cf01d7..fe9b7a82 100644 --- a/src/noiseOneBand/OneBandDisplayParams.java +++ b/src/noiseOneBand/OneBandDisplayParams.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; public class OneBandDisplayParams implements Serializable, Cloneable, ManagedParameters { @@ -61,7 +62,7 @@ public class OneBandDisplayParams implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("displayChannels"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/noiseOneBand/OneBandParameters.java b/src/noiseOneBand/OneBandParameters.java index b35e4f68..ea2f3320 100644 --- a/src/noiseOneBand/OneBandParameters.java +++ b/src/noiseOneBand/OneBandParameters.java @@ -10,6 +10,7 @@ import Filters.FilterType; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class OneBandParameters implements Serializable, Cloneable, ManagedParameters { @@ -235,7 +236,7 @@ public class OneBandParameters implements Serializable, Cloneable, ManagedParame @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("filterParams"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/noiseOneBand/offline/OneBandSummaryParams.java b/src/noiseOneBand/offline/OneBandSummaryParams.java index 7565c6e7..14e71708 100644 --- a/src/noiseOneBand/offline/OneBandSummaryParams.java +++ b/src/noiseOneBand/offline/OneBandSummaryParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class OneBandSummaryParams implements Serializable, Cloneable, ManagedParameters { @@ -23,7 +24,7 @@ public class OneBandSummaryParams implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/offlineProcessing/TaskGroupParams.java b/src/offlineProcessing/TaskGroupParams.java index 25497c24..c04a88cc 100644 --- a/src/offlineProcessing/TaskGroupParams.java +++ b/src/offlineProcessing/TaskGroupParams.java @@ -8,6 +8,7 @@ import java.util.Arrays; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameter control for offline task groups. @@ -137,7 +138,7 @@ public class TaskGroupParams implements Cloneable, Serializable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("taskSelection"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/quickAnnotation/QuickAnnotationParameters.java b/src/quickAnnotation/QuickAnnotationParameters.java index fd8ed2a7..65e3b05b 100644 --- a/src/quickAnnotation/QuickAnnotationParameters.java +++ b/src/quickAnnotation/QuickAnnotationParameters.java @@ -9,6 +9,7 @@ import PamController.PamSettingManager; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import difar.DifarControl; import difar.DifarParameters; import generalDatabase.lookupTables.LookupItem; @@ -96,7 +97,7 @@ public class QuickAnnotationParameters implements Serializable, Cloneable, Manag @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("exportClips"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLPredDisplayParams.java b/src/rawDeepLearningClassifier/dataPlotFX/DLPredDisplayParams.java index 7ad1ffb9..41ef495e 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLPredDisplayParams.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLPredDisplayParams.java @@ -3,6 +3,7 @@ package rawDeepLearningClassifier.dataPlotFX; import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import javafx.scene.paint.Color; /** @@ -25,7 +26,7 @@ public class DLPredDisplayParams implements Serializable, Cloneable, ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/rocca/RoccaParameters.java b/src/rocca/RoccaParameters.java index bec1fecb..c7db4aae 100644 --- a/src/rocca/RoccaParameters.java +++ b/src/rocca/RoccaParameters.java @@ -30,6 +30,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameters for Rocca
@@ -856,7 +857,7 @@ public class RoccaParameters implements Serializable, Cloneable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("runAncCalcs4Clicks"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/seismicVeto/VetoParameters.java b/src/seismicVeto/VetoParameters.java index 0ca08357..e41907f7 100644 --- a/src/seismicVeto/VetoParameters.java +++ b/src/seismicVeto/VetoParameters.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class VetoParameters implements Serializable, Cloneable, ManagedParameters { @@ -40,7 +41,7 @@ public class VetoParameters implements Serializable, Cloneable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("backgroundConstant"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/serialComms/SerialPortParameters.java b/src/serialComms/SerialPortParameters.java index bb9d699c..373852fd 100644 --- a/src/serialComms/SerialPortParameters.java +++ b/src/serialComms/SerialPortParameters.java @@ -29,6 +29,7 @@ import com.fazecast.jSerialComm.SerialPort; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * @author David McLaren, Paul Redmond @@ -166,7 +167,7 @@ public class SerialPortParameters implements Serializable, Cloneable, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/simulatedAcquisition/SimObject.java b/src/simulatedAcquisition/SimObject.java index 053c8a9b..335139fb 100644 --- a/src/simulatedAcquisition/SimObject.java +++ b/src/simulatedAcquisition/SimObject.java @@ -5,6 +5,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import simulatedAcquisition.movement.MovementModel; import simulatedAcquisition.movement.MovementModels; @@ -150,7 +151,7 @@ public class SimObject implements Serializable, Cloneable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("depth"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/simulatedAcquisition/movement/CircularMovementParams.java b/src/simulatedAcquisition/movement/CircularMovementParams.java index 4fe63e95..c956209e 100644 --- a/src/simulatedAcquisition/movement/CircularMovementParams.java +++ b/src/simulatedAcquisition/movement/CircularMovementParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class CircularMovementParams implements Serializable, Cloneable, ManagedParameters { @@ -84,7 +85,7 @@ public class CircularMovementParams implements Serializable, Cloneable, ManagedP @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/simulatedAcquisition/movement/GridMovementParams.java b/src/simulatedAcquisition/movement/GridMovementParams.java index 210ac943..72ae6cb2 100644 --- a/src/simulatedAcquisition/movement/GridMovementParams.java +++ b/src/simulatedAcquisition/movement/GridMovementParams.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class GridMovementParams implements Serializable, Cloneable, ManagedParameters { @@ -42,7 +43,7 @@ public class GridMovementParams implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("distRangeMetres"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/soundPlayback/PlaybackParameters.java b/src/soundPlayback/PlaybackParameters.java index 08c1e56a..2d7d22d1 100644 --- a/src/soundPlayback/PlaybackParameters.java +++ b/src/soundPlayback/PlaybackParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameters controlling sound playback @@ -165,7 +166,7 @@ public class PlaybackParameters implements Cloneable, Serializable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/soundPlayback/preprocess/EnvelopeParams.java b/src/soundPlayback/preprocess/EnvelopeParams.java index b3e0587c..7dd143e9 100644 --- a/src/soundPlayback/preprocess/EnvelopeParams.java +++ b/src/soundPlayback/preprocess/EnvelopeParams.java @@ -7,6 +7,7 @@ import Filters.FilterParams; import Filters.FilterType; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class EnvelopeParams implements Cloneable, Serializable, ManagedParameters { @@ -87,7 +88,7 @@ public class EnvelopeParams implements Cloneable, Serializable, ManagedParameter @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/soundtrap/STToolsParams.java b/src/soundtrap/STToolsParams.java index 722c6fb6..514b96d6 100644 --- a/src/soundtrap/STToolsParams.java +++ b/src/soundtrap/STToolsParams.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class STToolsParams implements Serializable, Cloneable, ManagedParameters { @@ -62,7 +63,7 @@ public class STToolsParams implements Serializable, Cloneable, ManagedParameters @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/spectrogramNoiseReduction/SpectrogramNoiseSettings.java b/src/spectrogramNoiseReduction/SpectrogramNoiseSettings.java index 0931c963..9cfa3765 100644 --- a/src/spectrogramNoiseReduction/SpectrogramNoiseSettings.java +++ b/src/spectrogramNoiseReduction/SpectrogramNoiseSettings.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class SpectrogramNoiseSettings implements Serializable, Cloneable, ManagedParameters { @@ -79,7 +80,7 @@ public class SpectrogramNoiseSettings implements Serializable, Cloneable, Manage @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("runMethod"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/spectrogramNoiseReduction/averageSubtraction/AverageSubtractionParameters.java b/src/spectrogramNoiseReduction/averageSubtraction/AverageSubtractionParameters.java index 23e9f1f7..18f1811e 100644 --- a/src/spectrogramNoiseReduction/averageSubtraction/AverageSubtractionParameters.java +++ b/src/spectrogramNoiseReduction/averageSubtraction/AverageSubtractionParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class AverageSubtractionParameters implements Serializable, Cloneable, ManagedParameters { @@ -25,7 +26,7 @@ public class AverageSubtractionParameters implements Serializable, Cloneable, Ma @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/spectrogramNoiseReduction/medianFilter/MedianFilterParams.java b/src/spectrogramNoiseReduction/medianFilter/MedianFilterParams.java index fe91beba..c24aed99 100644 --- a/src/spectrogramNoiseReduction/medianFilter/MedianFilterParams.java +++ b/src/spectrogramNoiseReduction/medianFilter/MedianFilterParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class MedianFilterParams implements Serializable, Cloneable, ManagedParameters { @@ -25,7 +26,7 @@ public class MedianFilterParams implements Serializable, Cloneable, ManagedParam @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/spectrogramNoiseReduction/threshold/ThresholdParams.java b/src/spectrogramNoiseReduction/threshold/ThresholdParams.java index 8842e6e0..5674d577 100644 --- a/src/spectrogramNoiseReduction/threshold/ThresholdParams.java +++ b/src/spectrogramNoiseReduction/threshold/ThresholdParams.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class ThresholdParams implements Serializable, Cloneable, ManagedParameters { @@ -27,7 +28,7 @@ public class ThresholdParams implements Serializable, Cloneable, ManagedParamete @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index ba530ce0..57b254b0 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -140,7 +140,7 @@ public class AutoTethysProvider implements TethysDataProvider { // TODO Auto-generated catch block e.printStackTrace(); } - List genList = paramPacker.packParameters(pamControlledUnit); + List genList = paramPacker.packParameters(pamDataBlock); if (genList == null || genList.size() == 0) { return null; } diff --git a/src/tethys/pamdata/TethysParameterPacker.java b/src/tethys/pamdata/TethysParameterPacker.java index 9a076bf7..55535594 100644 --- a/src/tethys/pamdata/TethysParameterPacker.java +++ b/src/tethys/pamdata/TethysParameterPacker.java @@ -23,6 +23,8 @@ import PamController.settings.output.xml.PamguardXMLWriter; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterData; import PamModel.parametermanager.PamParameterSet; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamProcess; import nilus.MarshalXML; /** @@ -85,8 +87,10 @@ public class TethysParameterPacker { xmlWriter = PamguardXMLWriter.getXMLWriter(); } - public List packParameters(PamControlledUnit pamControlledUnit) { - if (pamControlledUnit instanceof PamSettings == false) { + public List packParameters(PamDataBlock pamDataBlock) { + PamProcess pamProcess = pamDataBlock.getParentProcess(); + PamControlledUnit pamControlledUnit = pamProcess.getPamControlledUnit(); + if (pamControlledUnit == null || pamControlledUnit instanceof PamSettings == false) { return null; } PamSettings pamSettings = (PamSettings) pamControlledUnit; @@ -139,13 +143,44 @@ public class TethysParameterPacker { } } elList.add(el); - Element pgEl = xmlWriter.writeUnitSettings(doc, el, pamSettings); - if (pgEl != null) { - el.appendChild(pgEl); -// elList.add(pgEl); + xmlWriter.setExcludeDisplaySettings(true); + xmlWriter.makeSettingsList(); + ArrayList moduleChain = getParentChain(pamDataBlock); + for (PamControlledUnit pcu : moduleChain) { + if (pcu instanceof PamSettings == false) { + continue; + } + pamSettings = (PamSettings) pcu; + Element pgEl = xmlWriter.writeUnitSettings(doc, el, pamSettings); + if (pgEl != null) { + el.appendChild(pgEl); + // elList.add(pgEl); + } } return elList; } + + /** + * Get a list of parent modules of the datablock, including it's own. + * @param dataBlock + * @return + */ + private ArrayList getParentChain(PamDataBlock dataBlock) { + ArrayList chain = new ArrayList<>(); + while (dataBlock != null) { + PamProcess parentProcess = dataBlock.getParentProcess(); + if (parentProcess == null) { + break; + } + PamControlledUnit pamControlledUnit = parentProcess.getPamControlledUnit(); + if (pamControlledUnit == null) { + break; + } + chain.add(pamControlledUnit); + dataBlock = parentProcess.getParentDataBlock(); + } + return chain; + } private boolean createElement(Document document, Element parentEl, Object paramData, PamParameterData pamParam, ArrayList objectHierarchy) { Class javaClass = paramData.getClass(); diff --git a/src/userDisplay/DisplayProviderParameters.java b/src/userDisplay/DisplayProviderParameters.java index 1a3c08d2..05f7fef2 100644 --- a/src/userDisplay/DisplayProviderParameters.java +++ b/src/userDisplay/DisplayProviderParameters.java @@ -6,6 +6,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Data stored in a list for each component of a User Display Panel. @@ -97,7 +98,7 @@ public class DisplayProviderParameters implements Serializable, Cloneable, Mana @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/userDisplay/UserDisplayParameters.java b/src/userDisplay/UserDisplayParameters.java index e7d605fe..9a0dd74f 100644 --- a/src/userDisplay/UserDisplayParameters.java +++ b/src/userDisplay/UserDisplayParameters.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamModel.parametermanager.PrivatePamParameterData; import Spectrogram.SpectrogramParameters; @@ -34,7 +35,7 @@ public class UserDisplayParameters implements Serializable, Cloneable, ManagedPa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); try { Field field = this.getClass().getDeclaredField("displayProviderParameters"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/userDisplay/UserFrameParameters.java b/src/userDisplay/UserFrameParameters.java index 9b99eff6..cfe9d317 100644 --- a/src/userDisplay/UserFrameParameters.java +++ b/src/userDisplay/UserFrameParameters.java @@ -5,6 +5,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; abstract public class UserFrameParameters implements Cloneable, Serializable, ManagedParameters { @@ -28,7 +29,7 @@ abstract public class UserFrameParameters implements Cloneable, Serializable, Ma @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DISPLAY); return ps; } diff --git a/src/whistleClassifier/FragmentClassifierParams.java b/src/whistleClassifier/FragmentClassifierParams.java index c5d17663..68c5f166 100644 --- a/src/whistleClassifier/FragmentClassifierParams.java +++ b/src/whistleClassifier/FragmentClassifierParams.java @@ -10,6 +10,7 @@ import Jama.Matrix; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; /** * Parameters class for fragmented whistle classification. @@ -250,7 +251,7 @@ public class FragmentClassifierParams implements Serializable, Cloneable, Manage */ @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("confusionMatrix"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/whistleClassifier/TrainingContour.java b/src/whistleClassifier/TrainingContour.java index 93e77348..9ea1b111 100644 --- a/src/whistleClassifier/TrainingContour.java +++ b/src/whistleClassifier/TrainingContour.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class TrainingContour implements Serializable, ManagedParameters { @@ -44,7 +45,7 @@ public class TrainingContour implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/whistleClassifier/TrainingDataSet.java b/src/whistleClassifier/TrainingDataSet.java index 03fa94a8..53e7e107 100644 --- a/src/whistleClassifier/TrainingDataSet.java +++ b/src/whistleClassifier/TrainingDataSet.java @@ -17,7 +17,7 @@ import PamModel.parametermanager.PamParameterSet; * @author Doug Gillespie * @see TrainingDataGroup */ -public class TrainingDataSet implements Serializable, ManagedParameters { +public class TrainingDataSet implements Serializable { static public final long serialVersionUID = 0; @@ -162,10 +162,4 @@ public class TrainingDataSet implements Serializable, ManagedParameters { // nContours = contours; // } - @Override - public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); - return ps; - } - } diff --git a/src/whistleClassifier/WhistleClassificationParameters.java b/src/whistleClassifier/WhistleClassificationParameters.java index 83351a65..92cde266 100644 --- a/src/whistleClassifier/WhistleClassificationParameters.java +++ b/src/whistleClassifier/WhistleClassificationParameters.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class WhistleClassificationParameters implements Cloneable, Serializable, ManagedParameters { @@ -84,7 +85,7 @@ public class WhistleClassificationParameters implements Cloneable, Serializable, @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("lowWhistleNumber"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/whistleClassifier/training/BatchTrainingParams.java b/src/whistleClassifier/training/BatchTrainingParams.java index 452426a7..bdb32a9f 100644 --- a/src/whistleClassifier/training/BatchTrainingParams.java +++ b/src/whistleClassifier/training/BatchTrainingParams.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; public class BatchTrainingParams implements Serializable, Cloneable, ManagedParameters { @@ -27,7 +28,7 @@ public class BatchTrainingParams implements Serializable, Cloneable, ManagedPara @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("fragmentLength"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/whistleClassifier/training/TrainingContour.java b/src/whistleClassifier/training/TrainingContour.java index bb834583..2bd5d890 100644 --- a/src/whistleClassifier/training/TrainingContour.java +++ b/src/whistleClassifier/training/TrainingContour.java @@ -6,6 +6,7 @@ import java.lang.reflect.Field; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; import PamModel.parametermanager.PrivatePamParameterData; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import whistleClassifier.WhistleContour; public class TrainingContour implements Serializable, WhistleContour, ManagedParameters { @@ -51,7 +52,7 @@ public class TrainingContour implements Serializable, WhistleContour, ManagedPar @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); try { Field field = this.getClass().getDeclaredField("timeSeconds"); ps.put(new PrivatePamParameterData(this, field) { diff --git a/src/whistleClassifier/training/TrainingDataSet.java b/src/whistleClassifier/training/TrainingDataSet.java index d3787ff0..bcd5150a 100644 --- a/src/whistleClassifier/training/TrainingDataSet.java +++ b/src/whistleClassifier/training/TrainingDataSet.java @@ -6,6 +6,7 @@ import java.util.ListIterator; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import whistleClassifier.WhistleContour; import whistleClassifier.WhistleFragmenter; @@ -200,7 +201,7 @@ public class TrainingDataSet implements Serializable, ManagedParameters { @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/whistlesAndMoans/WhistleBinaryModuleHeader.java b/src/whistlesAndMoans/WhistleBinaryModuleHeader.java index cd75c774..e76ad76e 100644 --- a/src/whistlesAndMoans/WhistleBinaryModuleHeader.java +++ b/src/whistlesAndMoans/WhistleBinaryModuleHeader.java @@ -4,11 +4,12 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import binaryFileStorage.BinaryHeader; import binaryFileStorage.BinaryObjectData; import binaryFileStorage.ModuleHeader; -public class WhistleBinaryModuleHeader extends ModuleHeader implements Serializable, ManagedParameters { +public class WhistleBinaryModuleHeader extends ModuleHeader implements Serializable { private static final long serialVersionUID = 1L; @@ -26,7 +27,7 @@ public class WhistleBinaryModuleHeader extends ModuleHeader implements Serializa @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } } diff --git a/src/whistlesAndMoans/WhistleToneParameters.java b/src/whistlesAndMoans/WhistleToneParameters.java index 6f16f786..a0f7503d 100644 --- a/src/whistlesAndMoans/WhistleToneParameters.java +++ b/src/whistlesAndMoans/WhistleToneParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import spectrogramNoiseReduction.SpectrogramNoiseSettings; import PamView.GroupedSourceParameters; @@ -150,7 +151,7 @@ public class WhistleToneParameters extends GroupedSourceParameters implements Se @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } diff --git a/src/whistlesAndMoans/alarm/WMAlarmParameters.java b/src/whistlesAndMoans/alarm/WMAlarmParameters.java index 733b156d..1978d507 100644 --- a/src/whistlesAndMoans/alarm/WMAlarmParameters.java +++ b/src/whistlesAndMoans/alarm/WMAlarmParameters.java @@ -4,6 +4,7 @@ import java.io.Serializable; import PamModel.parametermanager.ManagedParameters; import PamModel.parametermanager.PamParameterSet; +import PamModel.parametermanager.PamParameterSet.ParameterSetType; import PamguardMVC.dataSelector.DataSelectParams; public class WMAlarmParameters extends DataSelectParams implements Cloneable, Serializable, ManagedParameters { @@ -27,7 +28,7 @@ public class WMAlarmParameters extends DataSelectParams implements Cloneable, Se @Override public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this); + PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); return ps; } From c221d78aa276cd9d63f560b77c8c5925121bd197 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 11 Sep 2023 17:07:15 +0100 Subject: [PATCH 46/65] Added bin granularity binned granularity in place. TODO Encounter granularity. --- .../output/xml/PamguardXMLWriter.java | 5 +- src/PamguardMVC/PamDataBlock.java | 5 +- .../dataSelector/DataSelector.java | 2 +- src/tethys/TethysControl.java | 62 +++-- src/tethys/dbxml/DBXMLQueries.java | 21 ++ .../detection/BinnedGranularityHandler.java | 161 +++++++++++++ .../detection/CallGranularityHandler.java | 40 ++++ .../detection/DetectionGranularity.java | 34 --- src/tethys/detection/DetectionsHandler.java | 226 ++++++++++-------- .../EncounterGranularityHandler.java | 36 +++ src/tethys/detection/GranularityHandler.java | 100 ++++++++ .../detection/GroupedGranularityHandler.java | 36 +++ src/tethys/output/DatablockSynchInfo.java | 2 +- src/tethys/output/StreamExportParams.java | 6 + src/tethys/output/TethysExporter.java | 33 +-- .../output/swing/TethysExportDialog.java | 2 +- src/tethys/pamdata/AutoTethysProvider.java | 23 +- src/tethys/pamdata/TethysParameterPacker.java | 70 ++++-- .../swing/DatablockDetectionsPanel.java | 10 + src/tethys/swing/DatablockSynchPanel.java | 4 +- src/tethys/swing/export/AlgorithmCard.java | 2 +- src/tethys/swing/export/DescriptionCard.java | 2 +- .../swing/export/DetectionsExportWizard.java | 12 +- src/tethys/swing/export/ExportWorkerCard.java | 7 +- src/tethys/swing/export/GranularityCard.java | 79 +++++- 25 files changed, 751 insertions(+), 229 deletions(-) create mode 100644 src/tethys/detection/BinnedGranularityHandler.java create mode 100644 src/tethys/detection/CallGranularityHandler.java delete mode 100644 src/tethys/detection/DetectionGranularity.java create mode 100644 src/tethys/detection/EncounterGranularityHandler.java create mode 100644 src/tethys/detection/GranularityHandler.java create mode 100644 src/tethys/detection/GroupedGranularityHandler.java diff --git a/src/PamController/settings/output/xml/PamguardXMLWriter.java b/src/PamController/settings/output/xml/PamguardXMLWriter.java index 37530402..f74edce0 100644 --- a/src/PamController/settings/output/xml/PamguardXMLWriter.java +++ b/src/PamController/settings/output/xml/PamguardXMLWriter.java @@ -581,10 +581,13 @@ public class PamguardXMLWriter implements PamSettings { return el; } - private Element writeObjectData(Document doc, Element el, Object data, ArrayList objectHierarchy) { + public Element writeObjectData(Document doc, Element el, Object data, ArrayList objectHierarchy) { if (data == null) { return null; } + if (objectHierarchy == null) { + objectHierarchy = new ArrayList<>(); + } if (objectHierarchy.contains(data)) { // just write the reference, but nothing else or we'll end up in an infinite loop of objects. Element e = doc.createElement("Object"); diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index c3a3ba7b..feff646e 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -50,6 +50,7 @@ import org.w3c.dom.Element; import Acquisition.AcquisitionControl; import Acquisition.AcquisitionProcess; import pamScrollSystem.ViewLoadObserver; +import tethys.TethysControl; import tethys.pamdata.AutoTethysProvider; import tethys.pamdata.TethysDataProvider; import tethys.species.DataBlockSpeciesManager; @@ -3089,9 +3090,9 @@ public class PamDataBlock extends PamObservable { * to be bespoke, but for now will autogenerate based on the SQLLogging information. * @return the tethysDataProvider */ - public TethysDataProvider getTethysDataProvider() { + public TethysDataProvider getTethysDataProvider(TethysControl tethysControl) { if (tethysDataProvider == null && PamDetection.class.isAssignableFrom(unitClass) && getLogging() != null) { - tethysDataProvider = new AutoTethysProvider(this); + tethysDataProvider = new AutoTethysProvider(tethysControl, this); } return tethysDataProvider; } diff --git a/src/PamguardMVC/dataSelector/DataSelector.java b/src/PamguardMVC/dataSelector/DataSelector.java index a91712b5..b033976c 100644 --- a/src/PamguardMVC/dataSelector/DataSelector.java +++ b/src/PamguardMVC/dataSelector/DataSelector.java @@ -139,7 +139,7 @@ public abstract class DataSelector { } return null; } - + /** * Score a PAMDataUnit. this is used in preference * to a boolean select function so that the user can add different diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 92d44aef..c5f26031 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -211,7 +211,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet ArrayList sets = new ArrayList<>(); ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); for (PamDataBlock aDataBlock : allDataBlocks) { - if (aDataBlock.getTethysDataProvider() != null) { + if (aDataBlock.getTethysDataProvider(this) != null) { sets.add(aDataBlock); } } @@ -335,31 +335,31 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet return tethysExportParams; } - /** - * We'll probably want to - * @param parentFrame - */ - protected void tethysExport(JFrame parentFrame) { - TethysExportParams newExportParams = TethysExportDialog.showDialog(parentFrame, this); - if (newExportParams != null) { - // dialog returns null if cancel was pressed. - tethysExportParams = newExportParams; - exportTethysData(tethysExportParams); - } - } - - /** - * We'll arrive here if the dialog has been opened and we want to export Tethys data. - * @param tethysExportParams2 - */ - private void exportTethysData(TethysExportParams tethysExportParams) { - TethysExporter tethysExporter = new TethysExporter(this, tethysExportParams); - tethysExporter.doExport(); - - sendStateUpdate(new TethysState(StateType.TRANSFERDATA)); - countProjectDetections(); - sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION)); - } +// /** +// * We'll probably want to +// * @param parentFrame +// */ +// protected void tethysExport(JFrame parentFrame) { +// TethysExportParams newExportParams = TethysExportDialog.showDialog(parentFrame, this); +// if (newExportParams != null) { +// // dialog returns null if cancel was pressed. +// tethysExportParams = newExportParams; +// exportTethysData(tethysExportParams); +// } +// } +// +// /** +// * We'll arrive here if the dialog has been opened and we want to export Tethys data. +// * @param tethysExportParams2 +// */ +// 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 @@ -644,4 +644,14 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet return itisFunctions; } + /** + * Called when a detections document has been exported. + * @param dataBlock + */ + public void exportedDetections(PamDataBlock dataBlock) { + sendStateUpdate(new TethysState(StateType.TRANSFERDATA)); + countProjectDetections(); + sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION)); + } + } diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index a1d07e7f..1275ff86 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -541,6 +541,27 @@ public class DBXMLQueries { // String queryBase = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; } + /** + * Find out if a document exists ? + * @param collection + * @param documentId + * @return + */ + public boolean documentExists(String collection, String documentId) { + Queries queries = dbXMLConnect.getTethysQueries(); + String result = null; + try { + result = queries.getDocument(collection, documentId); + } catch (Exception e) { + return false; + } + if (result == null || result.length() == 0) { + return false; + } + + return result.contains(documentId); + } + /** * Count on effort detections in a Detections document * @param docName diff --git a/src/tethys/detection/BinnedGranularityHandler.java b/src/tethys/detection/BinnedGranularityHandler.java new file mode 100644 index 00000000..b39f4550 --- /dev/null +++ b/src/tethys/detection/BinnedGranularityHandler.java @@ -0,0 +1,161 @@ +package tethys.detection; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Set; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import nilus.Detection; +import nilus.SpeciesIDType; +import tethys.TethysControl; +import tethys.TethysTimeFuncs; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; +import tethys.pamdata.TethysDataProvider; +import tethys.species.DataBlockSpeciesCodes; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.DataBlockSpeciesMap; +import tethys.species.SpeciesMapItem; + +/** + * Binned granularity + * Will have to collect different counts for each type of call for each datablock (if there + * are such things) so a little more complicated than might be expected. + * @author dg50 + * + */ +public class BinnedGranularityHandler extends GranularityHandler { + + private double binDurationSeconds; + + private long binStartMillis, binEndMillis; + + private TethysDataProvider dataProvider; + + private DataBlockSpeciesManager speciesManager; + + private HashMap currentDetections; + + public BinnedGranularityHandler(TethysControl tethysControl, PamDataBlock dataBlock, + TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { + super(tethysControl, dataBlock, tethysExportParams, streamExportParams); + + binDurationSeconds = streamExportParams.binDurationS; + dataProvider = dataBlock.getTethysDataProvider(tethysControl); + speciesManager = dataBlock.getDatablockSpeciesManager(); + + currentDetections = new HashMap(); + } + + @Override + public void prepare(long timeMillis) { + long binStart = DetectionsHandler.roundDownBinStart(timeMillis, (long) (binDurationSeconds*1000)); + startBin(binStart); + } + + private void startBin(long timeMillis) { + binStartMillis = timeMillis; + binEndMillis = binStartMillis + (long) (binDurationSeconds*1000.); + /* + * now make a Detection object for every possible species that + * this might throw out. + */ + ArrayList speciesCodes = speciesManager.getAllSpeciesCodes(); + String defaultCode = speciesManager.getDefaultSpeciesCode(); + Detection det; + currentDetections.put(defaultCode, det = new Detection()); + det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); + det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); + det.setCount(BigInteger.ZERO); + det.setChannel(BigInteger.ZERO); + // add codes at end, just before output. + if (speciesCodes != null) { + for (String code : speciesCodes) { + currentDetections.put(code, det = new Detection()); + det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); + det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); + det.setCount(BigInteger.ZERO); + det.setChannel(BigInteger.ZERO); + } + } + } + + @Override + public Detection[] addDataUnit(PamDataUnit dataUnit) { + Detection[] detections = null; + if (dataUnit.getTimeMilliseconds() >= binEndMillis) { + detections = closeBins(dataUnit.getTimeMilliseconds()); + } + String speciesCode = speciesManager.getSpeciesCode(dataUnit); + Detection det = currentDetections.get(speciesCode); + if (det != null) { + /* + * Increase the detection count + */ + int count = det.getCount().intValue(); + count++; + det.setCount(BigInteger.valueOf(count)); + /* + * Add to the channel map too ... + */ + int channel = det.getChannel().intValue(); + channel |= dataUnit.getChannelBitmap(); + det.setChannel(BigInteger.valueOf(channel)); + } + return detections; + } + + /** + * Called when units arrive after end of current bin, and also + * at end of deployment output, to get that last bine. + * @param timeMilliseconds + * @return + */ + private Detection[] closeBins(long timeMilliseconds) { + Set speciesKeys = currentDetections.keySet(); + int n = speciesKeys.size(); + int nGood = 0; + DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); + Detection detections[] = new Detection[n]; + for (String key : speciesKeys) { + Detection det = currentDetections.get(key); + int callCount = det.getCount().intValue(); + if (callCount < Math.max(streamExportParams.minBinCount,1)) { + continue; + } + SpeciesMapItem speciesStuff = speciesMap.getItem(key); // should be non null! + if (speciesStuff == null) { + continue; + } + SpeciesIDType species = new SpeciesIDType(); + species.setValue(BigInteger.valueOf(speciesStuff.getItisCode())); + det.setSpeciesId(species); + if (speciesStuff.getCallType() != null) { + det.getCall().add(speciesStuff.getCallType()); + } + detections[nGood++] = det; + } + + + // finally, start new bins (not really needed on last call, but do anyway). + startBin(binEndMillis); + + /* + * Clean up the end of the array and return detections that have enough calls. + */ + if (nGood == 0) { + return null; + } + detections = Arrays.copyOf(detections, nGood); + return detections; + } + + @Override + public Detection[] cleanup(long timeMillis) { + return closeBins(timeMillis); + } + +} diff --git a/src/tethys/detection/CallGranularityHandler.java b/src/tethys/detection/CallGranularityHandler.java new file mode 100644 index 00000000..4ff9a888 --- /dev/null +++ b/src/tethys/detection/CallGranularityHandler.java @@ -0,0 +1,40 @@ +package tethys.detection; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import nilus.Detection; +import tethys.TethysControl; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; +import tethys.pamdata.TethysDataProvider; + +public class CallGranularityHandler extends GranularityHandler { + + private TethysDataProvider dataProvider; + + public CallGranularityHandler(TethysControl tethysControl, PamDataBlock dataBlock, + TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { + super(tethysControl, dataBlock, tethysExportParams, streamExportParams); + + dataProvider = dataBlock.getTethysDataProvider(tethysControl); + + } + + @Override + public void prepare(long timeMillis) { + // never anything to do here for call level granularity. + } + + @Override + public Detection[] addDataUnit(PamDataUnit dataUnit) { + Detection det = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); + return toDetectionArray(det); + } + + @Override + public Detection[] cleanup(long timeMillis) { + // never anything to do here for call level granularity. + return null; + } + +} diff --git a/src/tethys/detection/DetectionGranularity.java b/src/tethys/detection/DetectionGranularity.java deleted file mode 100644 index 7380fb67..00000000 --- a/src/tethys/detection/DetectionGranularity.java +++ /dev/null @@ -1,34 +0,0 @@ -package tethys.detection; - -/** - * Class to help define what the granularity of exported detections - * documents should be. The entire document will be in memory, so it - * may be necessary to add many detections documents into the database - * for a single Deployment. - * @author dg50 - * - */ -public class DetectionGranularity { - - public enum GRANULARITY {NONE, BINARYFILE, TIME}; - - /** - * Type of granularity. Are data all in one lump, split by binary file or by time. - */ - GRANULARITY granularity = GRANULARITY.NONE; - - /** - * Granularity interval in seconds. Output system will try to round these - * to something with sensible boundaries. - * This field is only needed when using the GRANULARITY.TIME option. - */ - public long granularityIntervalSeconds; - - public DetectionGranularity(GRANULARITY granularity, long granularityIntervalSeconds) { - super(); - this.granularity = granularity; - this.granularityIntervalSeconds = granularityIntervalSeconds; - } - - -} diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index bc5992db..bd1c8370 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -1,33 +1,20 @@ package tethys.detection; -import java.math.BigInteger; import java.util.ArrayList; import java.util.List; import javax.swing.SwingWorker; -import javax.xml.XMLConstants; -import javax.xml.bind.JAXBException; -import javax.xml.parsers.ParserConfigurationException; - -import org.pamguard.x3.sud.SUDClickDetectorInfo; -import org.w3c.dom.Document; -import org.w3c.dom.Element; import PamController.PamControlledUnit; import PamController.PamguardVersionInfo; -import PamController.settings.output.xml.PamguardXMLWriter; import PamModel.PamPluginInterface; -import PamUtils.PamCalendar; -import PamUtils.XMLUtils; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamProcess; import PamguardMVC.dataSelector.DataSelector; import dataMap.OfflineDataMap; import dataMap.OfflineDataMapPoint; -import metadata.deployment.DeploymentData; import nilus.AlgorithmType; -import nilus.AlgorithmType.Parameters; import nilus.AlgorithmType.SupportSoftware; import nilus.DataSourceType; import nilus.Deployment; @@ -38,43 +25,38 @@ import nilus.DetectionGroup; import nilus.Detections; import nilus.Helper; import tethys.TethysControl; -import tethys.TethysState; -import tethys.TethysState.StateType; -import tethys.deployment.DeploymentHandler; -import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.TethysException; -import tethys.detection.DetectionGranularity.GRANULARITY; +import tethys.deployment.DeploymentHandler; import tethys.niluswraps.PDeployment; import tethys.niluswraps.PDetections; +import tethys.niluswraps.TethysCollections; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; -import tethys.pamdata.TethysDataPoint; import tethys.pamdata.TethysDataProvider; -import tethys.swing.export.ExportWorkerCard; public class DetectionsHandler { private TethysControl tethysControl; - + public int uniqueDetectionsId=1; public int uniqueDetectionId; private volatile boolean activeExport; private ExportWorker exportWorker; - + public DetectionsHandler(TethysControl tethysControl) { super(); this.tethysControl = tethysControl; } - - + + /** - * Get a list of Detections documents associated with a particular data stream for - * this data set (not the entire project). + * Get a list of Detections documents associated with a particular data stream for + * this data set (not the entire project). * @param dataBlock */ public StreamDetectionsSummary getStreamDetections(PamDataBlock dataBlock) { @@ -84,21 +66,21 @@ public class DetectionsHandler { /** * Get a list of Detections documents associated with a particular data block for the list of deployments - * documents. Group them by abstract or something + * documents. Group them by abstract or something * @param dataBlock * @param deployments * @return */ public StreamDetectionsSummary getStreamDetections(PamDataBlock dataBlock, ArrayList deployments) { - // get the basic data for each document including it's Description. - + // get the basic data for each document including it's Description. + ArrayList detectionsDocs = new ArrayList<>(); for (PDeployment aDep : deployments) { ArrayList someNames = tethysControl.getDbxmlQueries().getDetectionsDocuments(dataBlock, aDep.deployment.getId()); if (someNames == null) { continue; } - // no have a list of all the Detections documents of interest for this datablock. + // no have a list of all the Detections documents of interest for this datablock. for (String aDoc : someNames) { Detections detections = tethysControl.getDbxmlQueries().getDetectionsDocInfo(aDoc); int count = tethysControl.getDbxmlQueries().countDetections2(aDoc); @@ -114,7 +96,7 @@ public class DetectionsHandler { // * Here is where we export data for a specific data stream to Tethys. // * // * @param aDataBlock -// * @param aDeployment +// * @param aDeployment // * @param tethysExportParams // * @param streamExportParams // */ @@ -135,7 +117,7 @@ public class DetectionsHandler { // } // // return false; -// +// // // } // @@ -145,7 +127,7 @@ public class DetectionsHandler { // long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); // /* // * there should be a pretty good correspondence between the start of a binary file and the deploymentStart -// * since they all derived from the same start clock. +// * since they all derived from the same start clock. // */ // OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); // if (dataMap == null) { @@ -160,11 +142,11 @@ public class DetectionsHandler { // if (mapPoint.getStartTime() >= deploymentStop) { // continue; // } -// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, mapPoint.getStartTime()), +// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, mapPoint.getStartTime()), // Math.min(deploymentStop, mapPoint.getEndTime()), tethysExportParams, streamExportParams); // } -// -// +// +// // return ok; // } // @@ -185,11 +167,11 @@ public class DetectionsHandler { // exportStart *= chunkMillis; // boolean ok = true; // while (exportStart < deploymentStop) { -// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, exportStart), +// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, exportStart), // Math.min(deploymentStop, exportStart + chunkMillis), tethysExportParams, streamExportParams); // exportStart += chunkMillis; // } -// +// // return ok; // } // @@ -210,11 +192,11 @@ public class DetectionsHandler { // DataSelector dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); // /* // * for easier synching, get a copy of the data and also apply the data selector right away so that -// * we've a list of exactly the right data. +// * we've a list of exactly the right data. // */ // ArrayList data = dataBlock.getDataCopy(startTimeMillis, endTimeMillis, true, dataSelector); // /* -// * Here, make Detection object and add the DetectionEffort data. +// * Here, make Detection object and add the DetectionEffort data. // */ // DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); // TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); @@ -235,25 +217,25 @@ public class DetectionsHandler { // List detectionList = detectionGroup.getDetection(); // for (int i = 0; i < data.size(); i++) { // PamDataUnit dataUnit = data.get(i); -// Detection detection = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); +// Detection detection = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); // if (detection != null) { // detectionList.add(detection); // } // } -// System.out.printf("Exporting %d %s detections for time period %s to %s\n", detectionList.size(), dataBlock.getDataName(), +// System.out.printf("Exporting %d %s detections for time period %s to %s\n", detectionList.size(), dataBlock.getDataName(), // detections.getEffort().getStart().toString(), detections.getEffort().getEnd().toString()); // /* // * We should now have a fully populated Detections object, so write it to the database -// * using functions in DBXMLConnect +// * using functions in DBXMLConnect // */ // ArrayList detectionDocuments = new ArrayList(); // detectionDocuments.add(detections); -// -//// tethysControl.getDbxmlConnect().postToTethys(detectionDocuments); // call whatever you need to call in here to write the Detections. -// -// +// +//// tethysControl.getDbxmlConnect().postToTethys(detectionDocuments); // call whatever you need to call in here to write the Detections. +// +// // return true; -// +// // } // private boolean exportByTimeChunk(PamDataBlock aDataBlock, Deployment deployment, long granularityIntervalSeconds, @@ -272,15 +254,15 @@ public class DetectionsHandler { // effort.set // no setter for DetectionEffortKind List effortKinds = effort.getKind(); - TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); + TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl); dataProvider.getEffortKinds(pDeployment, effortKinds, exportParams); - - + + return effort; } /** - * Method string for Detections Algorithm documents. + * Method string for Detections Algorithm documents. * @param dataBlock * @return */ @@ -290,11 +272,11 @@ public class DetectionsHandler { } PamProcess process = dataBlock.getParentProcess(); return "PAMGuard " + process.getProcessName(); - + } /** - * Software string for Detections Algorithm documents. + * Software string for Detections Algorithm documents. * @param dataBlock * @return */ @@ -306,7 +288,7 @@ public class DetectionsHandler { } /** - * Software string for Detections Algorithm documents. + * Software string for Detections Algorithm documents. * @param dataBlock * @return */ @@ -324,31 +306,31 @@ public class DetectionsHandler { return plugin.getVersion(); } } - + public String getSupportSoftware(PamDataBlock dataBlock) { return "PAMGuard"; } - + public String getSupportSoftwareVersion(PamDataBlock dataBlock) { -// should try to dig into the binary store and get the version from there. +// should try to dig into the binary store and get the version from there. return PamguardVersionInfo.version; } // /** -// * Get a prefix for a id for a Detections document. This is just the project name +// * 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. +// * analysis going into one database. // * @param project // * @param dataBlock -// * @return Detections document prefix. +// * @return Detections document prefix. // */ // public static final String getDetectionsDocIdPrefix(String project, PamDataBlock dataBlock) { // return project + "_" + dataBlock.getDataName(); // } /** - * Detections will be exported in a separate worker thread since export may take some time and - * the user should be given ample opportunity to cancel it. - * @param pamDataBlock + * Detections will be exported in a separate worker thread since export may take some time and + * the user should be given ample opportunity to cancel it. + * @param pamDataBlock * @param streamExportParams * @param exportWorkerCard */ @@ -358,13 +340,25 @@ public class DetectionsHandler { exportWorker = new ExportWorker(pamDataBlock, streamExportParams, exportObserver); exportWorker.execute(); } - + public void cancelExport() { activeExport = false; } /** - * Export detections in all deployments for this PAMGuard dataset. + * Round a bin start so that it's aligned correctly with + * day starts. + * @param binStart + * @param binInterval + * @return + */ + public static long roundDownBinStart(long binStart, long binInterval) { + binStart/=binInterval; + return binStart*binInterval; + } + + /** + * Export detections in all deployments for this PAMGuard dataset. * @param dataBlock * @param streamExportParams * @param exportObserver @@ -372,8 +366,8 @@ public class DetectionsHandler { */ private int exportDetections(PamDataBlock dataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { /* - * This is currently called for the entire dataset, but we will need to loop over specific Deployment documents - * and export the content of each separately. + * This is currently called for the entire dataset, but we will need to loop over specific Deployment documents + * and export the content of each separately. */ TethysExportParams exportParams = tethysControl.getTethysExportParams(); DBXMLConnect dbxmlConnect = tethysControl.getDbxmlConnect(); @@ -381,28 +375,29 @@ public class DetectionsHandler { ArrayList deployments = depHandler.getMatchedDeployments(); Detections currentDetections = null; OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); - TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); DataSelector dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); int totalCount = dataMap.getDataCount(); int skipCount = 0; int exportCount = 0; long lastUnitTime = 0; DetectionExportProgress prog; + GranularityHandler granularityHandler = GranularityHandler.getHandler(streamExportParams.granularity, tethysControl, dataBlock, exportParams, streamExportParams); for (PDeployment deployment : deployments) { int documentCount = 0; - prog = new DetectionExportProgress(deployment, null, + prog = new DetectionExportProgress(deployment, null, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); exportObserver.update(prog); - // export everything in that deployment. - // need to loop through all map points in this interval. + granularityHandler.prepare(deployment.getAudioStart()); + // export everything in that deployment. + // need to loop through all map points in this interval. List mapPoints = dataMap.getMapPoints(); for (OfflineDataMapPoint mapPoint : mapPoints) { - if (activeExport == false) { - prog = new DetectionExportProgress(deployment, currentDetections, + if (!activeExport) { + prog = new DetectionExportProgress(deployment, currentDetections, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_CANCELED); exportObserver.update(prog); } - + if (currentDetections == null) { currentDetections = startDetectionsDocument(deployment, dataBlock, streamExportParams); currentDetections.getEffort().setStart(TethysTimeFuncs.xmlGregCalFromMillis(mapPoint.getStartTime())); @@ -418,19 +413,30 @@ public class DetectionsHandler { skipCount += dataBlock.getUnitsCount() - dataCopy.size(); DetectionGroup onEffort = currentDetections.getOnEffort(); for (PamDataUnit dataUnit : dataCopy) { - Detection det = dataProvider.createDetection(dataUnit, exportParams, streamExportParams); - exportCount++; - documentCount++; - onEffort.getDetection().add(det); + /* + * Here is where we need to handle the different granularities. + */ + Detection dets[] = granularityHandler.addDataUnit(dataUnit); + if (dets != null) { + for (int dd = 0; dd < dets.length; dd++) { + exportCount++; + documentCount++; + onEffort.getDetection().add(dets[dd]); + } + } +// Detection det = dataProvider.createDetection(dataUnit, exportParams, streamExportParams); +// exportCount++; +// documentCount++; +// onEffort.getDetection().add(det); lastUnitTime = dataUnit.getTimeMilliseconds(); } - - prog = new DetectionExportProgress(deployment, currentDetections, + + prog = new DetectionExportProgress(deployment, currentDetections, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); exportObserver.update(prog); - + if (documentCount > 500000 && mapPoint != dataMap.getLastMapPoint()) { - prog = new DetectionExportProgress(deployment, currentDetections, + prog = new DetectionExportProgress(deployment, currentDetections, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); exportObserver.update(prog); closeDetectionsDocument(currentDetections, mapPoint.getEndTime()); @@ -443,8 +449,17 @@ public class DetectionsHandler { } } + if (currentDetections != null) { - prog = new DetectionExportProgress(deployment, currentDetections, + Detection dets[] = granularityHandler.cleanup(deployment.getAudioEnd()); + if (dets != null) { + for (int dd = 0; dd < dets.length; dd++) { + exportCount++; + documentCount++; + currentDetections.getOnEffort().getDetection().add(dets[dd]); + } + } + prog = new DetectionExportProgress(deployment, currentDetections, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); closeDetectionsDocument(currentDetections, deployment.getAudioEnd()); try { @@ -456,7 +471,7 @@ public class DetectionsHandler { } } - prog = new DetectionExportProgress(null, null, + prog = new DetectionExportProgress(null, null, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COMPLETE); exportObserver.update(prog); return DetectionExportProgress.STATE_COMPLETE; @@ -472,7 +487,17 @@ public class DetectionsHandler { } String prefix = deployment.deployment.getId(); - detections.setId(String.format("%s_%d", prefix, uniqueDetectionsId++)); + String fullId = ""; + /* + * Check the document name isn't already used and increment id as necessary. + */ + while (true) { + fullId = String.format("%s_%d", prefix, uniqueDetectionsId++); + if (!tethysControl.getDbxmlQueries().documentExists(TethysCollections.Detections.toString(), fullId)) { + break; + } + } + detections.setId(fullId); // detections.setDescription(dataProvider.getDescription(deployment, tethysExportParams)); detections.setDescription(exportParams.getNilusDetectionDescription()); DataSourceType dataSource = new DataSourceType(); @@ -480,8 +505,8 @@ public class DetectionsHandler { // dataSource.setEnsembleId(""); ToDo detections.setDataSource(dataSource); AlgorithmType algorithm = detections.getAlgorithm(); - - TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); + + TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl); if (dataProvider != null) { algorithm = dataProvider.getAlgorithm(); // detections.setAlgorithm(algorithm); @@ -489,7 +514,7 @@ public class DetectionsHandler { algorithm.setMethod(getMethodString(dataBlock)); algorithm.setSoftware(getSoftwareString(dataBlock)); algorithm.setVersion(getVersionString(dataBlock)); - + List supSoft = algorithm.getSupportSoftware(); SupportSoftware supportSoft = new SupportSoftware(); supportSoft.setSoftware(getSupportSoftware(dataBlock)); @@ -498,13 +523,13 @@ public class DetectionsHandler { detections.setAlgorithm(algorithm); detections.setUserId("Unknown user"); detections.setEffort(getDetectorEffort(deployment, dataBlock, exportParams)); - + return detections; } /** * Close a detections document. This basically just means rewriting the end time and it's only - * important in the event that a document got too big and has to be restarted. + * important in the event that a document got too big and has to be restarted. * @param detections * @param audioEnd */ @@ -517,7 +542,7 @@ public class DetectionsHandler { private PamDataBlock dataBlock; private StreamExportParams exportParams; private DetectionExportObserver exportObserver; - + public ExportWorker(PamDataBlock dataBlock, StreamExportParams exportParams, DetectionExportObserver exportObserver) { super(); @@ -525,21 +550,28 @@ public class DetectionsHandler { this.exportParams = exportParams; this.exportObserver = exportObserver; } - + public void publish(DetectionExportProgress exportProgress) { super.publish(exportProgress); } - + @Override protected Integer doInBackground() throws Exception { - // eventually need to switch over the four granularity options here. - return exportDetections(dataBlock, exportParams, this); + Integer ans = null; + try { + ans = exportDetections(dataBlock, exportParams, this); + } + catch (Exception e) { + e.printStackTrace(); + } + return ans; } @Override protected void done() { // this. DetectionExportProgress prog = new DetectionExportProgress(null, null, 0, 0, 0, 0, DetectionExportProgress.STATE_COMPLETE); + tethysControl.exportedDetections(dataBlock); exportObserver.update(prog); } @@ -554,6 +586,6 @@ public class DetectionsHandler { public void update(DetectionExportProgress progress) { publish(progress); } - + } } diff --git a/src/tethys/detection/EncounterGranularityHandler.java b/src/tethys/detection/EncounterGranularityHandler.java new file mode 100644 index 00000000..061e018e --- /dev/null +++ b/src/tethys/detection/EncounterGranularityHandler.java @@ -0,0 +1,36 @@ +package tethys.detection; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import nilus.Detection; +import tethys.TethysControl; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; + +public class EncounterGranularityHandler extends GranularityHandler { + + public EncounterGranularityHandler(TethysControl tethysControl, PamDataBlock dataBlock, + TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { + super(tethysControl, dataBlock, tethysExportParams, streamExportParams); + // TODO Auto-generated constructor stub + } + + @Override + public void prepare(long timeMillis) { + // TODO Auto-generated method stub + + } + + @Override + public Detection[] addDataUnit(PamDataUnit dataUnit) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Detection[] cleanup(long timeMillis) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/tethys/detection/GranularityHandler.java b/src/tethys/detection/GranularityHandler.java new file mode 100644 index 00000000..e4e11da2 --- /dev/null +++ b/src/tethys/detection/GranularityHandler.java @@ -0,0 +1,100 @@ +package tethys.detection; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import nilus.Detection; +import nilus.GranularityEnumType; +import tethys.TethysControl; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; + +public abstract class GranularityHandler { + + protected TethysControl tethysControl; + + protected PamDataBlock dataBlock; + + protected TethysExportParams tethysExportParams; + + protected StreamExportParams streamExportParams; + + /** + * @param tethysControl + * @param dataBlock + * @param tethysExportParams + * @param streamExportParams + */ + public GranularityHandler(TethysControl tethysControl, PamDataBlock dataBlock, + TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { + this.tethysControl = tethysControl; + this.dataBlock = dataBlock; + this.tethysExportParams = tethysExportParams; + this.streamExportParams = streamExportParams; + } + + /** + * Prepare to start, passing the start time of the effort + * or of the first time bin for binned granularity types. + * @param timeMillis + */ + public abstract void prepare(long timeMillis); + + /** + * Put a data unit into a Detection object. for Call granularity + * this will probably return every time. For binned and encounter + * types this will only return at the end of a bin / encounter + * @param dataUnit + * @return Detection object, but only when ready to be added to Detections + */ + public abstract Detection[] addDataUnit(PamDataUnit dataUnit); + + /** + * Called after end end of all data units to get the last bin / encounter.

+ * + * @param timeMillis end time of effort or last bin in milliseconds. + * @return null for Call granularity, otherwise may be non null for binned or encounter. + */ + public abstract Detection[] cleanup(long timeMillis); + + /** + * Convert a single detection to a one element array since that's what' + * most functions need to return. + * @param det + * @return + */ + protected Detection[] toDetectionArray(Detection det) { + if (det == null) { + return null; + } + Detection[] dets = new Detection[1]; + dets[0] = det; + return dets; + } + + /** + * Create the correct type of granularity handler to put individual data units into + * Detection objects. + * @param granularity + * @param tethysControl + * @param dataBlock + * @param tethysExportParams + * @param streamExportParams + * @return + */ + public static GranularityHandler getHandler(GranularityEnumType granularity, TethysControl tethysControl, PamDataBlock dataBlock, + TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { + switch (granularity) { + case BINNED: + return new BinnedGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); + case CALL: + return new CallGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); + case ENCOUNTER: + new EncounterGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); + case GROUPED: + return new GroupedGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); + default: + break; + } + return null; + } +} diff --git a/src/tethys/detection/GroupedGranularityHandler.java b/src/tethys/detection/GroupedGranularityHandler.java new file mode 100644 index 00000000..298913a3 --- /dev/null +++ b/src/tethys/detection/GroupedGranularityHandler.java @@ -0,0 +1,36 @@ +package tethys.detection; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import nilus.Detection; +import tethys.TethysControl; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; + +public class GroupedGranularityHandler extends GranularityHandler { + + public GroupedGranularityHandler(TethysControl tethysControl, PamDataBlock dataBlock, + TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { + super(tethysControl, dataBlock, tethysExportParams, streamExportParams); + // TODO Auto-generated constructor stub + } + + @Override + public void prepare(long timeMillis) { + // TODO Auto-generated method stub + + } + + @Override + public Detection[] addDataUnit(PamDataUnit dataUnit) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Detection[] cleanup(long timeMillis) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/src/tethys/output/DatablockSynchInfo.java b/src/tethys/output/DatablockSynchInfo.java index 7664637b..d58be25c 100644 --- a/src/tethys/output/DatablockSynchInfo.java +++ b/src/tethys/output/DatablockSynchInfo.java @@ -8,7 +8,7 @@ import tethys.TethysControl; * 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 + * This needs to sit alongside the StreamExportParams objects since those others are serialisable whereas * there is a lot of stuff in here which isn't. * @author dg50 * diff --git a/src/tethys/output/StreamExportParams.java b/src/tethys/output/StreamExportParams.java index 2db881fa..27e2c2a6 100644 --- a/src/tethys/output/StreamExportParams.java +++ b/src/tethys/output/StreamExportParams.java @@ -31,6 +31,12 @@ public class StreamExportParams implements Serializable { public GranularityEnumType granularity = GranularityEnumType.CALL; + public double binDurationS = 60; + + public double encounterGapS = 60; + + public int minBinCount = 1; + /* * Can't have this here since it isn't serializable. */ diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 96e8c38c..a3cc0def 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -1,32 +1,16 @@ package tethys.output; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.ArrayList; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; - -import org.w3c.dom.Document; - import Acquisition.AcquisitionControl; import Acquisition.AcquisitionProcess; import Array.ArrayManager; import Array.Hydrophone; import Array.PamArray; import Array.SnapshotGeometry; -import PamController.PamControlledUnit; import PamController.PamController; -import PamController.PamSettings; -import PamController.settings.output.xml.PamguardXMLWriter; import PamUtils.PamCalendar; import PamguardMVC.PamDataBlock; -import PamguardMVC.PamDataUnit; -import PamguardMVC.dataSelector.DataSelector; -import dbxml.uploader.Importer; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; import nilus.Deployment; @@ -35,15 +19,13 @@ import tethys.dbxml.DBXMLConnect; import tethys.dbxml.TethysException; 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; -import tethys.pamdata.TethysDataProvider; -import tethys.pamdata.TethysSchema; /** + * No longer used. This was purely a test class used for making the first couple + * of test connections and exports to Tethys. Can probably delete. + * * Class sitting at the centre of all operations. It will talk to PAMGuard * objects to get schemas and data and talk to the database connection to move * data out (and possibly in). Eventually, a lot of the functionality in here @@ -55,6 +37,7 @@ import tethys.pamdata.TethysSchema; * @author dg50 * */ +@Deprecated public class TethysExporter { private TethysControl tethysControl; @@ -62,7 +45,7 @@ public class TethysExporter { private DBXMLConnect dbxmlConnect; - public TethysExporter(TethysControl tethysControl, TethysExportParams tethysExportParams) { + private TethysExporter(TethysControl tethysControl, TethysExportParams tethysExportParams) { this.tethysControl = tethysControl; this.tethysExportParams = tethysExportParams; dbxmlConnect = new DBXMLConnect(tethysControl); @@ -85,7 +68,7 @@ public class TethysExporter { */ // return false; // } - + /** * Doug populate instrument fields - may need to add a few things. Marie to * define what we mean by instrument. Instrument names probably need to be added @@ -206,7 +189,7 @@ 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 +// * Outer loop is through deployemnt documents. Will then export detections within each // * deployment detector by detector // */ // for (Deployment aDeployment : deploymentDocs) { @@ -215,7 +198,7 @@ public class TethysExporter { // if (streamExportParams == null || !streamExportParams.selected) { // continue; // not interested in this one. // } -// detectionsHandler.exportDetections(aDataBlock, aDeployment, +// detectionsHandler.exportDetections(aDataBlock, aDeployment, // new DetectionGranularity(GRANULARITY.TIME, 3600*12), tethysExportParams, streamExportParams); // } // } diff --git a/src/tethys/output/swing/TethysExportDialog.java b/src/tethys/output/swing/TethysExportDialog.java index ae86b315..46a6c690 100644 --- a/src/tethys/output/swing/TethysExportDialog.java +++ b/src/tethys/output/swing/TethysExportDialog.java @@ -112,7 +112,7 @@ public class TethysExportDialog extends PamDialog { ArrayList sets = new ArrayList<>(); ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); for (PamDataBlock aDataBlock : allDataBlocks) { - if (aDataBlock.getTethysDataProvider() != null) { + if (aDataBlock.getTethysDataProvider(tethysControl) != null) { sets.add(new DataStreamSet(aDataBlock)); } } diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 57b254b0..23a465a0 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -31,6 +31,7 @@ import nilus.DetectionEffortKind; import nilus.SpeciesIDType; import tethys.TethysControl; import tethys.TethysTimeFuncs; +import tethys.detection.DetectionsHandler; import tethys.niluswraps.PDeployment; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; @@ -62,8 +63,10 @@ public class AutoTethysProvider implements TethysDataProvider { private PamDataBlock pamDataBlock; private PamProcess pamProcess; private PamControlledUnit pamControlledUnit; + private TethysControl tethysControl; - public AutoTethysProvider(PamDataBlock pamDataBlock) { + public AutoTethysProvider(TethysControl tethysControl, PamDataBlock pamDataBlock) { + this.tethysControl = tethysControl; this.pamDataBlock = pamDataBlock; pamProcess = pamDataBlock.getParentProcess(); pamControlledUnit = pamProcess.getPamControlledUnit(); @@ -135,7 +138,7 @@ public class AutoTethysProvider implements TethysDataProvider { Object settings = pamSettings.getSettingsReference(); TethysParameterPacker paramPacker = null; try { - paramPacker = new TethysParameterPacker(); + paramPacker = new TethysParameterPacker(tethysControl); } catch (JAXBException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -425,6 +428,22 @@ public class AutoTethysProvider implements TethysDataProvider { kind.getSpeciesId().setValue(BigInteger.valueOf(mapItem.getItisCode())); kind.getGranularity().setValue(exportParams.granularity); +// nilus.DetectionEffortKind.Parameters granularityParams = kind.getParameters(); + switch (exportParams.granularity) { + case BINNED: + kind.getGranularity().setBinSizeM(exportParams.binDurationS/60.); + long firstBin = DetectionsHandler.roundDownBinStart(pDeployment.getAudioStart(), (long) (exportParams.binDurationS*1000)); + kind.getGranularity().setFirstBinStart(TethysTimeFuncs.xmlGregCalFromMillis(firstBin)); + break; + case CALL: + break; + case ENCOUNTER: + kind.getGranularity().setEncounterGapM(exportParams.encounterGapS/60.); + break; + case GROUPED: + break; + + } kind.setCall(mapItem.getCallType()); diff --git a/src/tethys/pamdata/TethysParameterPacker.java b/src/tethys/pamdata/TethysParameterPacker.java index 55535594..b663db20 100644 --- a/src/tethys/pamdata/TethysParameterPacker.java +++ b/src/tethys/pamdata/TethysParameterPacker.java @@ -14,6 +14,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMResult; +import org.docx4j.model.listnumbering.NumberFormatLowerLetter; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -25,7 +26,10 @@ import PamModel.parametermanager.PamParameterData; import PamModel.parametermanager.PamParameterSet; import PamguardMVC.PamDataBlock; import PamguardMVC.PamProcess; +import PamguardMVC.dataSelector.DataSelectParams; +import PamguardMVC.dataSelector.DataSelector; import nilus.MarshalXML; +import tethys.TethysControl; /** * Functions to pack up a PAMGuard parameters object into the correct format @@ -74,12 +78,15 @@ public class TethysParameterPacker { private PamguardXMLWriter xmlWriter; + private TethysControl tethysControl; + /** * @throws JAXBException * */ - public TethysParameterPacker() throws JAXBException { + public TethysParameterPacker(TethysControl tethysControl) throws JAXBException { super(); + this.tethysControl = tethysControl; try { marshaller = new MarshalXML(); } catch (JAXBException e) { @@ -117,12 +124,43 @@ public class TethysParameterPacker { if (parameterSet == null) { return null; } -// Document document = null; -// try { -// document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); -// } catch (ParserConfigurationException e1) { -// e1.printStackTrace(); -// } + // get the XML writer ready for a new export ... + xmlWriter.setExcludeDisplaySettings(true); + xmlWriter.makeSettingsList(); + + /** + * first do the data filter. I can't see any way of doing this + * without creating a doc as was in the helper example. + */ + QName qnamef = new QName(MarshalXML.schema, "datafilter", "ty"); + JAXBElement jaxelf = new JAXBElement( + qnamef, String.class, parameterSet.getParentObject().getClass().getCanonicalName()); + Document docf = null; + try { + docf = marshaller.marshalToDOM(jaxelf); + } catch (JAXBException | ParserConfigurationException e1) { + e1.printStackTrace(); + } + Element elf = docf.getDocumentElement(); + elList.add(elf);/** + * Is there a data filter ? If so, write it's + * XML parameters out here. + */ + DataSelector dataSelector = pamDataBlock.getDataSelector(tethysControl.getDataSelectName(), false); + if (dataSelector != null) { + DataSelectParams filterParams = dataSelector.getParams(); + if (filterParams != null) { + Element pEl = xmlWriter.writeObjectData(docf, elf, filterParams, null); +// if (pEl != null) { +//// filterEl.appendChild(pEl); +// elf.appendChild(filterEl); +// } + } + } + + + + QName qname = new QName(MarshalXML.schema, "parameters", "ty"); JAXBElement jaxel = new JAXBElement( qname, String.class, parameterSet.getParentObject().getClass().getCanonicalName()); @@ -133,18 +171,14 @@ public class TethysParameterPacker { e1.printStackTrace(); } Element el = doc.getDocumentElement(); - - for (PamParameterData pamParam : parameterSet.getParameterCollection()) { - try { - Object paramData = pamParam.getData(); - boolean ok = createElement(doc, el, paramData, pamParam, objectHierarchy); - } catch (IllegalArgumentException | IllegalAccessException e) { - e.printStackTrace(); - } - } elList.add(el); - xmlWriter.setExcludeDisplaySettings(true); - xmlWriter.makeSettingsList(); + + + + /** + * Now get the chain of PAMGuard modules for the current detector and for + * all upstream modules. + */ ArrayList moduleChain = getParentChain(pamDataBlock); for (PamControlledUnit pcu : moduleChain) { if (pcu instanceof PamSettings == false) { diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index be174a5e..2e2ebb40 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -20,6 +20,8 @@ import PamView.tables.SwingTableColumnWidths; import PamguardMVC.PamDataBlock; import nilus.Detections; import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysState.StateType; import tethys.dbxml.TethysException; import tethys.detection.StreamDetectionsSummary; import tethys.niluswraps.PDetections; @@ -75,6 +77,13 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa tableModel.fireTableDataChanged(); } + @Override + public void updateState(TethysState tethysState) { + if (dataBlock != null) { + selectDataBlock(dataBlock); + } + } + private class MouseActions extends MouseAdapter { @Override @@ -137,6 +146,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } catch (TethysException e) { getTethysControl().showException(e); } + getTethysControl().exportedDetections(dataBlock); selectDataBlock(dataBlock); // force table update. } diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java index 212eadf2..1c1131b5 100644 --- a/src/tethys/swing/DatablockSynchPanel.java +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -28,10 +28,11 @@ import PamguardMVC.PamDataBlock; import dataMap.OfflineDataMap; import tethys.TethysControl; import tethys.TethysState; +import tethys.TethysStateObserver; import tethys.output.DatablockSynchInfo; import tethys.species.DataBlockSpeciesManager; -public class DatablockSynchPanel extends TethysGUIPanel { +public class DatablockSynchPanel extends TethysGUIPanel { public JPanel mainPanel; @@ -133,6 +134,7 @@ public class DatablockSynchPanel extends TethysGUIPanel { @Override public void updateState(TethysState tethysState) { synchTableModel.fireTableDataChanged(); + selectRow(); } public void addTableObserver(StreamTableObserver observer) { diff --git a/src/tethys/swing/export/AlgorithmCard.java b/src/tethys/swing/export/AlgorithmCard.java index fd82e483..ff949d2b 100644 --- a/src/tethys/swing/export/AlgorithmCard.java +++ b/src/tethys/swing/export/AlgorithmCard.java @@ -20,7 +20,7 @@ public class AlgorithmCard extends ExportWizardCard { private JTextField method, software, version, supportSoftware; - public AlgorithmCard(TethysControl tethysControl, PamDataBlock dataBlock) { + public AlgorithmCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { super(tethysControl, "Algorithm", dataBlock); setBorder(new TitledBorder("Algorithm details")); method = new JTextField(40); diff --git a/src/tethys/swing/export/DescriptionCard.java b/src/tethys/swing/export/DescriptionCard.java index 13b13ee3..c2bacaef 100644 --- a/src/tethys/swing/export/DescriptionCard.java +++ b/src/tethys/swing/export/DescriptionCard.java @@ -10,7 +10,7 @@ public class DescriptionCard extends ExportWizardCard { private DescriptionTypePanel descriptionPanel; - public DescriptionCard(TethysControl tethysControl, PamDataBlock dataBlock) { + public DescriptionCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { super(tethysControl, "Description", dataBlock); this.setLayout(new BorderLayout()); descriptionPanel = new DescriptionTypePanel("Description data", true, true, true); diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index d04a8550..94ece212 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -48,10 +48,10 @@ public class DetectionsExportWizard extends PamDialog { cardPanel = new JPanel(cardLayout); mainPanel.add(BorderLayout.CENTER, cardPanel); - addCard(algorithmCard = new AlgorithmCard(tethysControl, dataBlock)); - addCard(granularityCard = new GranularityCard(tethysControl, dataBlock)); - addCard(descriptionCard = new DescriptionCard(tethysControl, dataBlock)); - addCard(exportWorkerCard = new ExportWorkerCard(tethysControl, dataBlock)); + addCard(algorithmCard = new AlgorithmCard(this, tethysControl, dataBlock)); + addCard(granularityCard = new GranularityCard(this, tethysControl, dataBlock)); + addCard(descriptionCard = new DescriptionCard(this, tethysControl, dataBlock)); + addCard(exportWorkerCard = new ExportWorkerCard(this, tethysControl, dataBlock)); cardLayout.first(cardPanel); @@ -98,6 +98,10 @@ public class DetectionsExportWizard extends PamDialog { cardLayout.previous(cardPanel); enableControls(); } + + public JButton getPreviousButton() { + return prevButton; + } @Override public boolean getParams() { diff --git a/src/tethys/swing/export/ExportWorkerCard.java b/src/tethys/swing/export/ExportWorkerCard.java index 3b4f73ee..bfe0c983 100644 --- a/src/tethys/swing/export/ExportWorkerCard.java +++ b/src/tethys/swing/export/ExportWorkerCard.java @@ -36,8 +36,11 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor private StreamExportParams streamExportParams; - public ExportWorkerCard(TethysControl tethysControl, PamDataBlock dataBlock) { + private DetectionsExportWizard detectionsExportWizard; + + public ExportWorkerCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { super(tethysControl, "Export", dataBlock); + this.detectionsExportWizard = detectionsExportWizard; setLayout(new BorderLayout()); setBorder(new TitledBorder("Export data")); JPanel exPanel = new PamNorthPanel(new GridBagLayout()); @@ -146,6 +149,8 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor break; case DetectionExportProgress.STATE_COMPLETE: progressText.setText("Export complete"); + detectionsExportWizard.getCancelButton().setText("Close"); + detectionsExportWizard.getPreviousButton().setEnabled(false); break; case DetectionExportProgress.STATE_WRITING: progressText.setText("Writing to Tethys: " + progress.currentDetections.getId()); diff --git a/src/tethys/swing/export/GranularityCard.java b/src/tethys/swing/export/GranularityCard.java index c7f3c4b8..a8fa539e 100644 --- a/src/tethys/swing/export/GranularityCard.java +++ b/src/tethys/swing/export/GranularityCard.java @@ -34,17 +34,22 @@ import tethys.output.StreamExportParams; public class GranularityCard extends ExportWizardCard { private JRadioButton[] granularities; - + private JTextArea dataSelectionText; - - private JTextField binLength, encounterGap; + + private JTextField binLength, minCalls, encounterGap; private DataSelector dataSelector; - - public GranularityCard(TethysControl tethysControl, PamDataBlock dataBlock) { + + private DetectionsExportWizard detectionsExportWizard; + + private int encounterIndex, binnedIndex; + + public GranularityCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { super(tethysControl, "Granularity", dataBlock); + this.detectionsExportWizard = detectionsExportWizard; setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); - + // granularity GranularityEnumType[] grans = GranularityEnumType.values(); granularities = new JRadioButton[grans.length]; @@ -52,34 +57,41 @@ public class GranularityCard extends ExportWizardCard { GridBagConstraints c = new PamGridBagContraints(); granPanel.setBorder(new TitledBorder("Granularity")); ButtonGroup granGroup = new ButtonGroup(); + GranularityChange gc = new GranularityChange(); for (int i = 0; i < grans.length; i++) { c.gridx = 0; granularities[i] = new JRadioButton(PGranularityType.prettyString(grans[i])); granularities[i].setToolTipText(PGranularityType.toolTip(grans[i])); + granularities[i].addActionListener(gc); granPanel.add(granularities[i], c); granGroup.add(granularities[i]); if (grans[i] == GranularityEnumType.BINNED) { + binnedIndex = i; c.gridx++; granPanel.add(new JLabel(" bin duration ", JLabel.RIGHT), c); c.gridx++; granPanel.add(binLength = new JTextField(5), c); c.gridx++; - granPanel.add(new JLabel(" (s) ", JLabel.LEFT), c); - + granPanel.add(new JLabel("(s), min Calls", JLabel.LEFT), c); + c.gridx++; + granPanel.add(minCalls = new JTextField(5), c); + binLength.setToolTipText("Time bin duration in seconds"); + minCalls.setToolTipText("Minimum number of calls for a bin to be output"); } if (grans[i] == GranularityEnumType.ENCOUNTER) { + encounterIndex = i; c.gridx++; granPanel.add(new JLabel(" min gap ", JLabel.RIGHT), c); c.gridx++; granPanel.add(encounterGap = new JTextField(5), c); c.gridx++; - granPanel.add(new JLabel(" (s) ", JLabel.LEFT), c); - + granPanel.add(new JLabel("(s) ", JLabel.LEFT), c); + encounterGap.setToolTipText("Minimum gap between separate encounters"); } c.gridy++; } this.add(granPanel); - + // data selection dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); if (dataSelector != null) { @@ -101,7 +113,22 @@ public class GranularityCard extends ExportWizardCard { dataPanel.add(BorderLayout.CENTER, sp); this.add(dataPanel); } - + + } + + private class GranularityChange implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + enableControls(); + } + + } + + private void enableControls() { + binLength.setEnabled(granularities[binnedIndex].isSelected()); + minCalls.setEnabled(granularities[binnedIndex].isSelected()); + encounterGap.setEnabled(granularities[encounterIndex].isSelected()); } protected void newDataSelection() { @@ -129,7 +156,29 @@ public class GranularityCard extends ExportWizardCard { break; } } - + if (streamExportParams.granularity == GranularityEnumType.BINNED) { + try { + streamExportParams.binDurationS = Double.valueOf(binLength.getText()); + } + catch (NumberFormatException e) { + return detectionsExportWizard.showWarning("Invalid bin duration parameter"); + } + try { + streamExportParams.minBinCount = Integer.valueOf(minCalls.getText()); + } + catch (NumberFormatException e) { + return detectionsExportWizard.showWarning("Invalid minimum call count"); + } + } + if (streamExportParams.granularity == GranularityEnumType.ENCOUNTER) { + try { + streamExportParams.encounterGapS = Double.valueOf(encounterGap.getText()); + } + catch (NumberFormatException e) { + return detectionsExportWizard.showWarning("Invalid encounter gap parameter"); + } + } + return streamExportParams.granularity != null; } @@ -139,7 +188,11 @@ public class GranularityCard extends ExportWizardCard { for (int i = 0; i < grans.length; i++) { granularities[i].setSelected(streamExportParams.granularity == grans[i]); } + binLength.setText(String.format("%3.1f", streamExportParams.binDurationS)); + minCalls.setText(String.format("%d", streamExportParams.minBinCount)); + encounterGap.setText(String.format("%3.1f", streamExportParams.encounterGapS)); newDataSelection(); + enableControls(); } } From 65dfb5688b7fb8374dd0bb6b48dec2c565fe8c16 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 11 Sep 2023 17:47:50 +0100 Subject: [PATCH 47/65] More on granularity Nice format of granularity in display tables. --- src/tethys/dbxml/DBXMLQueries.java | 56 ++++++++++++++++++- src/tethys/niluswraps/PDeployment.java | 17 ++++++ .../swing/DatablockDetectionsPanel.java | 23 +++++++- 3 files changed, 93 insertions(+), 3 deletions(-) diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 1275ff86..56abfde8 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -4,6 +4,7 @@ import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; +import java.util.List; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.parsers.DocumentBuilder; @@ -25,7 +26,10 @@ import nilus.Deployment; import nilus.Deployment.Instrument; import nilus.DeploymentRecoveryDetails; import nilus.DescriptionType; +import nilus.DetectionEffortKind; import nilus.Detections; +import nilus.GranularityEnumType; +import nilus.GranularityType; import nilus.Helper; import tethys.TethysControl; import tethys.TethysTimeFuncs; @@ -748,6 +752,27 @@ public class DBXMLQueries { return root.getTextContent(); } + + public String getElementAttribute(Element root, String elName, String attribute) { + 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; + } + int count = nodeList.getLength(); + for (int i = 0; i < count; i++) { + Node firstNode = nodeList.item(i); + if (firstNode instanceof Element) { + root = (Element) firstNode; + break; + } + } + } + return root.getAttribute(attribute); + } + public Document convertStringToXMLDocument(String xmlString) { //Parser that produces DOM object trees from XML content DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); @@ -776,7 +801,7 @@ public class DBXMLQueries { public Detections getDetectionsDocInfo(String detectionsDocName) { String oldqueryBase = "{\"species\":{\"query\":{\"op\":\"lib:abbrev2tsn\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]},\"return\":{\"op\":\"lib:tsn2abbrev\",\"optype\":\"function\",\"operands\":[\"%s\",\"SIO.SWAL.v1\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; // updated May 23 - String queryBase = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\",\"Detections/QualityAssurance\",\"Detections/UserId\",\"Detections/MetadataInfo\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String queryBase = "{\"species\":{\"query\":{\"op\":\"lib:completename2tsn\",\"optype\":\"function\",\"operands\":[\"%s\"]},\"return\":{\"op\":\"lib:tsn2completename\",\"optype\":\"function\",\"operands\":[\"%s\"]}},\"return\":[\"Detections/Id\",\"Detections/Description\",\"Detections/DataSource\",\"Detections/Algorithm\",\"Detections/QualityAssurance\",\"Detections/UserId\",\"Detections/MetadataInfo\",\"Detections/Effort\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Detections/Id\",\"DetectionsDocName\"],\"optype\":\"binary\"}],\"enclose\":1}"; String query = queryBase.replace("DetectionsDocName", detectionsDocName); DBQueryResult queryResult; try { @@ -819,6 +844,35 @@ public class DBXMLQueries { description.setAbstract(getElementData(result, "Description.Abstract")); description.setMethod(getElementData(result, "Description.Method")); description.setObjectives(getElementData(result, "Description.Objectives")); + + // try to find the granularity. + String granularityString = getElementData(result, "Effort.Kind.Granularity"); + GranularityEnumType granularity = null; + if (granularityString != null) { + granularity = GranularityEnumType.fromValue(granularityString); + List kinds = detections.getEffort().getKind(); + DetectionEffortKind kind = new DetectionEffortKind(); + GranularityType granularityType = new GranularityType(); + granularityType.setValue(granularity); + kind.setGranularity(granularityType); + // try to find the rest of the granularity information. + String binSize_m = getElementAttribute(result, "Effort.Kind.Granularity", "BinSize_m"); + String encounterGap_m = getElementAttribute(result, "Effort.Kind.Granularity", "EncounterGap_m"); + String firstBinStart = getElementAttribute(result, "Effort.Kind.Granularity", "FirstBinStart"); + try { + granularityType.setBinSizeM(Double.valueOf(binSize_m)); + } + catch (NumberFormatException e) { + } + try { + granularityType.setEncounterGapM(Double.valueOf(encounterGap_m)); + } + catch (NumberFormatException e) { + } + + kinds.add(kind); + } +// String diff --git a/src/tethys/niluswraps/PDeployment.java b/src/tethys/niluswraps/PDeployment.java index 5f9067db..4a7f29c3 100644 --- a/src/tethys/niluswraps/PDeployment.java +++ b/src/tethys/niluswraps/PDeployment.java @@ -3,6 +3,7 @@ package tethys.niluswraps; import PamUtils.PamCalendar; import nilus.Deployment; import nilus.DeploymentRecoveryDetails; +import nilus.GranularityType; import tethys.TethysTimeFuncs; import tethys.deployment.RecordingPeriod; /** @@ -61,5 +62,21 @@ public class PDeployment { } } + public static String formatGranularity(GranularityType granularity) { + if (granularity == null) { + return null; + } + String str = String.format("%s", granularity.getValue()); + Double bin = granularity.getBinSizeM(); + if (bin != null) { + str += String.format(" (%3.1f s)", bin*60); + } + Double gap = granularity.getEncounterGapM(); + if (gap != null) { + str += String.format( " (%3.1f s)", gap); + } + return str; + } + } diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index 2e2ebb40..dfa0c2cc 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -5,6 +5,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.List; import javax.swing.JComponent; import javax.swing.JLabel; @@ -18,12 +19,15 @@ import javax.swing.table.AbstractTableModel; import PamView.tables.SwingTableColumnWidths; import PamguardMVC.PamDataBlock; +import nilus.DetectionEffortKind; import nilus.Detections; +import nilus.GranularityType; import tethys.TethysControl; import tethys.TethysState; import tethys.TethysState.StateType; import tethys.dbxml.TethysException; import tethys.detection.StreamDetectionsSummary; +import tethys.niluswraps.PDeployment; import tethys.niluswraps.PDetections; import tethys.niluswraps.TethysCollections; @@ -167,7 +171,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa private class TableModel extends AbstractTableModel { - private String[] colNames = {"Document", "Count", "Abstract"}; + private String[] colNames = {"Document", "Granularity", "Count", "Abstract"}; @Override public int getRowCount() { @@ -205,8 +209,23 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa case 0: return dets.getId(); case 1: - return pDets.count; + List kinds = dets.getEffort().getKind(); + if (kinds == null) { + return null; + } + for (DetectionEffortKind kind : kinds) { + if (kind.getGranularity() != null) { + GranularityType granularity = kind.getGranularity(); + return PDeployment.formatGranularity(granularity); +// if (granularity != null) { +// return granularity.getValue(); +// } + } + } + break; case 2: + return pDets.count; + case 3: return dets.getDescription().getAbstract(); } return null; From 85fd84d18ace66f4c675ea7c0e07db794ebf3c96 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 11 Sep 2023 21:52:46 +0100 Subject: [PATCH 48/65] Fix binned output couple of fixes in binned output --- src/tethys/TethysControl.java | 11 +++--- .../detection/BinnedGranularityHandler.java | 3 +- src/tethys/pamdata/TethysParameterPacker.java | 35 ++++++++++--------- .../swing/DatablockDetectionsPanel.java | 25 +++++++++++-- .../swing/PAMGuardDeploymentsTable.java | 2 +- 5 files changed, 51 insertions(+), 25 deletions(-) diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index c5f26031..637f4bcb 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -596,17 +596,20 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet PamFileFilter fileFilter = new PamFileFilter("XML documents", ".xml"); // fileFilter - JFileChooser fileChooser = new PamFileChooser(); + JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileFilter(fileFilter); fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); // make a default name based on the document id and the dataset directory. String defFolder = PamFolders.getDefaultProjectFolder(); + File defFile = null; if (defFolder != null) { - defFolder = String.format("%s%s%s.xml", defFolder,File.separator,documentId); - File defFile = new File(defFolder); - fileChooser.setSelectedFile(defFile); + defFolder = String.format("%s%s%s_%s.xml", defFolder,File.separator,collection,documentId); + defFile = new File(defFolder); fileChooser.setAcceptAllFileFilterUsed(true); + fileChooser.setSelectedFile(defFile); +// fileChooser.setSelectedFile(new File(String.format("%s.xml", documentId))); +// fileChooser.set } int state = fileChooser.showSaveDialog(getGuiFrame()); if (state != JFileChooser.APPROVE_OPTION) return; diff --git a/src/tethys/detection/BinnedGranularityHandler.java b/src/tethys/detection/BinnedGranularityHandler.java index b39f4550..056fc092 100644 --- a/src/tethys/detection/BinnedGranularityHandler.java +++ b/src/tethys/detection/BinnedGranularityHandler.java @@ -88,6 +88,7 @@ public class BinnedGranularityHandler extends GranularityHandler { Detection[] detections = null; if (dataUnit.getTimeMilliseconds() >= binEndMillis) { detections = closeBins(dataUnit.getTimeMilliseconds()); + prepare(dataUnit.getTimeMilliseconds()); } String speciesCode = speciesManager.getSpeciesCode(dataUnit); Detection det = currentDetections.get(speciesCode); @@ -140,8 +141,6 @@ public class BinnedGranularityHandler extends GranularityHandler { } - // finally, start new bins (not really needed on last call, but do anyway). - startBin(binEndMillis); /* * Clean up the end of the array and return detections that have enough calls. diff --git a/src/tethys/pamdata/TethysParameterPacker.java b/src/tethys/pamdata/TethysParameterPacker.java index b663db20..c4c7e6f7 100644 --- a/src/tethys/pamdata/TethysParameterPacker.java +++ b/src/tethys/pamdata/TethysParameterPacker.java @@ -132,26 +132,29 @@ public class TethysParameterPacker { * first do the data filter. I can't see any way of doing this * without creating a doc as was in the helper example. */ - QName qnamef = new QName(MarshalXML.schema, "datafilter", "ty"); - JAXBElement jaxelf = new JAXBElement( - qnamef, String.class, parameterSet.getParentObject().getClass().getCanonicalName()); - Document docf = null; - try { - docf = marshaller.marshalToDOM(jaxelf); - } catch (JAXBException | ParserConfigurationException e1) { - e1.printStackTrace(); - } - Element elf = docf.getDocumentElement(); - elList.add(elf);/** - * Is there a data filter ? If so, write it's - * XML parameters out here. - */ DataSelector dataSelector = pamDataBlock.getDataSelector(tethysControl.getDataSelectName(), false); if (dataSelector != null) { DataSelectParams filterParams = dataSelector.getParams(); if (filterParams != null) { - Element pEl = xmlWriter.writeObjectData(docf, elf, filterParams, null); -// if (pEl != null) { + int selected = filterParams.getCombinationFlag(); + if (selected != DataSelectParams.DATA_SELECT_DISABLE) { + QName qnamef = new QName(MarshalXML.schema, "datafilter", "ty"); + JAXBElement jaxelf = new JAXBElement( + qnamef, String.class, parameterSet.getParentObject().getClass().getCanonicalName()); + Document docf = null; + try { + docf = marshaller.marshalToDOM(jaxelf); + } catch (JAXBException | ParserConfigurationException e1) { + e1.printStackTrace(); + } + Element elf = docf.getDocumentElement(); + elList.add(elf);/** + * Is there a data filter ? If so, write it's + * XML parameters out here. + */ + Element pEl = xmlWriter.writeObjectData(docf, elf, filterParams, null); + } + // if (pEl != null) { //// filterEl.appendChild(pEl); // elf.appendChild(filterEl); // } diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index dfa0c2cc..1decb0fd 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -17,6 +17,8 @@ import javax.swing.JTable; import javax.swing.border.TitledBorder; import javax.swing.table.AbstractTableModel; +import PamView.PamGui; +import PamView.dialog.warn.WarnOnce; import PamView.tables.SwingTableColumnWidths; import PamguardMVC.PamDataBlock; import nilus.DetectionEffortKind; @@ -120,7 +122,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa JPopupMenu popMenu = new JPopupMenu(); - JMenuItem menuItem = new JMenuItem("Delete " + pDets.detections.getId()); + JMenuItem menuItem = new JMenuItem("Delete document " + pDets.detections.getId()); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -129,7 +131,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa }); popMenu.add(menuItem); - menuItem = new JMenuItem("Display " + pDets.detections.getId()); + menuItem = new JMenuItem("Display document " + pDets.detections.getId()); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -138,6 +140,15 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa }); popMenu.add(menuItem); + menuItem = new JMenuItem("Export document " + pDets.detections.getId()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportDocument(pDets); + } + }); + popMenu.add(menuItem); + popMenu.show(e.getComponent(), e.getX(), e.getY()); @@ -145,6 +156,11 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } protected void deleteDocument(PDetections pDets) { + String msg = String.format("Are you sure you want to delete the Detections document %s ?", pDets.detections.getId()); + int ans = WarnOnce.showWarning(PamGui.findComponentWindow(mainPanel), "Delete Document", msg, WarnOnce.OK_CANCEL_OPTION); + if (ans != WarnOnce.OK_OPTION) { + return; + } try { getTethysControl().getDbxmlConnect().deleteDocument(pDets.detections); } catch (TethysException e) { @@ -159,6 +175,11 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } + private void exportDocument(PDetections pDets) { + getTethysControl().exportDocument(TethysCollections.Detections.toString(), pDets.detections.getId()); + + } + private PDetections detectionsForRow(int iRow) { if (streamDetectionsSummary == null || streamDetectionsSummary.detectionsDocs == null) { return null; diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index d17a659d..688c949b 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -134,7 +134,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } if (matchedDeployments.size() == 1) { JPopupMenu popMenu = new JPopupMenu(); - JMenuItem menuItem = new JMenuItem("Remove deployment document " + matchedDeployments.get(0)); + JMenuItem menuItem = new JMenuItem("Delete deployment document " + matchedDeployments.get(0)); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { From db1cc75bc1f96e39f49eaf0278b38fea801e6846 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 13 Sep 2023 17:28:47 +0100 Subject: [PATCH 49/65] More output More changes to Detections output. --- src/PamguardMVC/PamDataBlock.java | 4 +- src/RightWhaleEdgeDetector/RWEDataBlock.java | 12 + .../species/RWTethysDataProvider.java | 37 ++ src/Spectrogram/SpectrogramDisplay.java | 3 + src/dataMap/DataStreamPanel.java | 11 +- src/dataMap/OfflineDataMap.java | 10 +- src/tethys/TethysControl.java | 9 + src/tethys/dbxml/DBXMLQueries.java | 401 +++++++++--------- src/tethys/detection/DetectionsHandler.java | 103 ++++- .../EncounterGranularityHandler.java | 130 +++++- src/tethys/detection/GranularityHandler.java | 2 +- src/tethys/niluswraps/PDeployment.java | 2 +- src/tethys/niluswraps/PDetections.java | 6 +- src/tethys/output/DatablockSynchInfo.java | 30 +- src/tethys/pamdata/AutoTethysProvider.java | 45 +- .../swing/DatablockDetectionsPanel.java | 52 ++- src/tethys/swing/DatablockSynchPanel.java | 4 +- src/tethys/swing/DetectionsExportPanel.java | 3 +- src/tethys/swing/TethysMainPanel.java | 2 +- 19 files changed, 631 insertions(+), 235 deletions(-) create mode 100644 src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index feff646e..7779ea38 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -76,8 +76,10 @@ import PamguardMVC.background.BackgroundDataBlock; import PamguardMVC.background.BackgroundManager; import PamguardMVC.dataOffline.OfflineDataLoadInfo; import PamguardMVC.dataOffline.OfflineDataLoading; +import PamguardMVC.dataSelector.DataSelectParams; import PamguardMVC.dataSelector.DataSelector; import PamguardMVC.dataSelector.DataSelectorCreator; +import PamguardMVC.dataSelector.DataSelectorSettings; import PamguardMVC.dataSelector.NullDataSelectorCreator; import PamguardMVC.datamenus.DataMenuParent; import PamguardMVC.nanotime.NanoTimeCalculator; @@ -2840,7 +2842,7 @@ public class PamDataBlock extends PamObservable { * @return temporary copy of the data */ public ArrayList getDataCopy(long t1, long t2, boolean assumeOrder, DataSelector dataSelector) { - if (dataSelector == null) { + if (dataSelector == null || dataSelector.getParams().getCombinationFlag() == DataSelectParams.DATA_SELECT_DISABLE) { return getDataCopy(t1, t2, assumeOrder); } else { diff --git a/src/RightWhaleEdgeDetector/RWEDataBlock.java b/src/RightWhaleEdgeDetector/RWEDataBlock.java index a603eafe..4ddfd65b 100644 --- a/src/RightWhaleEdgeDetector/RWEDataBlock.java +++ b/src/RightWhaleEdgeDetector/RWEDataBlock.java @@ -7,7 +7,10 @@ import PamguardMVC.dataOffline.OfflineDataLoadInfo; import PamguardMVC.dataSelector.DataSelectorCreator; import RightWhaleEdgeDetector.datasel.RWDataSelCreator; import RightWhaleEdgeDetector.species.RWSpeciesManager; +import RightWhaleEdgeDetector.species.RWTethysDataProvider; import pamScrollSystem.ViewLoadObserver; +import tethys.TethysControl; +import tethys.pamdata.TethysDataProvider; import tethys.species.DataBlockSpeciesManager; import whistlesAndMoans.AbstractWhistleDataBlock; @@ -19,6 +22,7 @@ public class RWEDataBlock extends AbstractWhistleDataBlock implemen private RWDataSelCreator dataSelCreator; private RWSpeciesManager rwSpeciesManager; + private RWTethysDataProvider rwTethysDataProvider; public RWEDataBlock(RWEControl rweControl, String dataName, RWEProcess rweProcess, int channelMap) { @@ -65,4 +69,12 @@ public class RWEDataBlock extends AbstractWhistleDataBlock implemen return rwSpeciesManager; } + @Override + public TethysDataProvider getTethysDataProvider(TethysControl tethysControl) { + if (rwTethysDataProvider == null) { + rwTethysDataProvider = new RWTethysDataProvider(tethysControl, rweProcess.getRweDataBlock()); + } + return rwTethysDataProvider; + } + } diff --git a/src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java b/src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java new file mode 100644 index 00000000..3577bd12 --- /dev/null +++ b/src/RightWhaleEdgeDetector/species/RWTethysDataProvider.java @@ -0,0 +1,37 @@ +package RightWhaleEdgeDetector.species; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import RightWhaleEdgeDetector.RWEDataUnit; +import nilus.Detection; +import nilus.Detection.Parameters; +import tethys.TethysControl; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; +import tethys.pamdata.AutoTethysProvider; + +public class RWTethysDataProvider extends AutoTethysProvider { + + public RWTethysDataProvider(TethysControl tethysControl, PamDataBlock pamDataBlock) { + super(tethysControl, pamDataBlock); + } + + @Override + public Detection createDetection(PamDataUnit dataUnit, TethysExportParams tethysExportParams, + StreamExportParams streamExportParams) { + Detection detection = super.createDetection(dataUnit, tethysExportParams, streamExportParams); + if (detection == null) { + return null; + } + + RWEDataUnit rweDataUnit = (RWEDataUnit) dataUnit; + + Parameters parameters = detection.getParameters(); + parameters.setScore((double) rweDataUnit.rweSound.soundType); + double snr = 20.*Math.log10(rweDataUnit.rweSound.signal/rweDataUnit.rweSound.noise); + parameters.setSNRDB(snr); + + return detection; + } + +} diff --git a/src/Spectrogram/SpectrogramDisplay.java b/src/Spectrogram/SpectrogramDisplay.java index 99c86de6..bc1726cf 100644 --- a/src/Spectrogram/SpectrogramDisplay.java +++ b/src/Spectrogram/SpectrogramDisplay.java @@ -1639,6 +1639,9 @@ InternalFrameListener, DisplayPanelContainer, SpectrogramParametersUser, PamSett return; } long t1 = dataUnit.getTimeMilliseconds()-viewerScroller.getValueMillis(); + if (timeAxis == null) { + return; + } int x1 = (int) Math.floor(timeAxis.getPosition(t1/1000)); int x2 = x1; if (dataUnit.getDurationInMilliseconds() != null) { diff --git a/src/dataMap/DataStreamPanel.java b/src/dataMap/DataStreamPanel.java index aaa743e3..275be1f5 100644 --- a/src/dataMap/DataStreamPanel.java +++ b/src/dataMap/DataStreamPanel.java @@ -751,7 +751,16 @@ public class DataStreamPanel extends JPanel implements DataMapObserver { } else if (endTimeArrow != null && endTimeArrow.contains(me.getPoint())) { tipText = "Data End: " + PamCalendar.formatDateTime(dataBlock.getCurrentViewDataEnd(), false); } else { - tipText = "Cursor: " + PamCalendar.formatDateTime(tm, true); + OfflineDataMap dMap = dataBlock.getPrimaryDataMap(); + if (dMap != null) { + tipText = String.format("%s Data from

%s to %s

Cursor: %s", dataBlock.getDataName(), + PamCalendar.formatDateTime(dMap.getFirstDataTime(), false), + PamCalendar.formatDateTime(dMap.getLastDataTime(), false), + PamCalendar.formatDateTime(tm, true)); + } + else { + tipText = "Cursor: " + PamCalendar.formatDateTime(tm, true); + } } // tipText += "
Panel height = " + getHeight(); diff --git a/src/dataMap/OfflineDataMap.java b/src/dataMap/OfflineDataMap.java index cf9d9d37..d51ee120 100644 --- a/src/dataMap/OfflineDataMap.java +++ b/src/dataMap/OfflineDataMap.java @@ -84,6 +84,8 @@ abstract public class OfflineDataMap { public static final int POINT_END = 0x8; // 8 public static final int IN_DATA = 0x10; // 16 public static final int NO_DATA = 0x20; // 32 + + private static final long oneDayInMillis = 3600L*24L*1000L; public OfflineDataMap(OfflineDataStore offlineDataStore, PamDataBlock parentDataBlock) { super(); @@ -145,10 +147,10 @@ abstract public class OfflineDataMap { */ synchronized public void addDataPoint(TmapPoint mapPoint) { boolean first = (mapPoints.size() == 0); - if (mapPoint.getStartTime() > 0) { + if (mapPoint.getStartTime() > oneDayInMillis) { firstDataTime = Math.min(firstDataTime, mapPoint.getStartTime()); } - if (mapPoint.getEndTime() > 0) { + if (mapPoint.getEndTime() > oneDayInMillis) { lastDataTime = Math.max(lastDataTime, mapPoint.getEndTime()); // if (mapPoint.getEndTime() > System.currentTimeMillis()) { // System.out.println("Stupid large data time in " + mapPoint.getName()); @@ -273,10 +275,10 @@ abstract public class OfflineDataMap { while (it.hasNext()) { aPoint = it.next(); - if (aPoint.getStartTime() > 0) { + if (aPoint.getStartTime() > oneDayInMillis) { firstDataTime = Math.min(firstDataTime, aPoint.getStartTime()); } - if (aPoint.getEndTime() > 0) { + if (aPoint.getEndTime() > oneDayInMillis) { lastDataTime = Math.max(lastDataTime, aPoint.getEndTime()); } n = aPoint.getNDatas(); diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 637f4bcb..bd6e70f5 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -516,6 +516,15 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet count += dbxmlQueries.countData(synchInfo.getDataBlock(), pDepl.deployment.getId()); } synchInfo.setDataCount(count); + // also count the actual number of Detectoin documents + ArrayList someNames = getDbxmlQueries().getDetectionsDocuments(synchInfo.getDataBlock(), null); + if (someNames == null) { + synchInfo.setDetectionDocumentCount(0); + } + else { + synchInfo.setDetectionDocumentCount(someNames.size()); + } + i++; } // int[] counts = dbxmlQueries.countDataForProject(deplData.getProject(), dataPrefixes); diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 56abfde8..ecda02e6 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -47,7 +47,7 @@ public class DBXMLQueries { private TethysControl tethysControl; private DBXMLConnect dbXMLConnect; - + private PamWarning queryWarning; public DBXMLQueries(TethysControl tethysControl, DBXMLConnect dbXMLConnect) { @@ -119,7 +119,7 @@ public class DBXMLQueries { queryWarning.setEndOfLife(t2+10000); return result; } - + } private DBQueryResult executeQueryT(String jsonQueryString) throws TethysQueryException { @@ -136,7 +136,7 @@ public class DBXMLQueries { try { JerseyClient jerseyClient = dbxmlConnect.getJerseyClient(); -// Queries queries = new Queries(jerseyClient); + // Queries queries = new Queries(jerseyClient); queryResult = jerseyClient.queryJSON(jsonQueryString, 0); schemaPlan = jerseyClient.queryJSON(jsonQueryString, 1); @@ -148,7 +148,7 @@ public class DBXMLQueries { } return new DBQueryResult(System.currentTimeMillis()-t1, queryResult, schemaPlan); } - + /** * Check whether or not to strip of the s of one of the collection names. * This is caused by some daft thing whereby the Deployments colleciton is called Deployments @@ -169,7 +169,7 @@ public class DBXMLQueries { } return collection; } - + /** * Get a list of all documents in a collection. * @param collection @@ -180,18 +180,18 @@ public class DBXMLQueries { return null; } collection = checkCollectionPlural(collection); -// if (collection.endsWith("s")) { -// collection = collection.substring(0, collection.length()-1); -// } + // if (collection.endsWith("s")) { + // collection = collection.substring(0, collection.length()-1); + // } String baseQuery = "{\"return\":[\"COLLECTIONNAME/Id\"],\"select\":[],\"enclose\":1}"; baseQuery = baseQuery.replace("COLLECTIONNAME", collection); String tagName = "Id"; - + if (collection.equals("SpeciesAbbreviations")) { baseQuery = "{\"return\":[\"Abbreviations/Name\"],\"select\":[],\"enclose\":1}"; tagName = "Name"; } - + DBQueryResult result; try { result = executeQuery(baseQuery); @@ -216,7 +216,7 @@ public class DBXMLQueries { String docId = aNode.getTextContent(); docIds.add(docId); } - + return docIds; } @@ -236,7 +236,7 @@ public class DBXMLQueries { return null; } -// System.out.println("Project query execution time millis = " + result.queryTimeMillis); + // 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. @@ -264,22 +264,22 @@ public class DBXMLQueries { projectNames.add(projName); } } -// } -// if (aNode instanceof Element) { -// Node depEl = ((Element) aNode).getFirstChild(); -// if (depEl == null) { -// continue; -// } -// if (depEl instanceof Element) { -// Element projEl = (Element) ((Element) depEl).getFirstChild(); -// String projName = projEl.getTextContent(); -// if (projName != null) { -// if (!projectNames.contains(projName)) { -// projectNames.add(projName); -// } -// } -// } -// } + // } + // if (aNode instanceof Element) { + // Node depEl = ((Element) aNode).getFirstChild(); + // if (depEl == null) { + // continue; + // } + // if (depEl instanceof Element) { + // Element projEl = (Element) ((Element) depEl).getFirstChild(); + // String projName = projEl.getTextContent(); + // if (projName != null) { + // if (!projectNames.contains(projName)) { + // projectNames.add(projName); + // } + // } + // } + // } } Collections.sort(projectNames); @@ -310,7 +310,7 @@ public class DBXMLQueries { if (result == null) { return null; } -// System.out.println("Deployment query execution time millis = " + result.queryTimeMillis); + // System.out.println("Deployment query execution time millis = " + result.queryTimeMillis); PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); @@ -319,19 +319,19 @@ public class DBXMLQueries { return null; } -// System.out.println(pamXMLWriter.getAsString(doc)); + // System.out.println(pamXMLWriter.getAsString(doc)); ArrayList deployments = new ArrayList<>(); NodeList returns = doc.getElementsByTagName("Deployment"); -// if (returns.getLength() == 0) { -// // try REsult instead ! -// returns = doc.getElementsByTagName("Result"); -// } + // if (returns.getLength() == 0) { + // // try REsult instead ! + // returns = doc.getElementsByTagName("Result"); + // } // System.out.println("N projects = " + returns.getLength()); int n = returns.getLength(); -// Queries queries = new Queries(null) + // Queries queries = new Queries(null) for (int i = 0; i < n; i++) { Node aNode = returns.item(i); if (aNode instanceof Element) { @@ -357,8 +357,8 @@ public class DBXMLQueries { 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)); + // 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()); @@ -381,7 +381,7 @@ public class DBXMLQueries { /** * Get a list of Detections documents which associate with a datablock and a deploymentId. * @param dataBlock - * @param deploymentId + * @param deploymentId can be null to get all docs for data block * @return */ public ArrayList getDetectionsDocuments(PamDataBlock dataBlock, String deploymentId) { @@ -422,9 +422,9 @@ public class DBXMLQueries { ArrayList detectionsNames = new ArrayList(); int count = 0; NodeList returns = doc.getElementsByTagName("Detections"); -// if (returns.getLength() == 0) { -// returns = doc.getElementsByTagName("Result"); -// } + // if (returns.getLength() == 0) { + // returns = doc.getElementsByTagName("Result"); + // } for (int i = 0; i < returns.getLength(); i++) { Node aNode = returns.item(i); String docName = aNode.getTextContent(); @@ -433,80 +433,81 @@ public class DBXMLQueries { return detectionsNames; } + /** - * Get the names of all detection documents for a given deployment for all data streams. - * @param deploymentId - * @return - */ - public ArrayList getDetectionsDocuments(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 = null; - try { - queryResult = executeQuery(queryStr); - } catch (TethysQueryException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - 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"); - if (returns.getLength() == 0) { - returns = doc.getElementsByTagName("Result"); - } - for (int i = 0; i < returns.getLength(); i++) { - Node aNode = returns.item(i); - detectionDocs.add(aNode.getTextContent()); - } - return detectionDocs; + * Get the names of all detection documents for a given deployment for all data streams. + * @param deploymentId + * @return + */ + public ArrayList getDetectionsDocuments(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 = null; + try { + queryResult = executeQuery(queryStr); + } catch (TethysQueryException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + 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"); + if (returns.getLength() == 0) { + returns = doc.getElementsByTagName("Result"); + } + for (int i = 0; i < returns.getLength(); i++) { + Node aNode = returns.item(i); + detectionDocs.add(aNode.getTextContent()); + } + return detectionDocs; + } + public int countData(PamDataBlock dataBlock, String deploymentId) { -// /** -// * first query for Detections documents associated with this deployment and datablock. -// */ -// 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/Algorithm/Software\",\"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/Algorithm/Software\",\"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; -// try { -// doc = queryResult.getDocument(); -// } catch (ParserConfigurationException | SAXException | IOException e) { -// e.printStackTrace(); -// return 0; -// } -// -// int count = 0; -// NodeList returns = doc.getElementsByTagName("Return"); + // /** + // * first query for Detections documents associated with this deployment and datablock. + // */ + // 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/Algorithm/Software\",\"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/Algorithm/Software\",\"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; + // try { + // doc = queryResult.getDocument(); + // } catch (ParserConfigurationException | SAXException | IOException e) { + // e.printStackTrace(); + // return 0; + // } + // + // int count = 0; + // NodeList returns = doc.getElementsByTagName("Return"); ArrayList documentNames = getDetectionsDocuments(dataBlock, deploymentId); if (documentNames == null) { return 0; } int count = 0; for (String docName : documentNames) { -// System.out.println(aNode.getTextContent()); + // System.out.println(aNode.getTextContent()); int count2 = countDetections2(docName); count += count2; //countDetecionsData(docName); @@ -515,22 +516,22 @@ public class DBXMLQueries { } public String getDocument(String collection, String documentId) { -// String queryBase = "return:(collection(\"replaceCollectionName\")/Detections[Id=\"ReplaceDocumentId\"])"; -// queryBase = queryBase.replace("replaceCollectionName", collection); -// queryBase = queryBase.replace("ReplaceDocumentId", documentId); -// -// String result = null; -// try { -// Queries queries = dbXMLConnect.getTethysQueries(); -// result = queries.QueryTethys(queryBase); -//// System.out.println(result); -// } -// catch (Exception e) { -// System.out.println("Error executing " + queryBase); -//// e.printStackTrace(); -// return null; -// } -// return result; + // String queryBase = "return:(collection(\"replaceCollectionName\")/Detections[Id=\"ReplaceDocumentId\"])"; + // queryBase = queryBase.replace("replaceCollectionName", collection); + // queryBase = queryBase.replace("ReplaceDocumentId", documentId); + // + // String result = null; + // try { + // Queries queries = dbXMLConnect.getTethysQueries(); + // result = queries.QueryTethys(queryBase); + //// System.out.println(result); + // } + // catch (Exception e) { + // System.out.println("Error executing " + queryBase); + //// e.printStackTrace(); + // return null; + // } + // return result; Queries queries = dbXMLConnect.getTethysQueries(); String result = null; @@ -542,9 +543,9 @@ public class DBXMLQueries { } return result; -// String queryBase = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; + // String queryBase = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; } - + /** * Find out if a document exists ? * @param collection @@ -562,10 +563,10 @@ public class DBXMLQueries { if (result == null || result.length() == 0) { return false; } - + return result.contains(documentId); } - + /** * Count on effort detections in a Detections document * @param docName @@ -580,16 +581,16 @@ public class DBXMLQueries { try { Queries queries = dbXMLConnect.getTethysQueries(); result = queries.QueryTethys(query); -// System.out.println(result); + // System.out.println(result); } catch (Exception e) { System.out.println("Error executing " + query); -// e.printStackTrace(); + // e.printStackTrace(); return -1; } int count = 0; try { - count = Integer.valueOf(result); + count = Integer.valueOf(result); } catch (NumberFormatException e) { System.out.println("Unable to interpret count data " + result); @@ -598,67 +599,67 @@ public class DBXMLQueries { return count; } -// /** -// * 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; -// } + // /** + // * 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; -// } + // /** + // * 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 @@ -688,14 +689,14 @@ public class DBXMLQueries { return null; } -// System.out.println(pamXMLWriter.getAsString(doc)); + // System.out.println(pamXMLWriter.getAsString(doc)); NodeList detsDocs = doc.getElementsByTagName("Detections"); int[] blockCounts = new int[dataPrefixes.length]; -// String detDocPrefix = projectId + "_" + dataBlock.getDataName(); + // String detDocPrefix = projectId + "_" + dataBlock.getDataName(); -// int totalCalls = 0; + // int totalCalls = 0; int detCount = 0; int dataIndex; for (int i = 0; i < detsDocs.getLength(); i++) { @@ -716,17 +717,17 @@ public class DBXMLQueries { dataIndex = j; } } -// if (id != null && id.startsWith(detDocPrefix) == false) { -// detCount = 0; -// break; -// } + // 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); + // System.out.printf("%d Added %d for new total %d\n",i, detCount, totalCalls); } return blockCounts; @@ -818,7 +819,7 @@ public class DBXMLQueries { e.printStackTrace(); return null; } -// System.out.println(queryResult.queryResult); + // System.out.println(queryResult.queryResult); Detections detections = new Detections(); try { @@ -844,7 +845,7 @@ public class DBXMLQueries { description.setAbstract(getElementData(result, "Description.Abstract")); description.setMethod(getElementData(result, "Description.Method")); description.setObjectives(getElementData(result, "Description.Objectives")); - + // try to find the granularity. String granularityString = getElementData(result, "Effort.Kind.Granularity"); GranularityEnumType granularity = null; @@ -869,10 +870,10 @@ public class DBXMLQueries { } catch (NumberFormatException e) { } - + kinds.add(kind); } -// String + // String diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index bd1c8370..1770ef85 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -8,6 +8,7 @@ import javax.swing.SwingWorker; import PamController.PamControlledUnit; import PamController.PamguardVersionInfo; import PamModel.PamPluginInterface; +import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamProcess; @@ -84,7 +85,7 @@ public class DetectionsHandler { for (String aDoc : someNames) { Detections detections = tethysControl.getDbxmlQueries().getDetectionsDocInfo(aDoc); int count = tethysControl.getDbxmlQueries().countDetections2(aDoc); - PDetections pDetections = new PDetections(detections, null, count); + PDetections pDetections = new PDetections(detections, dataBlock, aDep, count); detectionsDocs.add(pDetections); } } @@ -364,6 +365,98 @@ public class DetectionsHandler { * @param exportObserver * @return */ + private int countDetections(PamDataBlock dataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { + /* + * This is currently called for the entire dataset, but we will need to loop over specific Deployment documents + * and export the content of each separately. + */ + TethysExportParams exportParams = tethysControl.getTethysExportParams(); + DBXMLConnect dbxmlConnect = tethysControl.getDbxmlConnect(); + DeploymentHandler depHandler = tethysControl.getDeploymentHandler(); + ArrayList deployments = depHandler.getMatchedDeployments(); +// Detections currentDetections = null; + OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); + DataSelector dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); + int totalCount = dataMap.getDataCount(); + int skipCount = 0; + int exportCount = 0; + long lastUnitTime = 0; + DetectionExportProgress prog; + GranularityHandler granularityHandler = GranularityHandler.getHandler(streamExportParams.granularity, tethysControl, dataBlock, exportParams, streamExportParams); + for (PDeployment deployment : deployments) { + int documentCount = 0; + prog = new DetectionExportProgress(deployment, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + exportObserver.update(prog); + granularityHandler.prepare(deployment.getAudioStart()); + // export everything in that deployment. + // need to loop through all map points in this interval. + List mapPoints = dataMap.getMapPoints(); + for (OfflineDataMapPoint mapPoint : mapPoints) { + if (!activeExport) { + prog = new DetectionExportProgress(deployment, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_CANCELED); + exportObserver.update(prog); + } + + if (mapPoint.getEndTime() < deployment.getAudioStart()) { + continue; + } + if (mapPoint.getStartTime() >= deployment.getAudioEnd()) { + break; + } + dataBlock.loadViewerData(mapPoint.getStartTime(), mapPoint.getEndTime(), null); + ArrayList dataCopy = dataBlock.getDataCopy(deployment.getAudioStart(), deployment.getAudioEnd(), true, dataSelector); + skipCount += dataBlock.getUnitsCount() - dataCopy.size(); + for (PamDataUnit dataUnit : dataCopy) { + /* + * Here is where we need to handle the different granularities. + */ + Detection dets[] = granularityHandler.addDataUnit(dataUnit); + if (dets != null) { + exportCount+=dets.length; + documentCount+=dets.length; + } +// Detection det = dataProvider.createDetection(dataUnit, exportParams, streamExportParams); +// exportCount++; +// documentCount++; +// onEffort.getDetection().add(det); + lastUnitTime = dataUnit.getTimeMilliseconds(); + } + + prog = new DetectionExportProgress(deployment, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + exportObserver.update(prog); + +// if (documentCount > 500000 && mapPoint != dataMap.getLastMapPoint()) { +// prog = new DetectionExportProgress(deployment, currentDetections, +// lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); +// exportObserver.update(prog); +// closeDetectionsDocument(currentDetections, mapPoint.getEndTime()); +// try { +// dbxmlConnect.postToTethys(currentDetections); +// } catch (TethysException e) { +// tethysControl.showException(e); +// } +// currentDetections = null; +// } + } + + + + } + + prog = new DetectionExportProgress(null, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COMPLETE); + exportObserver.update(prog); + return exportCount; + }/** + * Export detections in all deployments for this PAMGuard dataset. + * @param dataBlock + * @param streamExportParams + * @param exportObserver + * @return + */ private int exportDetections(PamDataBlock dataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { /* * This is currently called for the entire dataset, but we will need to loop over specific Deployment documents @@ -559,7 +652,13 @@ public class DetectionsHandler { protected Integer doInBackground() throws Exception { Integer ans = null; try { - ans = exportDetections(dataBlock, exportParams, this); + int count = countDetections(dataBlock, exportParams, exportObserver); + String msg = String.format("Do you want to go ahead and output %d %s detections to Tethys?", + count, exportParams.granularity); + int doit = WarnOnce.showWarning("Tethys Detections Export", msg, WarnOnce.OK_CANCEL_OPTION); + if (doit == WarnOnce.OK_OPTION) { + ans = exportDetections(dataBlock, exportParams, this); + } } catch (Exception e) { e.printStackTrace(); diff --git a/src/tethys/detection/EncounterGranularityHandler.java b/src/tethys/detection/EncounterGranularityHandler.java index 061e018e..c1cea4de 100644 --- a/src/tethys/detection/EncounterGranularityHandler.java +++ b/src/tethys/detection/EncounterGranularityHandler.java @@ -1,36 +1,152 @@ package tethys.detection; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Set; + import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import nilus.Detection; +import nilus.SpeciesIDType; import tethys.TethysControl; +import tethys.TethysTimeFuncs; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; +import tethys.pamdata.TethysDataProvider; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.SpeciesMapItem; +/** + * As with the binned Detections, this may generate multiple encounters + * at the same time for different types of sounds. + * @author dg50 + * + */ public class EncounterGranularityHandler extends GranularityHandler { + + private HashMap currentDetections; + private TethysDataProvider dataProvider; + private DataBlockSpeciesManager speciesManager; + private long maxGapMillis; + public EncounterGranularityHandler(TethysControl tethysControl, PamDataBlock dataBlock, TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { super(tethysControl, dataBlock, tethysExportParams, streamExportParams); - // TODO Auto-generated constructor stub + + dataProvider = dataBlock.getTethysDataProvider(tethysControl); + speciesManager = dataBlock.getDatablockSpeciesManager(); + + maxGapMillis = (long) (streamExportParams.encounterGapS*1000); + + currentDetections = new HashMap(); } @Override public void prepare(long timeMillis) { - // TODO Auto-generated method stub - + } @Override public Detection[] addDataUnit(PamDataUnit dataUnit) { - // TODO Auto-generated method stub - return null; + Detection[] completeDetections = checkCurrentEncounters(dataUnit.getTimeMilliseconds()); + // now look for new ones. First get the species of the dataUnit and find it in the hashmap + String speciesCode = speciesManager.getSpeciesCode(dataUnit); + Detection det = currentDetections.get(speciesCode); + if (det == null) { + // need to make a new one. + det = new Detection(); + det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getTimeMilliseconds())); + det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getEndTimeInMilliseconds())); + det.setCount(BigInteger.ONE); + det.setChannel(BigInteger.valueOf(dataUnit.getChannelBitmap())); + // this should always return something, so am going to crash if it doesn't. + // may revisit this later on if we've unassigned things we don't want to label + // in which case they should be rejected earlier than this. + SpeciesMapItem speciesStuff = speciesManager.getSpeciesItem(dataUnit); + SpeciesIDType species = new SpeciesIDType(); + species.setValue(BigInteger.valueOf(speciesStuff.getItisCode())); + det.setSpeciesId(species); + if (speciesStuff.getCallType() != null) { + det.getCall().add(speciesStuff.getCallType()); + } + currentDetections.put(speciesCode, det); + } + else { + // add to current detection. Set new end time and increment count + det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getEndTimeInMilliseconds())); + int count = det.getCount().intValue() + 1; + det.setCount(BigInteger.valueOf(count)); + int chan = det.getChannel().intValue(); + chan |= dataUnit.getChannelBitmap(); + det.setChannel(BigInteger.valueOf(chan)); + } + + + return completeDetections; } + /** + * See if it's time to close off any encounters. + * @param timeMilliseconds current time + * @return list of complete encounters. + */ + private Detection[] checkCurrentEncounters(long timeMilliseconds) { + Set keys = currentDetections.keySet(); + int nGood = 0; + Detection[] newDetections = new Detection[currentDetections.size()]; + for (String aKey : keys) { + Detection aDet = currentDetections.get(aKey); + Long detEnd = TethysTimeFuncs.millisFromGregorianXML(aDet.getEnd()); + if (timeMilliseconds-detEnd > maxGapMillis) { + // only keep if it's got a min number of calls. + if (aDet.getCount().intValue() >= streamExportParams.minBinCount) { + newDetections[nGood++] = aDet; + } + // remove from set. A new one will be created only when required. + currentDetections.remove(aKey); + } + } + + if (nGood == 0) { + return null; + } + else { + return Arrays.copyOf(newDetections, nGood); + } + } + + // private Detection[] checkCurrentEncounters(long timeMilliseconds) { + // if (currentDetections == null || currentDetections.size() == 0) { + // return null; + // } + // int nGood = 0; + // Detection[] newDetections = new Detection[currentDetections.size()]; + // Iterator detIt = currentDetections.iterator(); + // while (detIt.hasNext()) { + // Detection aDet = detIt.next(); + // Long detEnd = TethysTimeFuncs.millisFromGregorianXML(aDet.getEnd()); + // if (timeMilliseconds-detEnd > maxGapMillis) { + // detIt.remove(); + // newDetections[nGood++] = aDet; + // } + // } + // + // if (nGood == 0) { + // return null; + // } + // else { + // return Arrays.copyOf(newDetections, nGood); + // } + // } + @Override public Detection[] cleanup(long timeMillis) { - // TODO Auto-generated method stub - return null; + // get everything still on the go. + return checkCurrentEncounters(timeMillis + maxGapMillis); } } diff --git a/src/tethys/detection/GranularityHandler.java b/src/tethys/detection/GranularityHandler.java index e4e11da2..b2ef8803 100644 --- a/src/tethys/detection/GranularityHandler.java +++ b/src/tethys/detection/GranularityHandler.java @@ -89,7 +89,7 @@ public abstract class GranularityHandler { case CALL: return new CallGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); case ENCOUNTER: - new EncounterGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); + return new EncounterGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); case GROUPED: return new GroupedGranularityHandler(tethysControl, dataBlock, tethysExportParams, streamExportParams); default: diff --git a/src/tethys/niluswraps/PDeployment.java b/src/tethys/niluswraps/PDeployment.java index 4a7f29c3..d1633793 100644 --- a/src/tethys/niluswraps/PDeployment.java +++ b/src/tethys/niluswraps/PDeployment.java @@ -73,7 +73,7 @@ public class PDeployment { } Double gap = granularity.getEncounterGapM(); if (gap != null) { - str += String.format( " (%3.1f s)", gap); + str += String.format( " (%3.1f s)", gap*60.); } return str; } diff --git a/src/tethys/niluswraps/PDetections.java b/src/tethys/niluswraps/PDetections.java index 00fb073a..64602614 100644 --- a/src/tethys/niluswraps/PDetections.java +++ b/src/tethys/niluswraps/PDetections.java @@ -1,5 +1,6 @@ package tethys.niluswraps; +import PamguardMVC.PamDataBlock; import nilus.Detections; public class PDetections { @@ -10,8 +11,11 @@ public class PDetections { public PDeployment deployment; - public PDetections(Detections detections, PDeployment deployment, Integer count) { + public PamDataBlock dataBlock; + + public PDetections(Detections detections, PamDataBlock dataBlock, PDeployment deployment, Integer count) { super(); + this.dataBlock = dataBlock; this.detections = detections; this.deployment = deployment; this.count = count; diff --git a/src/tethys/output/DatablockSynchInfo.java b/src/tethys/output/DatablockSynchInfo.java index d58be25c..71177b0c 100644 --- a/src/tethys/output/DatablockSynchInfo.java +++ b/src/tethys/output/DatablockSynchInfo.java @@ -16,18 +16,28 @@ import tethys.TethysControl; public class DatablockSynchInfo { private PamDataBlock dataBlock; - public PamDataBlock getDataBlock() { - return dataBlock; - } private TethysControl tethysControl; + /** + * Count of individual datas in all Detections documents + */ private int setDataCount; + + /** + * Count of the number of Detections documents + */ + private int detectionDocumentCount; + public DatablockSynchInfo(TethysControl tethysControl, PamDataBlock dataBlock) { super(); this.tethysControl = tethysControl; this.dataBlock = dataBlock; } + + public PamDataBlock getDataBlock() { + return dataBlock; + } /** * Get the stored export params for this data block @@ -44,5 +54,19 @@ public class DatablockSynchInfo { public int getDataCount() { return setDataCount; } + + /** + * @return the detectionDocumentCount + */ + public int getDetectionDocumentCount() { + return detectionDocumentCount; + } + + /** + * @param detectionDocumentCount the detectionDocumentCount to set + */ + public void setDetectionDocumentCount(int detectionDocumentCount) { + this.detectionDocumentCount = detectionDocumentCount; + } } diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 23a465a0..daef05c0 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -20,14 +20,17 @@ import PamguardMVC.PamDataUnit; import PamguardMVC.PamProcess; import PamguardMVC.TFContourData; import PamguardMVC.TFContourProvider; +import binaryFileStorage.DataUnitFileInformation; import generalDatabase.DBSchemaWriter; import generalDatabase.SQLLogging; import nilus.AlgorithmType; -import nilus.AlgorithmType.Parameters; import nilus.Deployment; import nilus.DescriptionType; import nilus.Detection; +import nilus.Detection.Parameters; +import nilus.Detection.Parameters.UserDefined; import nilus.DetectionEffortKind; +import nilus.Helper; import nilus.SpeciesIDType; import tethys.TethysControl; import tethys.TethysTimeFuncs; @@ -42,6 +45,7 @@ import tethys.species.SpeciesMapItem; import whistleClassifier.WhistleContour; import javax.xml.bind.JAXBException; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.*; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; @@ -54,7 +58,9 @@ import java.net.URISyntaxException; /** * Automatically provides Tethys data based on the SQL database interface - * for a data block. + * for a data block. does most of what needs to be done, though individual modules + * may want to override this, call the base createDetection function and then add a + * few more bespoke elements. * @author dg50 * */ @@ -119,7 +125,7 @@ public class AutoTethysProvider implements TethysDataProvider { // algorithm.setMethod(this.getAlgorithmMethod()); // algorithm.setSoftware("PAMGuard"); // algorithm.setVersion(PamguardVersionInfo.version); - Parameters algoParameters = this.getAlgorithmParameters(); + nilus.AlgorithmType.Parameters algoParameters = this.getAlgorithmParameters(); if (algoParameters != null) { algorithm.setParameters(algoParameters); } @@ -128,12 +134,12 @@ public class AutoTethysProvider implements TethysDataProvider { } @Override - public Parameters getAlgorithmParameters() { + public nilus.AlgorithmType.Parameters getAlgorithmParameters() { if (pamControlledUnit instanceof PamSettings == false) { return null; } PamSettings pamSettings = (PamSettings) pamControlledUnit; - Parameters parameters = new Parameters(); + nilus.AlgorithmType.Parameters parameters = new nilus.AlgorithmType.Parameters(); List paramList = parameters.getAny(); Object settings = pamSettings.getSettingsReference(); TethysParameterPacker paramPacker = null; @@ -343,9 +349,38 @@ public class AutoTethysProvider implements TethysDataProvider { detParams.setReceivedLevelDB(ampli); // DataUnitBaseData basicData = dataUnit.getBasicData(); gotTonalContour(dataUnit, detParams); + + String uid = BigInteger.valueOf(dataUnit.getUID()).toString(); + Element el = addUserDefined(detParams,"PAMGuardUID", uid); + DataUnitFileInformation fileInf = dataUnit.getDataUnitFileInformation(); + if (fileInf != null) { + el.setAttribute("BinaryFile", fileInf.getShortFileName(2048)); + el.setAttribute("FileIndex", Long.valueOf(fileInf.getIndexInFile()).toString()); + } return detection; } + + private Element addUserDefined(Parameters parameters, String parameterName, String parameterValue) { + UserDefined userDefined = parameters.getUserDefined(); + if (userDefined == null) { + userDefined = new UserDefined(); + parameters.setUserDefined(userDefined); + } + Helper helper; + Element el = null; + try { + helper = new Helper(); + el = helper.AddAnyElement(userDefined.getAny(), parameterName, parameterValue); + } catch (JAXBException e) { + e.printStackTrace(); + return null; + } catch (ParserConfigurationException e) { + e.printStackTrace(); + return null; + } + return el; + } /** * Get tonal sounds contour. Sadly there are two slightly different interfaces in use diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index 1decb0fd..ca3014b3 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -16,6 +16,7 @@ import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.border.TitledBorder; import javax.swing.table.AbstractTableModel; +import javax.swing.table.JTableHeader; import PamView.PamGui; import PamView.dialog.warn.WarnOnce; @@ -26,7 +27,6 @@ import nilus.Detections; import nilus.GranularityType; import tethys.TethysControl; import tethys.TethysState; -import tethys.TethysState.StateType; import tethys.dbxml.TethysException; import tethys.detection.StreamDetectionsSummary; import tethys.niluswraps.PDeployment; @@ -61,7 +61,21 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa mainPanel.setBorder(new TitledBorder("Data stream Tethys Detections documents")); tableModel = new TableModel(); - table = new JTable(tableModel); + table = new JTable(tableModel) { + @Override + public String getToolTipText(MouseEvent event) { + return getToolTip(event); + } + + protected JTableHeader createDefaultTableHeader() { + return new JTableHeader(columnModel) { + public String getToolTipText(MouseEvent e) { + return getToolTip(e); + } + }; + } + + }; JScrollPane scrollPane = new JScrollPane(table); mainPanel.add(BorderLayout.CENTER, scrollPane); @@ -70,6 +84,29 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa table.addMouseListener(new MouseActions()); } + protected String getToolTip(MouseEvent event) { + java.awt.Point p = event.getPoint(); + int rowIndex = table.rowAtPoint(p); +// if (rowIndex < 0) { +// return null; +// } + int colIndex = table.columnAtPoint(p); + switch (colIndex) { + case 0: + return "Tethys Detections document name"; + case 1: + return "Name of PAMGuard data stream"; + case 2: + return "Output granularity"; + case 3: + return "Number of detection elements in document"; + case 4: + return "Document abstract"; + + } + return "No tip"; + } + @Override public JComponent getComponent() { return mainPanel; @@ -192,7 +229,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa private class TableModel extends AbstractTableModel { - private String[] colNames = {"Document", "Granularity", "Count", "Abstract"}; + private String[] colNames = {"Document", "Detector", "Granularity", "Count", "Abstract"}; @Override public int getRowCount() { @@ -230,6 +267,11 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa case 0: return dets.getId(); case 1: + if (pDets.dataBlock == null) { + return null; + } + return pDets.dataBlock.getDataName(); + case 2: List kinds = dets.getEffort().getKind(); if (kinds == null) { return null; @@ -244,9 +286,9 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } } break; - case 2: - return pDets.count; case 3: + return pDets.count; + case 4: return dets.getDescription().getAbstract(); } return null; diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java index 1c1131b5..0c05ea1f 100644 --- a/src/tethys/swing/DatablockSynchPanel.java +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -156,7 +156,7 @@ public class DatablockSynchPanel extends TethysGUIPanel { private class SynchTableModel extends AbstractTableModel { - String[] columnNames = {"Data Stream", "N PAM Datas", "PAMGuard Time", "N Tethys Datas", "Tethys Time", "Options"}; + String[] columnNames = {"Data Stream", "N PAM Datas", "PAMGuard Time", "Tethys Documents", "Tethys Time", "Options"}; @Override public int getRowCount() { @@ -200,7 +200,7 @@ public class DatablockSynchPanel extends TethysGUIPanel { long stop = synchInfo.getDataBlock().getPrimaryDataMap().getLastDataTime(); return String.format("%s - %s", PamCalendar.formatDBDateTime(start), PamCalendar.formatDBDateTime(stop)); case 3: - return synchInfo.getDataCount(); + return synchInfo.getDetectionDocumentCount(); } return null; } diff --git a/src/tethys/swing/DetectionsExportPanel.java b/src/tethys/swing/DetectionsExportPanel.java index eda59aaa..869cdc15 100644 --- a/src/tethys/swing/DetectionsExportPanel.java +++ b/src/tethys/swing/DetectionsExportPanel.java @@ -30,7 +30,7 @@ public class DetectionsExportPanel extends TethysGUIPanel implements StreamTable mainPanel = new PamAlignmentPanel(BorderLayout.NORTH); mainPanel.setLayout(new GridBagLayout()); mainPanel.setBorder(new TitledBorder("Export")); - exportButton = new JButton("Export"); + exportButton = new JButton("Export

>>>>"); exportButton.setToolTipText("Export PAMGaurd data to Tethys"); exportButton.addActionListener(new ActionListener() { @Override @@ -38,6 +38,7 @@ public class DetectionsExportPanel extends TethysGUIPanel implements StreamTable doExport(); } }); + exportButton.setToolTipText("Select a Data Block on the left to enable export"); exportButton.setEnabled(false); GridBagConstraints c = new PamGridBagContraints(); mainPanel.add(exportButton, c); diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index 251f6833..59c34d15 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -52,7 +52,7 @@ public class TethysMainPanel extends TethysGUIPanel { southwestSplit.add(datablockSynchPanel.getComponent()); southwestSplit.add(southEastPanel); southEastPanel.add(datablockDetectionsPanel.getComponent(), BorderLayout.CENTER); - southEastPanel.add(detectionsExportPanel.getComponent(), BorderLayout.EAST); + southEastPanel.add(detectionsExportPanel.getComponent(), BorderLayout.WEST); splitPane.add(southwestSplit); SwingUtilities.invokeLater(new Runnable() { // these only work if called after display is visible From 158eedce8c06d47719c49a97f6ea6d2f8446ce7c Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 15 Sep 2023 15:15:28 +0100 Subject: [PATCH 50/65] Detections output more work on detections output. --- src/PamguardMVC/DataAutomation.java | 29 ++++ src/PamguardMVC/DataAutomationInfo.java | 44 ++++++ src/PamguardMVC/PamDataBlock.java | 9 +- src/RightWhaleEdgeDetector/RWEDataBlock.java | 7 + src/tethys/dbxml/DBXMLQueries.java | 5 + .../detection/BinnedGranularityHandler.java | 146 ++++++++++-------- src/tethys/detection/DetectionsHandler.java | 14 +- .../EncounterGranularityHandler.java | 17 +- src/tethys/detection/GranularityHandler.java | 21 +++ src/tethys/output/StreamExportParams.java | 58 ++++++- .../output/swing/TethysExportDialog.java | 2 +- src/tethys/pamdata/AutoTethysProvider.java | 57 ++++++- src/tethys/pamdata/TethysDataProvider.java | 25 ++- .../swing/DatablockDetectionsPanel.java | 17 +- src/tethys/swing/DatablockSynchPanel.java | 2 +- .../swing/documents/TethysDocumentTable.java | 65 ++++++-- .../swing/export/DescriptionTypePanel.java | 22 +++ .../swing/export/DetectionsExportWizard.java | 2 +- src/tethys/swing/export/GranularityCard.java | 81 +++++++--- 19 files changed, 486 insertions(+), 137 deletions(-) create mode 100644 src/PamguardMVC/DataAutomation.java create mode 100644 src/PamguardMVC/DataAutomationInfo.java diff --git a/src/PamguardMVC/DataAutomation.java b/src/PamguardMVC/DataAutomation.java new file mode 100644 index 00000000..cd424c60 --- /dev/null +++ b/src/PamguardMVC/DataAutomation.java @@ -0,0 +1,29 @@ +package PamguardMVC; + +/** + * @author dg50 + * Levels of automation for the various datas in PAMGuard. + * Should be used within DataAutomationInfo to perhaps combine with other info in the future. + * + */ +public enum DataAutomation { + + AUTOMATIC, MANUAL, MANUALANDAUTOMATIC; + + @Override + public String toString() { + switch (this) { + case AUTOMATIC: + return "Automatic"; + case MANUAL: + return "Manual"; + case MANUALANDAUTOMATIC: + return "Manual and automatic"; + default: + break; + + } + return null; + } + +} diff --git a/src/PamguardMVC/DataAutomationInfo.java b/src/PamguardMVC/DataAutomationInfo.java new file mode 100644 index 00000000..42c7e42d --- /dev/null +++ b/src/PamguardMVC/DataAutomationInfo.java @@ -0,0 +1,44 @@ +package PamguardMVC; + +/** + * Returned by datablocks, though default is null, to give information on how + * automatic the process was. + * @author dg50 + * + */ +public class DataAutomationInfo { + + + private DataAutomation automation; + + /** + * @param automation + */ + public DataAutomationInfo(DataAutomation automation) { + this.setAutomation(automation); + } + + /** + * @return the automation + */ + public DataAutomation getAutomation() { + return automation; + } + + /** + * @param automation the automation to set + */ + public void setAutomation(DataAutomation automation) { + this.automation = automation; + } + + @Override + public String toString() { + if (automation == null) { + return "Unknown data automation"; + } + return automation.toString(); + } + + +} diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index 7779ea38..8c259d67 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -3100,11 +3100,12 @@ public class PamDataBlock extends PamObservable { } /** - * Set a data provider for Tethys. - * @param tethysDataProvider the tethysDataProvider to set + * Get the level of automation employed by the generation of these data. + * Should ideally be completed for everything providing data to Tethys. + * @return level of automation for this data block. */ - public void setTethysDataProvider(TethysDataProvider tethysDataProvider) { - this.tethysDataProvider = tethysDataProvider; + public DataAutomationInfo getDataAutomationInfo() { + return null; } /** diff --git a/src/RightWhaleEdgeDetector/RWEDataBlock.java b/src/RightWhaleEdgeDetector/RWEDataBlock.java index 4ddfd65b..90c9a616 100644 --- a/src/RightWhaleEdgeDetector/RWEDataBlock.java +++ b/src/RightWhaleEdgeDetector/RWEDataBlock.java @@ -2,6 +2,8 @@ package RightWhaleEdgeDetector; import PamView.GroupedDataSource; import PamView.GroupedSourceParameters; +import PamguardMVC.DataAutomation; +import PamguardMVC.DataAutomationInfo; import PamguardMVC.PamProcess; import PamguardMVC.dataOffline.OfflineDataLoadInfo; import PamguardMVC.dataSelector.DataSelectorCreator; @@ -77,4 +79,9 @@ public class RWEDataBlock extends AbstractWhistleDataBlock implemen return rwTethysDataProvider; } + @Override + public DataAutomationInfo getDataAutomationInfo() { + return new DataAutomationInfo(DataAutomation.AUTOMATIC); + } + } diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index ecda02e6..48dd2b7b 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -846,6 +846,11 @@ public class DBXMLQueries { description.setMethod(getElementData(result, "Description.Method")); description.setObjectives(getElementData(result, "Description.Objectives")); + // get the effort start an end + String effStart = getElementData(result, "Effort.Start"); + String effEnd = getElementData(result, "Effort.End"); + detections.getEffort().setStart(TethysTimeFuncs.fromGregorianXML(effStart)); + detections.getEffort().setEnd(TethysTimeFuncs.fromGregorianXML(effEnd)); // try to find the granularity. String granularityString = getElementData(result, "Effort.Kind.Granularity"); GranularityEnumType granularity = null; diff --git a/src/tethys/detection/BinnedGranularityHandler.java b/src/tethys/detection/BinnedGranularityHandler.java index 056fc092..db984b27 100644 --- a/src/tethys/detection/BinnedGranularityHandler.java +++ b/src/tethys/detection/BinnedGranularityHandler.java @@ -4,6 +4,8 @@ import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; import java.util.Set; import PamguardMVC.PamDataBlock; @@ -29,10 +31,8 @@ import tethys.species.SpeciesMapItem; */ public class BinnedGranularityHandler extends GranularityHandler { - private double binDurationSeconds; + private long binDurationMillis; - private long binStartMillis, binEndMillis; - private TethysDataProvider dataProvider; private DataBlockSpeciesManager speciesManager; @@ -43,7 +43,7 @@ public class BinnedGranularityHandler extends GranularityHandler { TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { super(tethysControl, dataBlock, tethysExportParams, streamExportParams); - binDurationSeconds = streamExportParams.binDurationS; + binDurationMillis = (long) (streamExportParams.binDurationS*1000.); dataProvider = dataBlock.getTethysDataProvider(tethysControl); speciesManager = dataBlock.getDatablockSpeciesManager(); @@ -52,61 +52,74 @@ public class BinnedGranularityHandler extends GranularityHandler { @Override public void prepare(long timeMillis) { - long binStart = DetectionsHandler.roundDownBinStart(timeMillis, (long) (binDurationSeconds*1000)); - startBin(binStart); +// long binStart = DetectionsHandler.roundDownBinStart(timeMillis, binDurationMillis); +// startBin(binStart); } - private void startBin(long timeMillis) { - binStartMillis = timeMillis; - binEndMillis = binStartMillis + (long) (binDurationSeconds*1000.); - /* - * now make a Detection object for every possible species that - * this might throw out. - */ - ArrayList speciesCodes = speciesManager.getAllSpeciesCodes(); - String defaultCode = speciesManager.getDefaultSpeciesCode(); - Detection det; - currentDetections.put(defaultCode, det = new Detection()); - det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); - det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); - det.setCount(BigInteger.ZERO); - det.setChannel(BigInteger.ZERO); - // add codes at end, just before output. - if (speciesCodes != null) { - for (String code : speciesCodes) { - currentDetections.put(code, det = new Detection()); - det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); - det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); - det.setCount(BigInteger.ZERO); - det.setChannel(BigInteger.ZERO); - } - } - } +// private void startBin(long timeMillis) { +// binStartMillis = timeMillis; +// binEndMillis = binStartMillis + binDurationMillis; +// /* +// * now make a Detection object for every possible species that +// * this might throw out. +// */ +// ArrayList speciesCodes = speciesManager.getAllSpeciesCodes(); +// String defaultCode = speciesManager.getDefaultSpeciesCode(); +// Detection det; +// currentDetections.put(defaultCode, det = new Detection()); +// det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); +// det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); +// det.setCount(BigInteger.ZERO); +// det.setChannel(BigInteger.ZERO); +// // add codes at end, just before output. +// if (speciesCodes != null) { +// for (String code : speciesCodes) { +// currentDetections.put(code, det = new Detection()); +// det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); +// det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); +// det.setCount(BigInteger.ZERO); +// det.setChannel(BigInteger.ZERO); +// } +// } +// } @Override public Detection[] addDataUnit(PamDataUnit dataUnit) { - Detection[] detections = null; - if (dataUnit.getTimeMilliseconds() >= binEndMillis) { - detections = closeBins(dataUnit.getTimeMilliseconds()); - prepare(dataUnit.getTimeMilliseconds()); + Detection[] completeDetections = closeBins(dataUnit.getTimeMilliseconds()); + // now look for new ones. First get the species of the dataUnit and find it in the hashmap + String groupName = getCallGroupName(dataUnit); + Detection det = currentDetections.get(groupName); + if (det == null) { + // need to make a new one. + det = new Detection(); + long binStart = DetectionsHandler.roundDownBinStart(dataUnit.getTimeMilliseconds(), binDurationMillis); + det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStart)); + det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binStart + binDurationMillis)); + det.setCount(BigInteger.ONE); + det.setChannel(BigInteger.valueOf(dataUnit.getChannelBitmap())); + // this should always return something, so am going to crash if it doesn't. + // may revisit this later on if we've unassigned things we don't want to label + // in which case they should be rejected earlier than this. + SpeciesMapItem speciesStuff = speciesManager.getSpeciesItem(dataUnit); + SpeciesIDType species = new SpeciesIDType(); + species.setValue(BigInteger.valueOf(speciesStuff.getItisCode())); + det.setSpeciesId(species); + if (speciesStuff.getCallType() != null) { + det.getCall().add(speciesStuff.getCallType()); + } + currentDetections.put(groupName, det); } - String speciesCode = speciesManager.getSpeciesCode(dataUnit); - Detection det = currentDetections.get(speciesCode); - if (det != null) { - /* - * Increase the detection count - */ - int count = det.getCount().intValue(); - count++; + else { + // add to current detection. Set new end time and increment count + int count = det.getCount().intValue() + 1; det.setCount(BigInteger.valueOf(count)); - /* - * Add to the channel map too ... - */ - int channel = det.getChannel().intValue(); - channel |= dataUnit.getChannelBitmap(); - det.setChannel(BigInteger.valueOf(channel)); + int chan = det.getChannel().intValue(); + chan |= dataUnit.getChannelBitmap(); + det.setChannel(BigInteger.valueOf(chan)); } - return detections; + + + return completeDetections; } /** @@ -115,33 +128,32 @@ public class BinnedGranularityHandler extends GranularityHandler { * @param timeMilliseconds * @return */ - private Detection[] closeBins(long timeMilliseconds) { + private synchronized Detection[] closeBins(long timeMilliseconds) { Set speciesKeys = currentDetections.keySet(); int n = speciesKeys.size(); int nGood = 0; DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); Detection detections[] = new Detection[n]; - for (String key : speciesKeys) { - Detection det = currentDetections.get(key); + Iterator> iter = currentDetections.entrySet().iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + Detection det = entry.getValue(); + long detEnd = TethysTimeFuncs.millisFromGregorianXML(det.getEnd()); + if (timeMilliseconds < detEnd) { + // we're not at the end of the bin, so carry on. + continue; + } + // we've reached the end of the bin, so remove it from the map + iter.remove(); + // now decide if we want to keep it or not. int callCount = det.getCount().intValue(); if (callCount < Math.max(streamExportParams.minBinCount,1)) { - continue; - } - SpeciesMapItem speciesStuff = speciesMap.getItem(key); // should be non null! - if (speciesStuff == null) { - continue; - } - SpeciesIDType species = new SpeciesIDType(); - species.setValue(BigInteger.valueOf(speciesStuff.getItisCode())); - det.setSpeciesId(species); - if (speciesStuff.getCallType() != null) { - det.getCall().add(speciesStuff.getCallType()); + continue; // won't add to output list } + detections[nGood++] = det; } - - /* * Clean up the end of the array and return detections that have enough calls. */ diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 1770ef85..6f2246aa 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -349,9 +349,9 @@ public class DetectionsHandler { /** * Round a bin start so that it's aligned correctly with * day starts. - * @param binStart - * @param binInterval - * @return + * @param binStart in milliseconds + * @param binInterval in milliseconds + * @return rounded time. */ public static long roundDownBinStart(long binStart, long binInterval) { binStart/=binInterval; @@ -441,6 +441,10 @@ public class DetectionsHandler { // currentDetections = null; // } } + Detection dets[] = granularityHandler.cleanup(deployment.getAudioEnd()); + if (dets != null) { + exportCount += dets.length; + } @@ -578,8 +582,9 @@ public class DetectionsHandler { e.printStackTrace(); return null; } + TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl); - String prefix = deployment.deployment.getId(); + String prefix = deployment.deployment.getId() + "_" + dataProvider.getDetectionsName(); String fullId = ""; /* * Check the document name isn't already used and increment id as necessary. @@ -599,7 +604,6 @@ public class DetectionsHandler { detections.setDataSource(dataSource); AlgorithmType algorithm = detections.getAlgorithm(); - TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl); if (dataProvider != null) { algorithm = dataProvider.getAlgorithm(); // detections.setAlgorithm(algorithm); diff --git a/src/tethys/detection/EncounterGranularityHandler.java b/src/tethys/detection/EncounterGranularityHandler.java index c1cea4de..57dd8757 100644 --- a/src/tethys/detection/EncounterGranularityHandler.java +++ b/src/tethys/detection/EncounterGranularityHandler.java @@ -6,6 +6,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Set; +import java.util.Map.Entry; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -54,8 +55,8 @@ public class EncounterGranularityHandler extends GranularityHandler { public Detection[] addDataUnit(PamDataUnit dataUnit) { Detection[] completeDetections = checkCurrentEncounters(dataUnit.getTimeMilliseconds()); // now look for new ones. First get the species of the dataUnit and find it in the hashmap - String speciesCode = speciesManager.getSpeciesCode(dataUnit); - Detection det = currentDetections.get(speciesCode); + String groupName = getCallGroupName(dataUnit); + Detection det = currentDetections.get(groupName); if (det == null) { // need to make a new one. det = new Detection(); @@ -73,7 +74,7 @@ public class EncounterGranularityHandler extends GranularityHandler { if (speciesStuff.getCallType() != null) { det.getCall().add(speciesStuff.getCallType()); } - currentDetections.put(speciesCode, det); + currentDetections.put(groupName, det); } else { // add to current detection. Set new end time and increment count @@ -98,16 +99,18 @@ public class EncounterGranularityHandler extends GranularityHandler { Set keys = currentDetections.keySet(); int nGood = 0; Detection[] newDetections = new Detection[currentDetections.size()]; - for (String aKey : keys) { - Detection aDet = currentDetections.get(aKey); - Long detEnd = TethysTimeFuncs.millisFromGregorianXML(aDet.getEnd()); + Iterator> iter = currentDetections.entrySet().iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + Detection aDet = entry.getValue(); + long detEnd = TethysTimeFuncs.millisFromGregorianXML(aDet.getEnd()); if (timeMilliseconds-detEnd > maxGapMillis) { // only keep if it's got a min number of calls. if (aDet.getCount().intValue() >= streamExportParams.minBinCount) { newDetections[nGood++] = aDet; } // remove from set. A new one will be created only when required. - currentDetections.remove(aKey); + iter.remove(); } } diff --git a/src/tethys/detection/GranularityHandler.java b/src/tethys/detection/GranularityHandler.java index b2ef8803..d172405e 100644 --- a/src/tethys/detection/GranularityHandler.java +++ b/src/tethys/detection/GranularityHandler.java @@ -7,6 +7,7 @@ import nilus.GranularityEnumType; import tethys.TethysControl; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; +import tethys.species.DataBlockSpeciesManager; public abstract class GranularityHandler { @@ -18,6 +19,8 @@ public abstract class GranularityHandler { protected StreamExportParams streamExportParams; + private DataBlockSpeciesManager speciesManager; + /** * @param tethysControl * @param dataBlock @@ -30,6 +33,7 @@ public abstract class GranularityHandler { this.dataBlock = dataBlock; this.tethysExportParams = tethysExportParams; this.streamExportParams = streamExportParams; + speciesManager = dataBlock.getDatablockSpeciesManager(); } /** @@ -48,6 +52,23 @@ public abstract class GranularityHandler { */ public abstract Detection[] addDataUnit(PamDataUnit dataUnit); + /** + * Get a grouping name for the call. This may just be the calls species code, + * or it may be appended with the channel number. This is used to find bin and + * encounter data in HashMaps in + * @param dataUnit + * @return + */ + public String getCallGroupName(PamDataUnit dataUnit) { + String groupName = speciesManager.getSpeciesCode(dataUnit); + if (groupName == null) { + groupName = "NullSpecies"; + } + if (streamExportParams.separateChannels) { + groupName += String.format("Chan%d", dataUnit.getChannelBitmap()); + } + return groupName; + } /** * Called after end end of all data units to get the last bin / encounter.

* diff --git a/src/tethys/output/StreamExportParams.java b/src/tethys/output/StreamExportParams.java index 27e2c2a6..9c9de08d 100644 --- a/src/tethys/output/StreamExportParams.java +++ b/src/tethys/output/StreamExportParams.java @@ -3,15 +3,20 @@ package tethys.output; import java.io.IOException; import java.io.Serializable; +import PamController.PamController; +import PamguardMVC.PamDataBlock; import nilus.DescriptionType; import nilus.GranularityEnumType; +import tethys.TethysControl; import tethys.niluswraps.PDescriptionType; +import tethys.pamdata.TethysDataProvider; /** * Parameters controlling export of a single stream. * Starts just with a boolean 'selected', but may grow. * These all contain data names rather than references to a Datablock so that - * they can be serialised. + * they can be serialised. However, created with TethysControl and datablock + * so that some stuff canbe automatically initialised. * @author dg50 * */ @@ -19,24 +24,44 @@ public class StreamExportParams implements Serializable { public static final long serialVersionUID = 1L; - public StreamExportParams(String longDataName, boolean selected) { - super(); - this.longDataName = longDataName; - this.selected = selected; - } - + /** + * Datablock long data name (used instead of datablock + * reference so this object and serialise. + */ public String longDataName; public boolean selected; + /** + * Granularity type, binned, call, encounter, grouped. + */ public GranularityEnumType granularity = GranularityEnumType.CALL; + /** + * Bin duration, seconds. + */ public double binDurationS = 60; + /** + * Minimum encounter gap, seconds + */ public double encounterGapS = 60; + /** + * Minimum count for a bin to be retained. + */ public int minBinCount = 1; + /** + * Minimum count for an encounter to be retained. + */ + public int minEncounterCount = 1; + + /** + * Keep channels separate when using binned data. + */ + public boolean separateChannels = true; + /* * Can't have this here since it isn't serializable. */ @@ -48,6 +73,25 @@ public class StreamExportParams implements Serializable { } return detectionDescription; } + + public StreamExportParams(TethysControl tethysControl, PamDataBlock dataBlock, boolean selected) { + super(); + this.longDataName = dataBlock.getLongDataName(); + this.selected = selected; + autoFill(tethysControl, dataBlock); + } + + /** + * Try to put some information automatically into the Methods. + * @param dataBlock2 + * @param tethysControl + */ + private void autoFill(TethysControl tethysControl, PamDataBlock dataBlock) { + // there should always be a data provider or we'd never have got this far. + TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl); + PDescriptionType desc = getDetectionDescription(); + desc.setMethod(dataProvider.getDetectionsMethod()); + } /** * Get the nilus detection description diff --git a/src/tethys/output/swing/TethysExportDialog.java b/src/tethys/output/swing/TethysExportDialog.java index 46a6c690..da63db50 100644 --- a/src/tethys/output/swing/TethysExportDialog.java +++ b/src/tethys/output/swing/TethysExportDialog.java @@ -151,7 +151,7 @@ public class TethysExportDialog extends PamDialog { } int nSel = 0; for (DataStreamSet streamSet : dataStreamSets) { - StreamExportParams streamOpts = new StreamExportParams(streamSet.dataBlock.getLongDataName(), streamSet.checkBox.isSelected()); + StreamExportParams streamOpts = new StreamExportParams(tethysControl, streamSet.dataBlock, streamSet.checkBox.isSelected()); exportParams.setStreamParams(streamSet.dataBlock, streamOpts); nSel++; } diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index daef05c0..22d971e4 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -14,6 +14,7 @@ import PamController.PamSettings; import PamController.PamguardVersionInfo; import PamController.settings.output.xml.PamguardXMLWriter; import PamUtils.XMLUtils; +import PamguardMVC.DataAutomationInfo; import PamguardMVC.DataUnitBaseData; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -30,6 +31,7 @@ import nilus.Detection; import nilus.Detection.Parameters; import nilus.Detection.Parameters.UserDefined; import nilus.DetectionEffortKind; +import nilus.GranularityEnumType; import nilus.Helper; import nilus.SpeciesIDType; import tethys.TethysControl; @@ -90,11 +92,11 @@ public class AutoTethysProvider implements TethysDataProvider { return schema; } - @Override - public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) { - // TODO Auto-generated method stub - return null; - } +// @Override +// public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) { +// // TODO Auto-generated method stub +// return null; +// } @Override public DescriptionType getDescription(Deployment deployment, TethysExportParams tethysExportParams) { @@ -488,4 +490,49 @@ public class AutoTethysProvider implements TethysDataProvider { } + @Override + public String getDetectionsMethod() { + /* + * could really do with knowing what type of detector we're dealing with, i.e. if it's + * automatic or manual. For most blocks this is fixed, though some may have a mixture of both ! + */ + DataAutomationInfo dataAutomation = pamDataBlock.getDataAutomationInfo(); + String method; + PamControlledUnit pcu = pamDataBlock.getParentProcess().getPamControlledUnit(); + if (dataAutomation == null) { + method = String.format("Processing using the PAMGuard %s", pcu.getUnitType()); + } + else { + method = String.format("%s processing using the PAMGuard %s", dataAutomation.getAutomation(), pcu.getUnitType()); + } + + return method; + } + + @Override + public GranularityEnumType[] getAllowedGranularities() { + GranularityEnumType[] allowed = {GranularityEnumType.CALL, GranularityEnumType.BINNED, GranularityEnumType.ENCOUNTER}; + return allowed; + } + + @Override + public String getDetectionsName() { + PamProcess process = pamDataBlock.getParentProcess(); + PamControlledUnit pcu = process.getPamControlledUnit(); + String pcuName = pcu.getUnitName(); + String blockName = pamDataBlock.getDataName(); + String documentName; + /** + * If the datablock name is the same as the unit name, no need to repeat onesself. + */ + if (pcuName.equals(blockName)) { + documentName = new String(pcuName); // copy it, since we're about to modify it! + } + else { + documentName = pcuName + " " + blockName; + } + documentName = documentName.replace(' ', '_'); + return documentName; + } + } diff --git a/src/tethys/pamdata/TethysDataProvider.java b/src/tethys/pamdata/TethysDataProvider.java index adb5cbbc..d040075f 100644 --- a/src/tethys/pamdata/TethysDataProvider.java +++ b/src/tethys/pamdata/TethysDataProvider.java @@ -9,6 +9,7 @@ import nilus.Deployment; import nilus.DescriptionType; import nilus.Detection; import nilus.DetectionEffortKind; +import nilus.GranularityEnumType; import tethys.niluswraps.PDeployment; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; @@ -38,8 +39,14 @@ public interface TethysDataProvider { * @param pamDataUnit * @return */ - public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit); +// public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit); + /** + * Get a standard Method string for each detector. This can be a bit + * verbose and might even have a reference to a paper ? Is this the best place for this ? + * @return + */ + public String getDetectionsMethod(); /** * Get DescriptionType object to include in a Tethys Detections document. @@ -55,6 +62,22 @@ public interface TethysDataProvider { * @return Algorithm information */ public AlgorithmType getAlgorithm(); + + /** + * Get a list of allowed granularity types for this output + * @return list of granularities. + */ + public GranularityEnumType[] getAllowedGranularities(); + + /** + * Get a name for the detections documents. This will be appended + * to the Deployment name and may also have a number after it.
+ * Note that the name isn't really important since all the matching between + * different documents is done internally, but it helps to make everything + * human readable. + * @return A name, similar to datablock.getLongDataName(), but no spaces. + */ + public String getDetectionsName(); /** diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index ca3014b3..36d2b6ee 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -17,6 +17,7 @@ import javax.swing.JTable; import javax.swing.border.TitledBorder; import javax.swing.table.AbstractTableModel; import javax.swing.table.JTableHeader; +import javax.xml.datatype.XMLGregorianCalendar; import PamView.PamGui; import PamView.dialog.warn.WarnOnce; @@ -97,10 +98,12 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa case 1: return "Name of PAMGuard data stream"; case 2: - return "Output granularity"; + return "Effort period"; case 3: - return "Number of detection elements in document"; + return "Output granularity"; case 4: + return "Number of detection elements in document"; + case 5: return "Document abstract"; } @@ -229,7 +232,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa private class TableModel extends AbstractTableModel { - private String[] colNames = {"Document", "Detector", "Granularity", "Count", "Abstract"}; + private String[] colNames = {"Document", "Detector", "Effort", "Granularity", "Count", "Abstract"}; @Override public int getRowCount() { @@ -272,6 +275,10 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } return pDets.dataBlock.getDataName(); case 2: + XMLGregorianCalendar start = dets.getEffort().getStart(); + XMLGregorianCalendar stop = dets.getEffort().getEnd(); + return start + " to " + stop; + case 3: List kinds = dets.getEffort().getKind(); if (kinds == null) { return null; @@ -286,9 +293,9 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } } break; - case 3: - return pDets.count; case 4: + return pDets.count; + case 5: return dets.getDescription().getAbstract(); } return null; diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java index 0c05ea1f..60281b76 100644 --- a/src/tethys/swing/DatablockSynchPanel.java +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -156,7 +156,7 @@ public class DatablockSynchPanel extends TethysGUIPanel { private class SynchTableModel extends AbstractTableModel { - String[] columnNames = {"Data Stream", "N PAM Datas", "PAMGuard Time", "Tethys Documents", "Tethys Time", "Options"}; + String[] columnNames = {"Data Stream", "N PAM Datas", "PAMGuard Time", "Tethys Documents"};//, "Tethys Time", "Options"}; @Override public int getRowCount() { diff --git a/src/tethys/swing/documents/TethysDocumentTable.java b/src/tethys/swing/documents/TethysDocumentTable.java index dcdb6a0a..96cf8ef5 100644 --- a/src/tethys/swing/documents/TethysDocumentTable.java +++ b/src/tethys/swing/documents/TethysDocumentTable.java @@ -14,6 +14,7 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JTable; +import javax.swing.ListSelectionModel; import javax.swing.table.AbstractTableModel; import PamController.PamController; @@ -57,7 +58,9 @@ public class TethysDocumentTable implements PamDialogPanel { mainPanel.add(BorderLayout.CENTER, scrollPane); new SwingTableColumnWidths(tethysControl.getUnitName()+"TethysDocumentsTable", mainTable); this.setCollectionName(collectionName); - mainTable.addMouseListener(new TableMouse()); + mainTable.addMouseListener(new TableMouse()); + mainTable.setRowSelectionAllowed(true); + mainTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); } public void updateTableData() { @@ -94,6 +97,7 @@ public class TethysDocumentTable implements PamDialogPanel { if (row < 0|| row >= documentNames.size()) { return; } + String docName = documentNames.get(row); JPopupMenu popMenu = new JPopupMenu(); JMenuItem menuItem = new JMenuItem("Show document " + docName); @@ -104,14 +108,31 @@ public class TethysDocumentTable implements PamDialogPanel { } }); popMenu.add(menuItem); - menuItem = new JMenuItem("Delete document " + docName); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - deleteDocument(docName); - } - }); - popMenu.add(menuItem); + + + int[] rows = mainTable.getSelectedRows(); + if (rows != null && rows.length == 1) { +// docName = documentNames.get(rows[0]); + menuItem = new JMenuItem("Delete document " + docName); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteDocument(docName); + } + }); + popMenu.add(menuItem); + } + else if (rows != null && rows.length > 1) { + String mt = String.format("Delete multiple (%d) documents", rows.length); + menuItem = new JMenuItem(mt); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteDocuments(rows); + } + }); + popMenu.add(menuItem); + } popMenu.show(e.getComponent(), e.getX(), e.getY()); } @@ -134,6 +155,32 @@ public class TethysDocumentTable implements PamDialogPanel { updateTableData(); } + private void deleteDocuments(int[] rows) { + int ans = WarnOnce.showNamedWarning("deletedoc"+collectionName, PamController.getMainFrame(), "Delete documents", + "Are you sure you want to delete multiple documents ", WarnOnce.OK_CANCEL_OPTION); + if (ans != WarnOnce.OK_OPTION) { + return; + } + /* + * make a new list before anything is deleted since the + * man list will get updated during deletion and be out of date. + */ + String[] docNames = new String[rows.length]; + for (int i = 0; i < rows.length; i++) { + docNames[i] = documentNames.get(rows[i]); + } + // now it's safe to delete them. + for (int i = 0; i < docNames.length; i++) { + try { + tethysControl.getDbxmlConnect().removeDocument(collectionName, docNames[i]); + } catch (TethysException e) { + System.out.println("Failed to delete " + docNames[i]); + System.out.println(e.getMessage()); + } + } + updateTableData(); + } + private class TableModel extends AbstractTableModel { private String[] columnNames = {"", "Document Id/Name"}; diff --git a/src/tethys/swing/export/DescriptionTypePanel.java b/src/tethys/swing/export/DescriptionTypePanel.java index 362103ff..c17549a2 100644 --- a/src/tethys/swing/export/DescriptionTypePanel.java +++ b/src/tethys/swing/export/DescriptionTypePanel.java @@ -39,6 +39,13 @@ public class DescriptionTypePanel { private static final int ctrlWidth = 40; + public static final String objectivesTip = "What are the objectives of this effort? Examples:\r\n" + + "Beamform to increase SNR for detection.\r\n" + + "Detect every click of a rare species.\r\n" + + "Verify data quality."; + public static final String abstractTip = "Overview of effort."; + public static final String methodTip = "High-level description of the method used."; + public DescriptionTypePanel(String bordertitle, boolean requireObjective, boolean requireAbstract, boolean requireMethod) { this.requireObjective = requireObjective; this.requireAbstract = requireAbstract; @@ -51,6 +58,16 @@ public class DescriptionTypePanel { tObjectives = new JTextArea(12, ctrlWidth); tAbstract = new JTextArea(8, ctrlWidth); tMethod = new JTextArea(9, ctrlWidth); + tObjectives.setLineWrap(true); + tObjectives.setWrapStyleWord(true); + tAbstract.setLineWrap(true); + tAbstract.setWrapStyleWord(true); + tMethod.setLineWrap(true); + tMethod.setWrapStyleWord(true); + + tObjectives.setToolTipText(objectivesTip); + tAbstract.setToolTipText(abstractTip); + tMethod.setToolTipText(methodTip); mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); addScrollablePanel(tObjectives, "Objectives"); @@ -78,6 +95,11 @@ public class DescriptionTypePanel { tAbstract.setText(null); tMethod.setText(null); } + else { + tObjectives.setText(description.getObjectives()); + tAbstract.setText(description.getAbstract()); + tMethod.setText(description.getMethod()); + } } public boolean getParams(PDescriptionType description) { diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index 94ece212..f476a7ee 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -39,7 +39,7 @@ public class DetectionsExportWizard extends PamDialog { streamExportParams = tethysControl.getTethysExportParams().getStreamParams(dataBlock); if (streamExportParams == null) { - streamExportParams = new StreamExportParams(dataBlock.getLongDataName(), false); + streamExportParams = new StreamExportParams(tethysControl, dataBlock, false); } cardLayout = new CardLayout(); diff --git a/src/tethys/swing/export/GranularityCard.java b/src/tethys/swing/export/GranularityCard.java index a8fa539e..988ca513 100644 --- a/src/tethys/swing/export/GranularityCard.java +++ b/src/tethys/swing/export/GranularityCard.java @@ -30,6 +30,7 @@ import nilus.GranularityEnumType; import tethys.TethysControl; import tethys.niluswraps.PGranularityType; import tethys.output.StreamExportParams; +import tethys.pamdata.TethysDataProvider; public class GranularityCard extends ExportWizardCard { @@ -37,7 +38,9 @@ public class GranularityCard extends ExportWizardCard { private JTextArea dataSelectionText; - private JTextField binLength, minCalls, encounterGap; + private JTextField binLength, minBinnedCalls, encounterGap, minEncounterCalls; + + private JRadioButton groupChannels, separateChannels; private DataSelector dataSelector; @@ -45,51 +48,68 @@ public class GranularityCard extends ExportWizardCard { private int encounterIndex, binnedIndex; + private GranularityEnumType[] allowedGranularities; + public GranularityCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { super(tethysControl, "Granularity", dataBlock); this.detectionsExportWizard = detectionsExportWizard; setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + TethysDataProvider tethysDataProvider = dataBlock.getTethysDataProvider(tethysControl); // granularity - GranularityEnumType[] grans = GranularityEnumType.values(); - granularities = new JRadioButton[grans.length]; + allowedGranularities = tethysDataProvider.getAllowedGranularities(); + granularities = new JRadioButton[allowedGranularities.length]; JPanel granPanel = new WestAlignedPanel(new GridBagLayout()); GridBagConstraints c = new PamGridBagContraints(); granPanel.setBorder(new TitledBorder("Granularity")); ButtonGroup granGroup = new ButtonGroup(); GranularityChange gc = new GranularityChange(); - for (int i = 0; i < grans.length; i++) { + for (int i = 0; i < allowedGranularities.length; i++) { c.gridx = 0; - granularities[i] = new JRadioButton(PGranularityType.prettyString(grans[i])); - granularities[i].setToolTipText(PGranularityType.toolTip(grans[i])); + granularities[i] = new JRadioButton(PGranularityType.prettyString(allowedGranularities[i])); + granularities[i].setToolTipText(PGranularityType.toolTip(allowedGranularities[i])); granularities[i].addActionListener(gc); granPanel.add(granularities[i], c); granGroup.add(granularities[i]); - if (grans[i] == GranularityEnumType.BINNED) { + if (allowedGranularities[i] == GranularityEnumType.BINNED) { binnedIndex = i; c.gridx++; - granPanel.add(new JLabel(" bin duration ", JLabel.RIGHT), c); + granPanel.add(new JLabel(" Bin duration ", JLabel.RIGHT), c); c.gridx++; granPanel.add(binLength = new JTextField(5), c); c.gridx++; - granPanel.add(new JLabel("(s), min Calls", JLabel.LEFT), c); + granPanel.add(new JLabel("(s), Min Calls", JLabel.LEFT), c); c.gridx++; - granPanel.add(minCalls = new JTextField(5), c); + granPanel.add(minBinnedCalls = new JTextField(5), c); binLength.setToolTipText("Time bin duration in seconds"); - minCalls.setToolTipText("Minimum number of calls for a bin to be output"); + minBinnedCalls.setToolTipText("Minimum number of calls for a bin to be output"); } - if (grans[i] == GranularityEnumType.ENCOUNTER) { + if (allowedGranularities[i] == GranularityEnumType.ENCOUNTER) { encounterIndex = i; c.gridx++; - granPanel.add(new JLabel(" min gap ", JLabel.RIGHT), c); + granPanel.add(new JLabel(" Minimum gap ", JLabel.RIGHT), c); c.gridx++; granPanel.add(encounterGap = new JTextField(5), c); c.gridx++; - granPanel.add(new JLabel("(s) ", JLabel.LEFT), c); + granPanel.add(new JLabel("(s), Min Calls", JLabel.LEFT), c); + c.gridx++; + granPanel.add(minEncounterCalls = new JTextField(5), c); encounterGap.setToolTipText("Minimum gap between separate encounters"); + minEncounterCalls.setToolTipText("Minimum number of calls for an encounter to be output"); } c.gridy++; } + c.gridx = 1; + c.gridwidth = 2; + granPanel.add(separateChannels = new JRadioButton("Separate channels"), c); + c.gridx += c.gridwidth; + granPanel.add(groupChannels = new JRadioButton("Group channels"), c); + separateChannels.setToolTipText("Use separate bins/encounters for each detection channel"); + groupChannels.setToolTipText("Combine detections from different channels into the same bins/encounters"); + ButtonGroup chanGroup = new ButtonGroup(); + chanGroup.add(separateChannels); + chanGroup.add(groupChannels); + this.add(granPanel); // data selection @@ -127,8 +147,12 @@ public class GranularityCard extends ExportWizardCard { private void enableControls() { binLength.setEnabled(granularities[binnedIndex].isSelected()); - minCalls.setEnabled(granularities[binnedIndex].isSelected()); + minBinnedCalls.setEnabled(granularities[binnedIndex].isSelected()); encounterGap.setEnabled(granularities[encounterIndex].isSelected()); + minEncounterCalls.setEnabled(granularities[encounterIndex].isSelected()); + boolean binOrencount = granularities[binnedIndex].isSelected() | granularities[encounterIndex].isSelected(); + separateChannels.setEnabled(binOrencount); + groupChannels.setEnabled(binOrencount); } protected void newDataSelection() { @@ -149,10 +173,9 @@ public class GranularityCard extends ExportWizardCard { @Override public boolean getParams(StreamExportParams streamExportParams) { - GranularityEnumType[] grans = GranularityEnumType.values(); - for (int i = 0; i < grans.length; i++) { + for (int i = 0; i < allowedGranularities.length; i++) { if (granularities[i].isSelected()) { - streamExportParams.granularity = grans[i]; + streamExportParams.granularity = allowedGranularities[i]; break; } } @@ -164,10 +187,10 @@ public class GranularityCard extends ExportWizardCard { return detectionsExportWizard.showWarning("Invalid bin duration parameter"); } try { - streamExportParams.minBinCount = Integer.valueOf(minCalls.getText()); + streamExportParams.minBinCount = Integer.valueOf(minBinnedCalls.getText()); } catch (NumberFormatException e) { - return detectionsExportWizard.showWarning("Invalid minimum call count"); + return detectionsExportWizard.showWarning("Invalid minimum binned call count"); } } if (streamExportParams.granularity == GranularityEnumType.ENCOUNTER) { @@ -177,20 +200,30 @@ public class GranularityCard extends ExportWizardCard { catch (NumberFormatException e) { return detectionsExportWizard.showWarning("Invalid encounter gap parameter"); } + try { + streamExportParams.minEncounterCount = Integer.valueOf(minEncounterCalls.getText()); + } + catch (NumberFormatException e) { + return detectionsExportWizard.showWarning("Invalid minimum encounter call count"); + } } + streamExportParams.separateChannels = separateChannels.isSelected(); + return streamExportParams.granularity != null; } @Override public void setParams(StreamExportParams streamExportParams) { - GranularityEnumType[] grans = GranularityEnumType.values(); - for (int i = 0; i < grans.length; i++) { - granularities[i].setSelected(streamExportParams.granularity == grans[i]); + for (int i = 0; i < granularities.length; i++) { + granularities[i].setSelected(streamExportParams.granularity == allowedGranularities[i]); } binLength.setText(String.format("%3.1f", streamExportParams.binDurationS)); - minCalls.setText(String.format("%d", streamExportParams.minBinCount)); + minBinnedCalls.setText(String.format("%d", streamExportParams.minBinCount)); encounterGap.setText(String.format("%3.1f", streamExportParams.encounterGapS)); + minEncounterCalls.setText(String.format("%d", streamExportParams.minEncounterCount)); + separateChannels.setSelected(streamExportParams.separateChannels); + groupChannels.setSelected(streamExportParams.separateChannels == false); newDataSelection(); enableControls(); } From 415ec87938d5e7d76e2d9c7735826d3e42738695 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sun, 17 Sep 2023 21:10:23 +0100 Subject: [PATCH 51/65] noise output --- src/PamguardMVC/PamDataBlock.java | 7 +- src/noiseMonitor/NoiseDataBlock.java | 32 ++- .../species/TethysNoiseDataProvider.java | 95 +++++++ src/tethys/pamdata/AutoTethysProvider.java | 235 ++++++++++-------- src/tethys/pamdata/TethysDataProvider.java | 2 + src/tethys/species/FixedSpeciesManager.java | 23 ++ src/tethys/swing/export/GranularityCard.java | 12 +- .../ConnectedRegionDataBlock.java | 19 ++ .../species/WhistleMoanTethysProvider.java | 13 + 9 files changed, 319 insertions(+), 119 deletions(-) create mode 100644 src/noiseMonitor/species/TethysNoiseDataProvider.java create mode 100644 src/tethys/species/FixedSpeciesManager.java create mode 100644 src/whistlesAndMoans/species/WhistleMoanTethysProvider.java diff --git a/src/PamguardMVC/PamDataBlock.java b/src/PamguardMVC/PamDataBlock.java index 8c259d67..b865d572 100644 --- a/src/PamguardMVC/PamDataBlock.java +++ b/src/PamguardMVC/PamDataBlock.java @@ -2872,8 +2872,6 @@ public class PamDataBlock extends PamObservable { private Vector offlineDataMaps = null; private SQLLogging logging; - - private TethysDataProvider tethysDataProvider; private JSONObjectDataSource jsonDataSource; @@ -3093,10 +3091,7 @@ public class PamDataBlock extends PamObservable { * @return the tethysDataProvider */ public TethysDataProvider getTethysDataProvider(TethysControl tethysControl) { - if (tethysDataProvider == null && PamDetection.class.isAssignableFrom(unitClass) && getLogging() != null) { - tethysDataProvider = new AutoTethysProvider(tethysControl, this); - } - return tethysDataProvider; + return null; } /** diff --git a/src/noiseMonitor/NoiseDataBlock.java b/src/noiseMonitor/NoiseDataBlock.java index b5e7f628..effbd591 100644 --- a/src/noiseMonitor/NoiseDataBlock.java +++ b/src/noiseMonitor/NoiseDataBlock.java @@ -2,11 +2,18 @@ package noiseMonitor; import noiseMonitor.alarm.NoiseAlarmCounter; import noiseMonitor.alarm.NoiseAlarmProvider; +import noiseMonitor.species.TethysNoiseDataProvider; +import tethys.TethysControl; +import tethys.pamdata.TethysDataProvider; +import tethys.species.DataBlockSpeciesManager; +import tethys.species.FixedSpeciesManager; import alarm.AlarmCounter; import alarm.AlarmCounterProvider; import alarm.AlarmDataSource; import PamUtils.FrequencyFormat; import PamUtils.PamUtils; +import PamguardMVC.DataAutomation; +import PamguardMVC.DataAutomationInfo; import PamguardMVC.PamDataBlock; import PamguardMVC.PamProcess; @@ -32,13 +39,15 @@ public class NoiseDataBlock extends PamDataBlock implements Alarm private NoiseAlarmProvider noiseAlarmCounter; /** - * These are the names used in the database columns, so dont' change them on pain of + * These are the names used in the database columns, so don't change them on pain of * nothing ever working ever again ! */ public static final String[] measureNames = {"mean", "median", "low95", "high95", "Min", "Max", "Peak"}; public static final String[] displayNames = {"Mean", "Median", "Lower 95%", "Upper 95%", "Minimum", "Maximim", "Peak"}; private int statisticTypes; + private TethysNoiseDataProvider tethysNoiseDataProvider; + private FixedSpeciesManager fixedSpeciesManager; public NoiseDataBlock(String dataName, PamProcess parentProcess, int channelMap) { @@ -244,6 +253,27 @@ public class NoiseDataBlock extends PamDataBlock implements Alarm } return noiseAlarmCounter; } + + @Override + public DataAutomationInfo getDataAutomationInfo() { + return new DataAutomationInfo(DataAutomation.AUTOMATIC); + } + + @Override + public TethysDataProvider getTethysDataProvider(TethysControl tethysControl) { + if (tethysNoiseDataProvider == null) { + tethysNoiseDataProvider = new TethysNoiseDataProvider(tethysControl, this); + } + return tethysNoiseDataProvider; + } + + @Override + public DataBlockSpeciesManager getDatablockSpeciesManager() { + if (fixedSpeciesManager == null) { + fixedSpeciesManager = new FixedSpeciesManager(this, -10, "anthropogenic", "noise"); + } + return fixedSpeciesManager; + } } diff --git a/src/noiseMonitor/species/TethysNoiseDataProvider.java b/src/noiseMonitor/species/TethysNoiseDataProvider.java new file mode 100644 index 00000000..f342b499 --- /dev/null +++ b/src/noiseMonitor/species/TethysNoiseDataProvider.java @@ -0,0 +1,95 @@ +package noiseMonitor.species; + +import java.util.List; + +import PamUtils.PamUtils; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import nilus.Detection; +import nilus.Detection.Parameters; +import nilus.DetectionEffortKind; +import nilus.GranularityEnumType; +import nilus.Helper; +import noiseMonitor.NoiseDataBlock; +import noiseMonitor.NoiseDataUnit; +import tethys.TethysControl; +import tethys.niluswraps.PDeployment; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; +import tethys.pamdata.AutoTethysProvider; + +public class TethysNoiseDataProvider extends AutoTethysProvider { + + private NoiseDataBlock noiseDataBlock; + + public TethysNoiseDataProvider(TethysControl tethysControl, NoiseDataBlock noiseDataBlock) { + super(tethysControl, noiseDataBlock); + this.noiseDataBlock = noiseDataBlock; + } + + @Override + public GranularityEnumType[] getAllowedGranularities() { + GranularityEnumType[] allowed = {GranularityEnumType.CALL}; + return allowed; + } + + @Override + public Detection createDetection(PamDataUnit dataUnit, TethysExportParams tethysExportParams, + StreamExportParams streamExportParams) { + Detection detection = super.createDetection(dataUnit, tethysExportParams, streamExportParams); + NoiseDataUnit noiseDataUnit = (NoiseDataUnit) dataUnit; + /* + * Now all the noise measurements, noting thre may be several types. + */ + int statTypes = noiseDataBlock.getStatisticTypes(); + int nTypes = PamUtils.getNumChannels(statTypes); + Parameters params = detection.getParameters(); + List measurements = params.getFrequencyMeasurementsDB(); + double[][] noiseData = noiseDataUnit.getNoiseBandData(); + int meanIndex = -1; + for (int i = 0; i < nTypes; i++) { + int type = PamUtils.getNthChannel(i, statTypes); + String name = noiseDataBlock.getMeasureName(type); + if (1< effortKinds, + StreamExportParams exportParams) { + super.getEffortKinds(pDeployment, effortKinds, exportParams); + DetectionEffortKind kind = effortKinds.get(0); + nilus.DetectionEffortKind.Parameters params = kind.getParameters(); + if (params == null) { + params = new nilus.DetectionEffortKind.Parameters(); + try { + Helper.createRequiredElements(params); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + kind.setParameters(params); + } + List fMeasures = params.getFrequencyMeasurementsHz(); + double[] loEdges = noiseDataBlock.getBandLoEdges(); + double[] hiEdges = noiseDataBlock.getBandHiEdges(); + // put lot mean into the array + for (int i = 0; i < loEdges.length; i++) { + fMeasures.add(roundSignificantFigures(Math.sqrt(loEdges[i]*hiEdges[i]), 4)); + } + } + +} diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 22d971e4..4ffd3af1 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -66,7 +66,7 @@ import java.net.URISyntaxException; * @author dg50 * */ -public class AutoTethysProvider implements TethysDataProvider { +abstract public class AutoTethysProvider implements TethysDataProvider { private PamDataBlock pamDataBlock; private PamProcess pamProcess; @@ -92,11 +92,11 @@ public class AutoTethysProvider implements TethysDataProvider { return schema; } -// @Override -// public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) { -// // TODO Auto-generated method stub -// return null; -// } + // @Override + // public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) { + // // TODO Auto-generated method stub + // return null; + // } @Override public DescriptionType getDescription(Deployment deployment, TethysExportParams tethysExportParams) { @@ -124,14 +124,14 @@ public class AutoTethysProvider implements TethysDataProvider { // TODO Auto-generated catch block e.printStackTrace(); } -// algorithm.setMethod(this.getAlgorithmMethod()); -// algorithm.setSoftware("PAMGuard"); -// algorithm.setVersion(PamguardVersionInfo.version); + // algorithm.setMethod(this.getAlgorithmMethod()); + // algorithm.setSoftware("PAMGuard"); + // algorithm.setVersion(PamguardVersionInfo.version); nilus.AlgorithmType.Parameters algoParameters = this.getAlgorithmParameters(); if (algoParameters != null) { algorithm.setParameters(algoParameters); } - + return algorithm; } @@ -156,67 +156,67 @@ public class AutoTethysProvider implements TethysDataProvider { return null; } paramList.addAll(genList); - -// Document doc = XMLUtils.createBlankDoc(); -// PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); -// Element dummyEl = doc.createElement("MODULES"); -// doc.appendChild(dummyEl); -// PamSettings[] settingsObjs = getSettingsObjects(); -// if (settingsObjs == null) { -// return null; -// } -//// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); -// Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); -// if (settingsEl == null) { -// return null; -// } -// -//// settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); -// -// -// dummyEl.appendChild(settingsEl); -// NodeList childs = settingsEl.getChildNodes(); -// for (int i = 0; i < childs.getLength(); i++) { -// Node el = childs.item(i); -// // System.out.println(el.getNodeName()); -// if (el instanceof Element) { -// paramList.add((Element) el); -// } -// } -// -// // Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); -// // String moduleXML = null; -// if (doc != null) { -// // this string should be XML of all the settings for the module controlling this -// // datablock. -// // moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml -// // System.out.printf("Module settings for datablock %s are:\n", moduleXML); -// // System.out.println(moduleXML); -// // Element pamguard = doc.get("PAMGUARD"); -// // Element modules = (Element) pamguard.getElementsByTagName("MODULES"); -// // doc.get -// // NodeList childs = doc.getChildNodes(); -// // for (int i = 0; i < childs.getLength(); i++) { -// // Node el = childs.item(i); -// // System.out.println(el.getNodeName()); -// // if (el instanceof Element) { -// // paramList.add((Element) el); -// // } -// // } -// // String moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml -// // System.out.printf("Module settings for datablock %s are:\n%s", this.pamDataBlock.getDataName(), moduleXML); -// } -// -// // // try the old say -// // Document doc2 = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); -// // String moduleXML = null; -// // if (doc2 != null) { -// // // this string should be XML of all the settings for the module controlling this -// // // datablock. -// // moduleXML = pamXMLWriter.getAsString(doc2, true); // change to false to get smaller xml -// // System.out.printf("Module settings for datablock %s are:\n%s", pamDataBlock.getDataName(),moduleXML); -// // } -// // + + // Document doc = XMLUtils.createBlankDoc(); + // PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + // Element dummyEl = doc.createElement("MODULES"); + // doc.appendChild(dummyEl); + // PamSettings[] settingsObjs = getSettingsObjects(); + // if (settingsObjs == null) { + // return null; + // } + //// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); + // Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); + // if (settingsEl == null) { + // return null; + // } + // + //// settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); + // + // + // dummyEl.appendChild(settingsEl); + // NodeList childs = settingsEl.getChildNodes(); + // for (int i = 0; i < childs.getLength(); i++) { + // Node el = childs.item(i); + // // System.out.println(el.getNodeName()); + // if (el instanceof Element) { + // paramList.add((Element) el); + // } + // } + // + // // Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); + // // String moduleXML = null; + // if (doc != null) { + // // this string should be XML of all the settings for the module controlling this + // // datablock. + // // moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml + // // System.out.printf("Module settings for datablock %s are:\n", moduleXML); + // // System.out.println(moduleXML); + // // Element pamguard = doc.get("PAMGUARD"); + // // Element modules = (Element) pamguard.getElementsByTagName("MODULES"); + // // doc.get + // // NodeList childs = doc.getChildNodes(); + // // for (int i = 0; i < childs.getLength(); i++) { + // // Node el = childs.item(i); + // // System.out.println(el.getNodeName()); + // // if (el instanceof Element) { + // // paramList.add((Element) el); + // // } + // // } + // // String moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml + // // System.out.printf("Module settings for datablock %s are:\n%s", this.pamDataBlock.getDataName(), moduleXML); + // } + // + // // // try the old say + // // Document doc2 = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); + // // String moduleXML = null; + // // if (doc2 != null) { + // // // this string should be XML of all the settings for the module controlling this + // // // datablock. + // // moduleXML = pamXMLWriter.getAsString(doc2, true); // change to false to get smaller xml + // // System.out.printf("Module settings for datablock %s are:\n%s", pamDataBlock.getDataName(),moduleXML); + // // } + // // return parameters; } @@ -224,26 +224,26 @@ public class AutoTethysProvider implements TethysDataProvider { private Element addNameSpaceToElements(Document doc, Element settingsEl, String xmlNameSpace) { -// String xsltString = "\r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + " \r\n" -// + "\r\n"; + // String xsltString = "\r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + " \r\n" + // + "\r\n"; String xsltString = "\n"; try { TransformerFactory factory = TransformerFactory.newInstance(); -// Source xslt = new StreamSource(new File("transform.xslt")); + // Source xslt = new StreamSource(new File("transform.xslt")); StringReader reader = new StringReader(xmlNameSpace); Source xslt = new StreamSource(reader); - + Transformer transformer = factory.newTransformer(xslt); - DOMSource source = new DOMSource(doc); - -// Result -// Source text = new StreamSource(new File("input.xml")); - DOMResult result = new DOMResult(); + DOMSource source = new DOMSource(doc); + + // Result + // Source text = new StreamSource(new File("input.xml")); + DOMResult result = new DOMResult(); transformer.transform(source, result); - + System.out.println(result.toString()); } catch (Exception e) { @@ -312,13 +312,13 @@ public class AutoTethysProvider implements TethysDataProvider { Detection detection = new Detection(); detection.setStart(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getTimeMilliseconds())); detection.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getEndTimeInMilliseconds())); - + DataBlockSpeciesManager speciesManager = pamDataBlock.getDatablockSpeciesManager(); SpeciesMapItem speciesItem = null; if (speciesManager != null) { speciesItem = speciesManager.getSpeciesItem(dataUnit); -// detection.setSpeciesId(new Species); -// detection.setSpeciesId(getSpeciesIdType()); + // detection.setSpeciesId(new Species); + // detection.setSpeciesId(getSpeciesIdType()); } else { } @@ -351,7 +351,7 @@ public class AutoTethysProvider implements TethysDataProvider { detParams.setReceivedLevelDB(ampli); // DataUnitBaseData basicData = dataUnit.getBasicData(); gotTonalContour(dataUnit, detParams); - + String uid = BigInteger.valueOf(dataUnit.getUID()).toString(); Element el = addUserDefined(detParams,"PAMGuardUID", uid); DataUnitFileInformation fileInf = dataUnit.getDataUnitFileInformation(); @@ -362,7 +362,7 @@ public class AutoTethysProvider implements TethysDataProvider { return detection; } - + private Element addUserDefined(Parameters parameters, String parameterName, String parameterValue) { UserDefined userDefined = parameters.getUserDefined(); if (userDefined == null) { @@ -465,7 +465,7 @@ public class AutoTethysProvider implements TethysDataProvider { kind.getSpeciesId().setValue(BigInteger.valueOf(mapItem.getItisCode())); kind.getGranularity().setValue(exportParams.granularity); -// nilus.DetectionEffortKind.Parameters granularityParams = kind.getParameters(); + // nilus.DetectionEffortKind.Parameters granularityParams = kind.getParameters(); switch (exportParams.granularity) { case BINNED: kind.getGranularity().setBinSizeM(exportParams.binDurationS/60.); @@ -479,7 +479,7 @@ public class AutoTethysProvider implements TethysDataProvider { break; case GROUPED: break; - + } kind.setCall(mapItem.getCallType()); @@ -487,7 +487,7 @@ public class AutoTethysProvider implements TethysDataProvider { effortKinds.add(kind); } - + } @Override @@ -505,7 +505,7 @@ public class AutoTethysProvider implements TethysDataProvider { else { method = String.format("%s processing using the PAMGuard %s", dataAutomation.getAutomation(), pcu.getUnitType()); } - + return method; } @@ -535,4 +535,23 @@ public class AutoTethysProvider implements TethysDataProvider { return documentName; } + public static double roundDecimalPlaces(double value, int decPlaces) { + double scale = Math.pow(10, decPlaces); + long longVal = Math.round(value*scale); + return (double) longVal/scale; + } + + public static double roundSignificantFigures(double value, int sigFigs) { + if (value == 0) { + return 0; + } + double sign = Math.signum(value); + value = Math.abs(value); + double scale = sigFigs-Math.floor(Math.log10(value)); + scale = Math.pow(10, scale); + long longVal = Math.round(value*scale); + return sign*(double) longVal/scale; + } + + } diff --git a/src/tethys/pamdata/TethysDataProvider.java b/src/tethys/pamdata/TethysDataProvider.java index d040075f..709fc1ba 100644 --- a/src/tethys/pamdata/TethysDataProvider.java +++ b/src/tethys/pamdata/TethysDataProvider.java @@ -69,6 +69,8 @@ public interface TethysDataProvider { */ public GranularityEnumType[] getAllowedGranularities(); +// public String getGranularityName GranularityEnumType); + /** * Get a name for the detections documents. This will be appended * to the Deployment name and may also have a number after it.
diff --git a/src/tethys/species/FixedSpeciesManager.java b/src/tethys/species/FixedSpeciesManager.java new file mode 100644 index 00000000..dd795412 --- /dev/null +++ b/src/tethys/species/FixedSpeciesManager.java @@ -0,0 +1,23 @@ +package tethys.species; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; + +public class FixedSpeciesManager extends DataBlockSpeciesManager { + + + public FixedSpeciesManager(PamDataBlock dataBlock, int itisCode, String name, String callType) { + super(dataBlock); + setDefaultDefaultSpecies(new SpeciesMapItem(itisCode, name, callType)); + } + + @Override + public DataBlockSpeciesCodes getSpeciesCodes() { + return null; + } + + @Override + public String getSpeciesCode(T dataunit) { + return getDefaultSpeciesCode(); + } +} diff --git a/src/tethys/swing/export/GranularityCard.java b/src/tethys/swing/export/GranularityCard.java index 988ca513..c3aeaf8e 100644 --- a/src/tethys/swing/export/GranularityCard.java +++ b/src/tethys/swing/export/GranularityCard.java @@ -64,6 +64,10 @@ public class GranularityCard extends ExportWizardCard { granPanel.setBorder(new TitledBorder("Granularity")); ButtonGroup granGroup = new ButtonGroup(); GranularityChange gc = new GranularityChange(); + binLength = new JTextField(5); + minBinnedCalls = new JTextField(5); + encounterGap = new JTextField(5); + minEncounterCalls = new JTextField(5); for (int i = 0; i < allowedGranularities.length; i++) { c.gridx = 0; granularities[i] = new JRadioButton(PGranularityType.prettyString(allowedGranularities[i])); @@ -76,11 +80,11 @@ public class GranularityCard extends ExportWizardCard { c.gridx++; granPanel.add(new JLabel(" Bin duration ", JLabel.RIGHT), c); c.gridx++; - granPanel.add(binLength = new JTextField(5), c); + granPanel.add(binLength, c); c.gridx++; granPanel.add(new JLabel("(s), Min Calls", JLabel.LEFT), c); c.gridx++; - granPanel.add(minBinnedCalls = new JTextField(5), c); + granPanel.add(minBinnedCalls, c); binLength.setToolTipText("Time bin duration in seconds"); minBinnedCalls.setToolTipText("Minimum number of calls for a bin to be output"); } @@ -89,11 +93,11 @@ public class GranularityCard extends ExportWizardCard { c.gridx++; granPanel.add(new JLabel(" Minimum gap ", JLabel.RIGHT), c); c.gridx++; - granPanel.add(encounterGap = new JTextField(5), c); + granPanel.add(encounterGap, c); c.gridx++; granPanel.add(new JLabel("(s), Min Calls", JLabel.LEFT), c); c.gridx++; - granPanel.add(minEncounterCalls = new JTextField(5), c); + granPanel.add(minEncounterCalls, c); encounterGap.setToolTipText("Minimum gap between separate encounters"); minEncounterCalls.setToolTipText("Minimum number of calls for an encounter to be output"); } diff --git a/src/whistlesAndMoans/ConnectedRegionDataBlock.java b/src/whistlesAndMoans/ConnectedRegionDataBlock.java index 5787c098..1a298fb0 100644 --- a/src/whistlesAndMoans/ConnectedRegionDataBlock.java +++ b/src/whistlesAndMoans/ConnectedRegionDataBlock.java @@ -2,15 +2,20 @@ package whistlesAndMoans; import whistlesAndMoans.alarm.WMAlarmCounterProvider; import whistlesAndMoans.dataSelector.WMDDataSelectCreator; +import whistlesAndMoans.species.WhistleMoanTethysProvider; import whistlesAndMoans.species.WhistleSpeciesManager; import whistlesAndMoans.toad.WSLToadCalculator; import PamView.GroupedDataSource; import PamView.GroupedSourceParameters; +import PamguardMVC.DataAutomation; +import PamguardMVC.DataAutomationInfo; import PamguardMVC.FFTDataHolderBlock; import PamguardMVC.dataSelector.DataSelectorCreator; import PamguardMVC.toad.TOADCalculator; import alarm.AlarmCounterProvider; import alarm.AlarmDataSource; +import tethys.TethysControl; +import tethys.pamdata.TethysDataProvider; import tethys.species.DataBlockSpeciesManager; public class ConnectedRegionDataBlock extends AbstractWhistleDataBlock implements AlarmDataSource, GroupedDataSource, FFTDataHolderBlock { @@ -21,6 +26,7 @@ public class ConnectedRegionDataBlock extends AbstractWhistleDataBlock Date: Mon, 18 Sep 2023 12:49:39 +0100 Subject: [PATCH 52/65] Noise logging --- src/noiseMonitor/NoiseLogging.java | 22 ++++++++++ .../species/TethysNoiseDataProvider.java | 12 ++++- src/tethys/deployment/DeploymentHandler.java | 17 +++---- .../detection/DetectionExportProgress.java | 1 + src/tethys/detection/DetectionsHandler.java | 14 +++--- src/tethys/pamdata/AutoTethysProvider.java | 44 ++++++++++++------- src/tethys/pamdata/TethysDataProvider.java | 23 +++++++--- .../swing/export/DetectionsExportWizard.java | 5 +++ src/tethys/swing/export/ExportWizardCard.java | 4 +- src/tethys/swing/export/ExportWorkerCard.java | 3 ++ 10 files changed, 105 insertions(+), 40 deletions(-) diff --git a/src/noiseMonitor/NoiseLogging.java b/src/noiseMonitor/NoiseLogging.java index 4bbc0cfd..a3a62d61 100644 --- a/src/noiseMonitor/NoiseLogging.java +++ b/src/noiseMonitor/NoiseLogging.java @@ -116,5 +116,27 @@ public class NoiseLogging extends SQLLogging { } } } + + private long lastTime; + + @Override + protected PamDataUnit createDataUnit(SQLTypes sqlTypes, long timeMilliseconds, int databaseIndex) { + int chan = channelNumber.getIntegerValue(); + int nBands = noiseDataBlock.getBandLoEdges().length; + int nMeasures = noiseDataBlock.getUsedMeasureNames().length; + if (nMeasures * nBands != bandItems.length) { + return null; + } + double[][] bandData = new double[nBands][nMeasures]; + for (int iBand = 0, iCol = 0; iBand < nBands; iBand++) { + for (int iMeasure = 0; iMeasure < nMeasures; iMeasure++, iCol++) { + bandData[iBand][iMeasure] = bandItems[iCol].getDoubleValue(); + } + } + + NoiseDataUnit noiseDataUnit = new NoiseDataUnit(timeMilliseconds, 1< projectDeployments; + private Helper nilusHelper; + public DeploymentHandler(TethysControl tethysControl) { super(); this.tethysControl = tethysControl; - tethysControl.addStateObserver(this); + tethysControl.addStateObserver(this); + try { + nilusHelper = new Helper(); + } catch (JAXBException e) { + e.printStackTrace(); + } } @Override @@ -973,12 +979,7 @@ public class DeploymentHandler implements TethysStateObserver { ArrayList phones = array.getHydrophoneArray(); int iPhone = 0; long timeMillis = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); - Helper nilusHelper = null; - try { - nilusHelper = new Helper(); - } catch (JAXBException e) { - e.printStackTrace(); - } + for (Hydrophone aPhone : phones) { PamVector hydLocs = array.getAbsHydrophoneVector(iPhone, timeMillis); Audio audio = new Audio(); diff --git a/src/tethys/detection/DetectionExportProgress.java b/src/tethys/detection/DetectionExportProgress.java index 4456547f..abfa74b8 100644 --- a/src/tethys/detection/DetectionExportProgress.java +++ b/src/tethys/detection/DetectionExportProgress.java @@ -9,6 +9,7 @@ public class DetectionExportProgress { public static final int STATE_CANCELED = 2; public static final int STATE_COMPLETE = 3; public static final int STATE_WRITING = 4; + public static final int STATE_COUNTING = 5; public PDeployment currentDeployment; public Detections currentDetections; public long lastUnitTime; diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 6f2246aa..9d0fbb10 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -8,6 +8,7 @@ import javax.swing.SwingWorker; import PamController.PamControlledUnit; import PamController.PamguardVersionInfo; import PamModel.PamPluginInterface; +import PamUtils.PamCalendar; import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -371,7 +372,6 @@ public class DetectionsHandler { * and export the content of each separately. */ TethysExportParams exportParams = tethysControl.getTethysExportParams(); - DBXMLConnect dbxmlConnect = tethysControl.getDbxmlConnect(); DeploymentHandler depHandler = tethysControl.getDeploymentHandler(); ArrayList deployments = depHandler.getMatchedDeployments(); // Detections currentDetections = null; @@ -386,7 +386,7 @@ public class DetectionsHandler { for (PDeployment deployment : deployments) { int documentCount = 0; prog = new DetectionExportProgress(deployment, null, - lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING); exportObserver.update(prog); granularityHandler.prepare(deployment.getAudioStart()); // export everything in that deployment. @@ -407,6 +407,8 @@ public class DetectionsHandler { } dataBlock.loadViewerData(mapPoint.getStartTime(), mapPoint.getEndTime(), null); ArrayList dataCopy = dataBlock.getDataCopy(deployment.getAudioStart(), deployment.getAudioEnd(), true, dataSelector); +// System.out.printf("%d loaded from %s to %s %d kept\n", dataBlock.getUnitsCount(), PamCalendar.formatDateTime(mapPoint.getStartTime()), +// PamCalendar.formatDateTime(mapPoint.getEndTime()), dataCopy.size()); skipCount += dataBlock.getUnitsCount() - dataCopy.size(); for (PamDataUnit dataUnit : dataCopy) { /* @@ -425,7 +427,7 @@ public class DetectionsHandler { } prog = new DetectionExportProgress(deployment, null, - lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING); exportObserver.update(prog); // if (documentCount > 500000 && mapPoint != dataMap.getLastMapPoint()) { @@ -450,9 +452,9 @@ public class DetectionsHandler { } - prog = new DetectionExportProgress(null, null, - lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COMPLETE); - exportObserver.update(prog); +// prog = new DetectionExportProgress(null, null, +// lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); +// exportObserver.update(prog); return exportCount; }/** * Export detections in all deployments for this PAMGuard dataset. diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 4ffd3af1..bda6fa70 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -44,6 +44,7 @@ import tethys.species.DataBlockSpeciesManager; import tethys.species.DataBlockSpeciesMap; import tethys.species.ITISTypes; import tethys.species.SpeciesMapItem; +import tethys.swing.export.ExportWizardCard; import whistleClassifier.WhistleContour; import javax.xml.bind.JAXBException; @@ -72,25 +73,31 @@ abstract public class AutoTethysProvider implements TethysDataProvider { private PamProcess pamProcess; private PamControlledUnit pamControlledUnit; private TethysControl tethysControl; + private Helper helper; public AutoTethysProvider(TethysControl tethysControl, PamDataBlock pamDataBlock) { this.tethysControl = tethysControl; this.pamDataBlock = pamDataBlock; pamProcess = pamDataBlock.getParentProcess(); pamControlledUnit = pamProcess.getPamControlledUnit(); - } - - @Override - public TethysSchema getSchema() { - SQLLogging logging = pamDataBlock.getLogging(); - if (logging == null) { - return null; + try { + helper = new Helper(); + } catch (JAXBException e) { + e.printStackTrace(); } - DBSchemaWriter schemaWriter = new DBSchemaWriter(); - Document doc = schemaWriter.generateDatabaseSchema(pamDataBlock, logging, logging.getTableDefinition()); - TethysSchema schema = new TethysSchema(doc); - return schema; } +// +// @Override +// public TethysSchema getSchema() { +// SQLLogging logging = pamDataBlock.getLogging(); +// if (logging == null) { +// return null; +// } +// DBSchemaWriter schemaWriter = new DBSchemaWriter(); +// Document doc = schemaWriter.generateDatabaseSchema(pamDataBlock, logging, logging.getTableDefinition()); +// TethysSchema schema = new TethysSchema(doc); +// return schema; +// } // @Override // public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) { @@ -369,10 +376,8 @@ abstract public class AutoTethysProvider implements TethysDataProvider { userDefined = new UserDefined(); parameters.setUserDefined(userDefined); } - Helper helper; Element el = null; try { - helper = new Helper(); el = helper.AddAnyElement(userDefined.getAny(), parameterName, parameterValue); } catch (JAXBException e) { e.printStackTrace(); @@ -401,8 +406,8 @@ abstract public class AutoTethysProvider implements TethysDataProvider { List offsetS = tonal.getOffsetS(); List hz = tonal.getHz(); for (int i = 0; i < tMillis.length; i++) { - offsetS.add((double) (tMillis[i]-tMillis[0]) / 1000.); - hz.add(fHz[i]); + offsetS.add(roundSignificantFigures((double) (tMillis[i]-tMillis[0]) / 1000., 4)); + hz.add(roundSignificantFigures(fHz[i], 4)); } detParams.setTonal(tonal); return true; @@ -417,8 +422,8 @@ abstract public class AutoTethysProvider implements TethysDataProvider { List offsetS = tonal.getOffsetS(); List hz = tonal.getHz(); for (int i = 0; i < t.length; i++) { - offsetS.add(t[i]-t[0]); - hz.add(f[i]); + offsetS.add(roundSignificantFigures(t[i]-t[0],4)); + hz.add(roundSignificantFigures(f[i],4)); } detParams.setTonal(tonal); return true; @@ -553,5 +558,10 @@ abstract public class AutoTethysProvider implements TethysDataProvider { return sign*(double) longVal/scale; } + @Override + public boolean wantExportDialogCard(ExportWizardCard wizPanel) { + return true; + } + } diff --git a/src/tethys/pamdata/TethysDataProvider.java b/src/tethys/pamdata/TethysDataProvider.java index 709fc1ba..f273c3bd 100644 --- a/src/tethys/pamdata/TethysDataProvider.java +++ b/src/tethys/pamdata/TethysDataProvider.java @@ -13,6 +13,7 @@ import nilus.GranularityEnumType; import tethys.niluswraps.PDeployment; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; +import tethys.swing.export.ExportWizardCard; /** * Any PAMGuard data stream which can provide Detection data to PAMGuard will @@ -24,13 +25,13 @@ import tethys.output.TethysExportParams; */ public interface TethysDataProvider { - /** - * This gets the Tethys schema for this type of data in whatever - * form we decide it's best stored in, an XML string, or what ? - * @return - */ - public TethysSchema getSchema(); - +// /** +// * This gets the Tethys schema for this type of data in whatever +// * form we decide it's best stored in, an XML string, or what ? +// * @return +// */ +// public TethysSchema getSchema(); +// /** * This will convert a data unit for this provider into whatever format we need the @@ -110,6 +111,14 @@ public interface TethysDataProvider { * @param exportParams */ public void getEffortKinds(PDeployment pDeployment, List effortKinds, StreamExportParams exportParams); + + /** + * See if a particular card should be used in the export wizard. This may + * not be the best way of doing this, but will do for now. + * @param wizPanel + * @return + */ + public boolean wantExportDialogCard(ExportWizardCard wizPanel); } diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index f476a7ee..2e71784e 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -18,6 +18,7 @@ import PamView.dialog.PamDialog; import PamguardMVC.PamDataBlock; import tethys.TethysControl; import tethys.output.StreamExportParams; +import tethys.pamdata.TethysDataProvider; public class DetectionsExportWizard extends PamDialog { @@ -32,6 +33,7 @@ public class DetectionsExportWizard extends PamDialog { private ArrayList wizardCards = new ArrayList(); private AlgorithmCard algorithmCard; private ExportWorkerCard exportWorkerCard; + private TethysDataProvider tethysDataProvider; private DetectionsExportWizard(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { super(parentFrame, "Detections Export", false); @@ -41,6 +43,7 @@ public class DetectionsExportWizard extends PamDialog { if (streamExportParams == null) { streamExportParams = new StreamExportParams(tethysControl, dataBlock, false); } + tethysDataProvider = dataBlock.getTethysDataProvider(tethysControl); cardLayout = new CardLayout(); JPanel mainPanel = new JPanel(new BorderLayout()); @@ -73,8 +76,10 @@ public class DetectionsExportWizard extends PamDialog { } private void addCard(ExportWizardCard wizPanel) { + if (tethysDataProvider.wantExportDialogCard(wizPanel)) { cardPanel.add(wizPanel, wizPanel.getTitle()); wizardCards.add(wizPanel); + } } public static void showDilaog(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { diff --git a/src/tethys/swing/export/ExportWizardCard.java b/src/tethys/swing/export/ExportWizardCard.java index f81c7b12..9b33369b 100644 --- a/src/tethys/swing/export/ExportWizardCard.java +++ b/src/tethys/swing/export/ExportWizardCard.java @@ -12,7 +12,9 @@ import tethys.output.StreamExportParams; * @author dg50 * */ -abstract class ExportWizardCard extends JPanel { +abstract public class ExportWizardCard extends JPanel { + + private static final long serialVersionUID = 1L; private String title; private PamDataBlock dataBlock; diff --git a/src/tethys/swing/export/ExportWorkerCard.java b/src/tethys/swing/export/ExportWorkerCard.java index bfe0c983..f8400587 100644 --- a/src/tethys/swing/export/ExportWorkerCard.java +++ b/src/tethys/swing/export/ExportWorkerCard.java @@ -144,6 +144,9 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor case DetectionExportProgress.STATE_GATHERING: progressText.setText("Running export"); break; + case DetectionExportProgress.STATE_COUNTING: + progressText.setText("Counting data"); + break; case DetectionExportProgress.STATE_CANCELED: progressText.setText("Export cancelled"); break; From 4d294b56a8673a3f938f87b6be3237e234804a17 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Tue, 19 Sep 2023 16:55:33 +0100 Subject: [PATCH 53/65] Click event output --- src/Array/StreamerDialog.java | 12 +++ src/clickDetector/ClickDataBlock.java | 20 +++++ .../offlineFuncs/OfflineEventDataBlock.java | 44 ++++++++--- .../tethys/ClickEventSpeciesManager.java | 53 +++++++++++++ .../tethys/ClickEventTethysDataProvider.java | 72 ++++++++++++++++++ .../tethys/ClickTethysDataProvider.java | 22 ++++++ src/generalDatabase/SQLTypes.java | 1 + src/tethys/deployment/DeploymentHandler.java | 34 ++++++++- src/tethys/detection/DetectionsHandler.java | 75 +++++++++++++++++-- .../detection/GroupedGranularityHandler.java | 12 +-- src/tethys/output/TethysExportParams.java | 2 +- .../output/swing/TethysExportDialog.java | 4 +- src/tethys/pamdata/AutoTethysProvider.java | 7 +- src/tethys/swing/DatablockSynchPanel.java | 2 +- src/tethys/swing/DetectionsExportPanel.java | 2 +- .../swing/export/DetectionsExportWizard.java | 2 +- src/tethys/swing/export/ExportWorkerCard.java | 11 ++- 17 files changed, 339 insertions(+), 36 deletions(-) create mode 100644 src/clickDetector/tethys/ClickEventSpeciesManager.java create mode 100644 src/clickDetector/tethys/ClickEventTethysDataProvider.java create mode 100644 src/clickDetector/tethys/ClickTethysDataProvider.java diff --git a/src/Array/StreamerDialog.java b/src/Array/StreamerDialog.java index e629b142..73a81469 100644 --- a/src/Array/StreamerDialog.java +++ b/src/Array/StreamerDialog.java @@ -259,6 +259,7 @@ public class StreamerDialog extends PamDialog { // } singleInstance.currentArray = currentArray; singleInstance.defaultStreamer = streamer;//.clone(); +// singleInstance.st singleInstance.setParams(); singleInstance.setVisible(true); return singleInstance.defaultStreamer; @@ -549,6 +550,17 @@ public class StreamerDialog extends PamDialog { pack(); } } + + try { + OriginSettings os = defaultStreamer.getOriginSettings(currentOriginMethod.getClass()); + if (os != null) { + currentOriginMethod.setOriginSettings(os); + } + } + catch (Exception e) { + // will throw if it tries to set the wrong type of settings. + } + enableControls(); } diff --git a/src/clickDetector/ClickDataBlock.java b/src/clickDetector/ClickDataBlock.java index e00c42ce..5536aa79 100644 --- a/src/clickDetector/ClickDataBlock.java +++ b/src/clickDetector/ClickDataBlock.java @@ -3,6 +3,8 @@ package clickDetector; import java.util.ListIterator; import pamScrollSystem.ViewLoadObserver; +import tethys.TethysControl; +import tethys.pamdata.TethysDataProvider; import tethys.species.DataBlockSpeciesManager; //import staticLocaliser.StaticLocaliserControl; //import staticLocaliser.StaticLocaliserProvider; @@ -14,6 +16,7 @@ import binaryFileStorage.BinaryStore; import clickDetector.ClickClassifiers.ClickBlockSpeciesManager; import clickDetector.dataSelector.ClickDataSelectCreator; import clickDetector.offlineFuncs.OfflineClickLogging; +import clickDetector.tethys.ClickTethysDataProvider; import clickDetector.toad.ClickTOADCalculator; import dataMap.OfflineDataMap; import fftManager.fftorganiser.FFTDataOrganiser; @@ -26,6 +29,8 @@ import PamUtils.PamUtils; import PamView.GroupedDataSource; import PamView.GroupedSourceParameters; import PamguardMVC.AcousticDataBlock; +import PamguardMVC.DataAutomation; +import PamguardMVC.DataAutomationInfo; import PamguardMVC.FFTDataHolderBlock; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -69,6 +74,8 @@ public class ClickDataBlock extends AcousticDataBlock implement private ClickTOADCalculator clickTOADCalculator; + private ClickTethysDataProvider clickTethysDataProvider; + /** * Click detector loading has to be a bit different to normal - first * data are loaded from the binary store, then a subset of these data @@ -316,5 +323,18 @@ public class ClickDataBlock extends AcousticDataBlock implement return clickBlockSpeciesManager; } + @Override + public TethysDataProvider getTethysDataProvider(TethysControl tethysControl) { + if (clickTethysDataProvider == null) { + clickTethysDataProvider = new ClickTethysDataProvider(tethysControl, this); + } + return clickTethysDataProvider; + } + + @Override + public DataAutomationInfo getDataAutomationInfo() { + return new DataAutomationInfo(DataAutomation.AUTOMATIC); + } + } diff --git a/src/clickDetector/offlineFuncs/OfflineEventDataBlock.java b/src/clickDetector/offlineFuncs/OfflineEventDataBlock.java index 4af5872b..afc60ec7 100644 --- a/src/clickDetector/offlineFuncs/OfflineEventDataBlock.java +++ b/src/clickDetector/offlineFuncs/OfflineEventDataBlock.java @@ -16,6 +16,10 @@ import PamController.PamViewParameters; import PamUtils.PamCalendar; import PamView.symbol.StandardSymbolManager; import pamScrollSystem.ViewLoadObserver; +import tethys.TethysControl; +import tethys.pamdata.TethysDataProvider; +import tethys.species.DataBlockSpeciesManager; +import clickDetector.ClickDetection; //import staticLocaliser.StaticLocaliserControl; //import staticLocaliser.StaticLocaliserProvider; //import staticLocaliser.panels.AbstractLocaliserControl; @@ -23,7 +27,12 @@ import pamScrollSystem.ViewLoadObserver; import clickDetector.ClickDetector; import clickDetector.ClickTrainDetection; import clickDetector.dataSelector.ClickTrainDataSelectorCreator; +import clickDetector.tethys.ClickEventSpeciesManager; +import clickDetector.tethys.ClickEventTethysDataProvider; +import clickDetector.tethys.ClickTethysDataProvider; import dataMap.OfflineDataMap; +import PamguardMVC.DataAutomation; +import PamguardMVC.DataAutomationInfo; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.dataOffline.OfflineDataLoadInfo; @@ -49,6 +58,8 @@ public class OfflineEventDataBlock extends SuperDetDataBlock getDatablockSpeciesManager() { + if (eventSpeciesManager == null) { + eventSpeciesManager = new ClickEventSpeciesManager(clickDetector, this); + } + return eventSpeciesManager; + } + + @Override + public TethysDataProvider getTethysDataProvider(TethysControl tethysControl) { + if (eventTethysDataProvider == null) { + eventTethysDataProvider = new ClickEventTethysDataProvider(tethysControl, this); + } + return eventTethysDataProvider; + } + + @Override + public DataAutomationInfo getDataAutomationInfo() { + return new DataAutomationInfo(DataAutomation.MANUALANDAUTOMATIC); + } + } diff --git a/src/clickDetector/tethys/ClickEventSpeciesManager.java b/src/clickDetector/tethys/ClickEventSpeciesManager.java new file mode 100644 index 00000000..1e16a4b1 --- /dev/null +++ b/src/clickDetector/tethys/ClickEventSpeciesManager.java @@ -0,0 +1,53 @@ +package clickDetector.tethys; + +import java.util.Vector; + +import PamguardMVC.PamDataUnit; +import clickDetector.ClickControl; +import clickDetector.ClickDetector; +import clickDetector.offlineFuncs.ClicksOffline; +import clickDetector.offlineFuncs.OfflineEventDataBlock; +import clickDetector.offlineFuncs.OfflineEventDataUnit; +import generalDatabase.lookupTables.LookUpTables; +import generalDatabase.lookupTables.LookupItem; +import generalDatabase.lookupTables.LookupList; +import tethys.species.DataBlockSpeciesCodes; +import tethys.species.DataBlockSpeciesManager; + +public class ClickEventSpeciesManager extends DataBlockSpeciesManager { + + private OfflineEventDataBlock eventDataBlock; + private ClickDetector clickDetector; + private ClickControl clickControl; + private ClicksOffline clicksOffline; + + public ClickEventSpeciesManager(ClickDetector clickDetector, OfflineEventDataBlock eventDataBlock) { + super(eventDataBlock); + this.clickDetector = clickDetector; + this.eventDataBlock = eventDataBlock; + clickControl = clickDetector.getClickControl(); + clicksOffline = clickControl.getClicksOffline(); + } + + @Override + public DataBlockSpeciesCodes getSpeciesCodes() { + LookupList lutList = LookUpTables.getLookUpTables().getLookupList(ClicksOffline.ClickTypeLookupName); + if (lutList == null || lutList.getLutList().size() == 0) { + return new DataBlockSpeciesCodes("Unknown"); + } + Vector spList = lutList.getLutList(); + String[] spNames = new String[spList.size()]; + int i = 0; + for (LookupItem lItem : spList) { + spNames[i++] = lItem.getCode(); + } + return new DataBlockSpeciesCodes("Unknown", spNames); + } + + @Override + public String getSpeciesCode(PamDataUnit dataUnit) { + OfflineEventDataUnit eventDataUnit = (OfflineEventDataUnit) dataUnit; + return eventDataUnit.getEventType(); + } + +} diff --git a/src/clickDetector/tethys/ClickEventTethysDataProvider.java b/src/clickDetector/tethys/ClickEventTethysDataProvider.java new file mode 100644 index 00000000..bbc5d1fd --- /dev/null +++ b/src/clickDetector/tethys/ClickEventTethysDataProvider.java @@ -0,0 +1,72 @@ +package clickDetector.tethys; + +import java.math.BigInteger; + +import PamguardMVC.PamDataUnit; +import clickDetector.offlineFuncs.OfflineEventDataBlock; +import clickDetector.offlineFuncs.OfflineEventDataUnit; +import nilus.Detection; +import nilus.GranularityEnumType; +import nilus.Detection.Parameters; +import nilus.Detection.Parameters.UserDefined; +import tethys.TethysControl; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; +import tethys.pamdata.AutoTethysProvider; +import tethys.swing.export.ExportWizardCard; +import tethys.swing.export.GranularityCard; + +public class ClickEventTethysDataProvider extends AutoTethysProvider { + + private OfflineEventDataBlock eventDataBlock; + + public ClickEventTethysDataProvider(TethysControl tethysControl, OfflineEventDataBlock eventDataBlock) { + super(tethysControl, eventDataBlock); + this.eventDataBlock = eventDataBlock; + } + + @Override + public GranularityEnumType[] getAllowedGranularities() { + GranularityEnumType[] allowed = {GranularityEnumType.GROUPED}; + return allowed; + } + @Override + public Detection createDetection(PamDataUnit dataUnit, TethysExportParams tethysExportParams, + StreamExportParams streamExportParams) { + Detection detection = super.createDetection(dataUnit, tethysExportParams, streamExportParams); + if (detection == null) { + return null; + } + OfflineEventDataUnit eventDataUnit = (OfflineEventDataUnit) dataUnit; + detection.setCount(BigInteger.valueOf(eventDataUnit.getSubDetectionsCount())); + String comment = eventDataUnit.getComment(); + if (comment != null && comment.length() > 0) { + detection.setComment(comment); + } + Parameters params = detection.getParameters(); + addUserNumber(params, "MinNumber", eventDataUnit.getMinNumber()); + addUserNumber(params, "BestNumber", eventDataUnit.getBestNumber()); + addUserNumber(params, "MaxNumber", eventDataUnit.getMaxNumber()); + + + return detection; + } + + private void addUserNumber(Parameters params, String numName, Short number) { + if (number == null) { + return; + } + addUserDefined(params, numName, number.toString()); + } + + @Override + public boolean wantExportDialogCard(ExportWizardCard wizPanel) { + if (wizPanel.getClass() == GranularityCard.class) { + return false; + } + else { + return true; + } + } + +} diff --git a/src/clickDetector/tethys/ClickTethysDataProvider.java b/src/clickDetector/tethys/ClickTethysDataProvider.java new file mode 100644 index 00000000..44e1c8c8 --- /dev/null +++ b/src/clickDetector/tethys/ClickTethysDataProvider.java @@ -0,0 +1,22 @@ +package clickDetector.tethys; + +import clickDetector.ClickDataBlock; +import nilus.GranularityEnumType; +import tethys.TethysControl; +import tethys.pamdata.AutoTethysProvider; + +public class ClickTethysDataProvider extends AutoTethysProvider { + + private ClickDataBlock clickDataBlock; + + public ClickTethysDataProvider(TethysControl tethysControl, ClickDataBlock clickDataBlock) { + super(tethysControl, clickDataBlock); + this.clickDataBlock = clickDataBlock; + } + + @Override + public GranularityEnumType[] getAllowedGranularities() { + return GranularityEnumType.values(); // everything ! + } + +} diff --git a/src/generalDatabase/SQLTypes.java b/src/generalDatabase/SQLTypes.java index a81edd89..fd8ccb56 100644 --- a/src/generalDatabase/SQLTypes.java +++ b/src/generalDatabase/SQLTypes.java @@ -387,6 +387,7 @@ public class SQLTypes { return timestamp.getTime() + tz.getOffset(timestamp.getTime()); } else if (timeValue instanceof String) { + timeValue = ((String) timeValue).replace("'", ""); return PamCalendar.millisFromDateString((String) timeValue, false); } if (timeValue instanceof Long) { diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 0d910f87..10f95acf 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -2,6 +2,7 @@ package tethys.deployment; import java.math.BigInteger; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; @@ -302,7 +303,7 @@ public class DeploymentHandler implements TethysStateObserver { // do the lot, whatever ... selectedDeployments = getDeploymentOverview().getRecordingPeriods(); int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId(); - RecordingPeriod onePeriod = new RecordingPeriod(selectedDeployments.get(freeId).getRecordStart(), + RecordingPeriod onePeriod = new RecordingPeriod(selectedDeployments.get(0).getRecordStart(), selectedDeployments.get(selectedDeployments.size()-1).getRecordStop()); Deployment deployment = createDeploymentDocument(freeId, onePeriod); // fill in a few things from here @@ -487,7 +488,15 @@ public class DeploymentHandler implements TethysStateObserver { ons[i] = tempPeriods.get(i).getDuration()/1000.; gaps[i] = (tempPeriods.get(i+1).getRecordStart()-tempPeriods.get(i).getRecordStop())/1000.; } - // now look at how consistent those values are + /* now look at how consistent those values are + * But some data gets messed by small gaps, so want to + * remove outliers and concentrate on say 80% of the data. + */ + ons = getDistributionCentre(ons, 80); + gaps = getDistributionCentre(gaps, 80); + Arrays.sort(gaps); + + STD std = new STD(); double onsMean = std.getMean(ons); double onsSTD = std.getSTD(ons); @@ -497,6 +506,27 @@ public class DeploymentHandler implements TethysStateObserver { DutyCycleInfo cycleInfo = new DutyCycleInfo(dutyCycle, onsMean, gapsMean, tempPeriods.size()); return cycleInfo; } + + /** + * Get the central part of a distribution without any outliers so + * that we can get a better assessment of duty cycle. + * @param data unsorted distribution data. + * @param percent percentage to include (half this removed from top and bottom) + * @return + */ + private double[] getDistributionCentre(double[] data, double percent) { + if (data == null) { + return null; + } + Arrays.sort(data); + int nRem = (int) Math.round(data.length * (100-percent)/200); + int newLen = data.length-nRem*2; + double[] subdata = Arrays.copyOfRange(data, nRem, data.length-2*nRem); + if (subdata.length < 2) { + return data; + } + return subdata; + } private ArrayList extractTimesFromStatus(ArrayList allStatusData) { ArrayList tempPeriods = new ArrayList<>(); diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 9d0fbb10..a28ce7e3 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -14,6 +14,8 @@ import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamProcess; import PamguardMVC.dataSelector.DataSelector; +import PamguardMVC.superdet.SuperDetDataBlock; +import PamguardMVC.superdet.SuperDetDataBlock.ViewerLoadPolicy; import dataMap.OfflineDataMap; import dataMap.OfflineDataMapPoint; import nilus.AlgorithmType; @@ -25,6 +27,7 @@ import nilus.DetectionEffort; import nilus.DetectionEffortKind; import nilus.DetectionGroup; import nilus.Detections; +import nilus.GranularityEnumType; import nilus.Helper; import tethys.TethysControl; import tethys.TethysTimeFuncs; @@ -337,12 +340,42 @@ public class DetectionsHandler { * @param exportWorkerCard */ public void startExportThread(PamDataBlock pamDataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { + checkGranularity(pamDataBlock, streamExportParams); tethysControl.getTethysExportParams().setStreamParams(pamDataBlock, streamExportParams); activeExport = true; exportWorker = new ExportWorker(pamDataBlock, streamExportParams, exportObserver); exportWorker.execute(); } + /** + * Fudge because some outputs don't show the granularity card, but need to + * make sure that it's set to the correct only option .. + * @param pamDataBlock + * @param streamExportParams + */ + private void checkGranularity(PamDataBlock pamDataBlock, StreamExportParams streamExportParams) { + if (streamExportParams == null) { + return; + } + TethysDataProvider tethysProvider = pamDataBlock.getTethysDataProvider(tethysControl); + if (tethysProvider == null) return; + GranularityEnumType[] allowed = tethysProvider.getAllowedGranularities(); + if (allowed == null || allowed.length == 0) { + return; + } + for (int i = 0; i < allowed.length; i++) { + if (allowed[i] == streamExportParams.granularity) { + return; // matches allowed value, so OK + } + } + /* + * if we get here, it's all wrong, so set to the first allowed value + * which will be the only one if the card wasn't shown + */ + streamExportParams.granularity = allowed[0]; + } + + public void cancelExport() { activeExport = false; } @@ -382,6 +415,14 @@ public class DetectionsHandler { int exportCount = 0; long lastUnitTime = 0; DetectionExportProgress prog; + ViewerLoadPolicy viewerLoadPolicy = ViewerLoadPolicy.LOAD_UTCNORMAL; + if (dataBlock instanceof SuperDetDataBlock) { + SuperDetDataBlock superDataBlock = (SuperDetDataBlock) dataBlock; + viewerLoadPolicy = superDataBlock.getViewerLoadPolicy(); + } + if (viewerLoadPolicy == null) { + viewerLoadPolicy = ViewerLoadPolicy.LOAD_UTCNORMAL; + } GranularityHandler granularityHandler = GranularityHandler.getHandler(streamExportParams.granularity, tethysControl, dataBlock, exportParams, streamExportParams); for (PDeployment deployment : deployments) { int documentCount = 0; @@ -392,6 +433,7 @@ public class DetectionsHandler { // export everything in that deployment. // need to loop through all map points in this interval. List mapPoints = dataMap.getMapPoints(); + for (OfflineDataMapPoint mapPoint : mapPoints) { if (!activeExport) { prog = new DetectionExportProgress(deployment, null, @@ -418,6 +460,12 @@ public class DetectionsHandler { if (dets != null) { exportCount+=dets.length; documentCount+=dets.length; + + if (exportCount % 100 == 0) { + prog = new DetectionExportProgress(deployment, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING); + exportObserver.update(prog); + } } // Detection det = dataProvider.createDetection(dataUnit, exportParams, streamExportParams); // exportCount++; @@ -429,6 +477,10 @@ public class DetectionsHandler { prog = new DetectionExportProgress(deployment, null, lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING); exportObserver.update(prog); + + if (viewerLoadPolicy == ViewerLoadPolicy.LOAD_ALWAYS_EVERYTHING) { + break; + } // if (documentCount > 500000 && mapPoint != dataMap.getLastMapPoint()) { // prog = new DetectionExportProgress(deployment, currentDetections, @@ -480,11 +532,19 @@ public class DetectionsHandler { int exportCount = 0; long lastUnitTime = 0; DetectionExportProgress prog; + ViewerLoadPolicy viewerLoadPolicy = ViewerLoadPolicy.LOAD_UTCNORMAL; + if (dataBlock instanceof SuperDetDataBlock) { + SuperDetDataBlock superDataBlock = (SuperDetDataBlock) dataBlock; + viewerLoadPolicy = superDataBlock.getViewerLoadPolicy(); + } + if (viewerLoadPolicy == null) { + viewerLoadPolicy = ViewerLoadPolicy.LOAD_UTCNORMAL; + } GranularityHandler granularityHandler = GranularityHandler.getHandler(streamExportParams.granularity, tethysControl, dataBlock, exportParams, streamExportParams); for (PDeployment deployment : deployments) { int documentCount = 0; prog = new DetectionExportProgress(deployment, null, - lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COUNTING); exportObserver.update(prog); granularityHandler.prepare(deployment.getAudioStart()); // export everything in that deployment. @@ -523,10 +583,11 @@ public class DetectionsHandler { onEffort.getDetection().add(dets[dd]); } } -// Detection det = dataProvider.createDetection(dataUnit, exportParams, streamExportParams); -// exportCount++; -// documentCount++; -// onEffort.getDetection().add(det); + if (exportCount % 100 == 0) { + prog = new DetectionExportProgress(deployment, null, + lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); + exportObserver.update(prog); + } lastUnitTime = dataUnit.getTimeMilliseconds(); } @@ -546,6 +607,10 @@ public class DetectionsHandler { } currentDetections = null; } + + if (viewerLoadPolicy == ViewerLoadPolicy.LOAD_ALWAYS_EVERYTHING) { + break; + } } diff --git a/src/tethys/detection/GroupedGranularityHandler.java b/src/tethys/detection/GroupedGranularityHandler.java index 298913a3..93dbbd99 100644 --- a/src/tethys/detection/GroupedGranularityHandler.java +++ b/src/tethys/detection/GroupedGranularityHandler.java @@ -7,30 +7,26 @@ import tethys.TethysControl; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; -public class GroupedGranularityHandler extends GranularityHandler { +public class GroupedGranularityHandler extends CallGranularityHandler { public GroupedGranularityHandler(TethysControl tethysControl, PamDataBlock dataBlock, TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { super(tethysControl, dataBlock, tethysExportParams, streamExportParams); - // TODO Auto-generated constructor stub } @Override public void prepare(long timeMillis) { - // TODO Auto-generated method stub - + super.prepare(timeMillis); } @Override public Detection[] addDataUnit(PamDataUnit dataUnit) { - // TODO Auto-generated method stub - return null; + return super.addDataUnit(dataUnit); } @Override public Detection[] cleanup(long timeMillis) { - // TODO Auto-generated method stub - return null; + return super.cleanup(timeMillis); } } diff --git a/src/tethys/output/TethysExportParams.java b/src/tethys/output/TethysExportParams.java index 7e93fe16..89d9a43f 100644 --- a/src/tethys/output/TethysExportParams.java +++ b/src/tethys/output/TethysExportParams.java @@ -17,7 +17,7 @@ public class TethysExportParams implements Serializable, Cloneable{ public static final long serialVersionUID = 1L; /* - * Need to add lots of other parameters here, such as the connection detils + * Need to add lots of other parameters here, such as the connection details * for the tethys database. */ public String serverName = "http://localhost"; diff --git a/src/tethys/output/swing/TethysExportDialog.java b/src/tethys/output/swing/TethysExportDialog.java index da63db50..91302405 100644 --- a/src/tethys/output/swing/TethysExportDialog.java +++ b/src/tethys/output/swing/TethysExportDialog.java @@ -63,8 +63,8 @@ public class TethysExportDialog extends PamDialog { setResizable(true); } - - public static TethysExportParams showDialog(Window parentFrame, TethysControl tethysControl) { + @Deprecated + private static TethysExportParams showDialog(Window parentFrame, TethysControl tethysControl) { if (singleInstance == null || singleInstance.getOwner() != parentFrame || singleInstance.tethysControl != tethysControl) { singleInstance = new TethysExportDialog(parentFrame, tethysControl); } diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index bda6fa70..81c95644 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -350,7 +350,7 @@ abstract public class AutoTethysProvider implements TethysDataProvider { nilus.Detection.Parameters detParams = new nilus.Detection.Parameters(); detection.setParameters(detParams); double[] freqs = dataUnit.getFrequency(); - if (freqs != null) { + if (freqs != null && freqs[1] != 0) { detParams.setMinFreqHz(freqs[0]); detParams.setMaxFreqHz(freqs[1]); } @@ -366,11 +366,14 @@ abstract public class AutoTethysProvider implements TethysDataProvider { el.setAttribute("BinaryFile", fileInf.getShortFileName(2048)); el.setAttribute("FileIndex", Long.valueOf(fileInf.getIndexInFile()).toString()); } + if (dataUnit.getDatabaseIndex() >= 0) { + addUserDefined(detParams, "DatabaseId", String.format("%d", dataUnit.getDatabaseIndex())); + } return detection; } - private Element addUserDefined(Parameters parameters, String parameterName, String parameterValue) { + public Element addUserDefined(Parameters parameters, String parameterName, String parameterValue) { UserDefined userDefined = parameters.getUserDefined(); if (userDefined == null) { userDefined = new UserDefined(); diff --git a/src/tethys/swing/DatablockSynchPanel.java b/src/tethys/swing/DatablockSynchPanel.java index 60281b76..8f1a220f 100644 --- a/src/tethys/swing/DatablockSynchPanel.java +++ b/src/tethys/swing/DatablockSynchPanel.java @@ -183,7 +183,7 @@ public class DatablockSynchPanel extends TethysGUIPanel { OfflineDataMap dataMap = synchInfo.getDataBlock().getPrimaryDataMap(); switch (columnIndex) { case 0: - return synchInfo.getDataBlock().getDataName(); + return synchInfo.getDataBlock().getLongDataName(); case 1: if (dataMap == null) { return null; diff --git a/src/tethys/swing/DetectionsExportPanel.java b/src/tethys/swing/DetectionsExportPanel.java index 869cdc15..b790e7f8 100644 --- a/src/tethys/swing/DetectionsExportPanel.java +++ b/src/tethys/swing/DetectionsExportPanel.java @@ -53,7 +53,7 @@ public class DetectionsExportPanel extends TethysGUIPanel implements StreamTable if (selectedDataBlock == null) { return; } - DetectionsExportWizard.showDilaog(getTethysControl().getGuiFrame(), getTethysControl(), selectedDataBlock); + DetectionsExportWizard.showDialog(getTethysControl().getGuiFrame(), getTethysControl(), selectedDataBlock); } @Override diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index 2e71784e..9973d3c3 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -82,7 +82,7 @@ public class DetectionsExportWizard extends PamDialog { } } - public static void showDilaog(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { + public static void showDialog(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { DetectionsExportWizard wiz = new DetectionsExportWizard(parentFrame, tethysControl, dataBlock); wiz.setParams(); wiz.setVisible(true); diff --git a/src/tethys/swing/export/ExportWorkerCard.java b/src/tethys/swing/export/ExportWorkerCard.java index f8400587..1cc44e79 100644 --- a/src/tethys/swing/export/ExportWorkerCard.java +++ b/src/tethys/swing/export/ExportWorkerCard.java @@ -103,6 +103,7 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor protected void exportData() { DetectionsHandler detHandler = getTethysControl().getDetectionsHandler(); + detHandler.startExportThread(getDataBlock(), streamExportParams, this); enableControls(DetectionExportProgress.STATE_GATHERING); } @@ -128,7 +129,14 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor if (progress == null) { return; } - if (progress.totalUnits > 0) { + if (progress.state == DetectionExportProgress.STATE_COUNTING) { + itemCount.setText("0"); + projectedCount.setText(String.format("%d", progress.exportCount)); + skipCount.setText(String.format("%d", progress.skipCount)); + long perc = (progress.exportCount+progress.skipCount) * 100 / progress.totalUnits; + progressBar.setValue((int) perc); + } + else if (progress.totalUnits > 0) { itemCount.setText(String.format("%d", progress.exportCount)); skipCount.setText(String.format("%d", progress.skipCount)); long totExpected = progress.totalUnits; @@ -136,7 +144,6 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor totExpected *= progress.exportCount/(progress.exportCount+progress.skipCount); } projectedCount.setText(String.format("%d", totExpected)); - itemCount.setText(String.format("%d", totExpected)); long perc = (progress.exportCount+progress.skipCount) * 100 / progress.totalUnits; progressBar.setValue((int) perc); } From 8257122d140f1834cfbb2dfdf8d223a385a0515c Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:01:41 +0100 Subject: [PATCH 54/65] Log Tethys output and include instrumentid in Deployment queries --- src/tethys/database/TethysActions.java | 27 ++ src/tethys/database/TethysLogDataBlock.java | 16 ++ src/tethys/database/TethysLogDataUnit.java | 59 +++++ src/tethys/database/TethysLogger.java | 128 ++++++++++ src/tethys/dbxml/DBXMLConnect.java | 26 +- src/tethys/dbxml/DBXMLQueries.java | 26 ++ src/tethys/deployment/DeploymentHandler.java | 6 +- src/tethys/detection/DetectionsHandler.java | 233 ++++-------------- src/tethys/output/TethysExporter.java | 2 +- src/tethys/pamdata/AutoTethysProvider.java | 96 +------- src/tethys/pamdata/TethysDataPoint.java | 18 -- src/tethys/pamdata/TethysParameterPacker.java | 40 +-- src/tethys/pamdata/TethysSchema.java | 1 + 13 files changed, 354 insertions(+), 324 deletions(-) create mode 100644 src/tethys/database/TethysActions.java create mode 100644 src/tethys/database/TethysLogDataBlock.java create mode 100644 src/tethys/database/TethysLogDataUnit.java create mode 100644 src/tethys/database/TethysLogger.java delete mode 100644 src/tethys/pamdata/TethysDataPoint.java diff --git a/src/tethys/database/TethysActions.java b/src/tethys/database/TethysActions.java new file mode 100644 index 00000000..0c9ece36 --- /dev/null +++ b/src/tethys/database/TethysActions.java @@ -0,0 +1,27 @@ +package tethys.database; + +/** + * Possible document actions + * @author dg50 + * + */ +public enum TethysActions { + + ADDDOCUMENT, DELETEDOCUMENT, UPDATEDOCUMENT; + +// @Override +// public String toString() { +// switch (this) { +// case ADDDOCUMENT: +// return "Add document"; +// case DELETEDOCUMENT: +// return "Delete document"; +// case UPDATEDOCUMENT: +// return "Update document"; +// default: +// return null; +// } +// } + + +} diff --git a/src/tethys/database/TethysLogDataBlock.java b/src/tethys/database/TethysLogDataBlock.java new file mode 100644 index 00000000..7a3639d6 --- /dev/null +++ b/src/tethys/database/TethysLogDataBlock.java @@ -0,0 +1,16 @@ +package tethys.database; + +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamProcess; +import tethys.TethysControl; + +public class TethysLogDataBlock extends PamDataBlock { + + private TethysControl tethysControl; + + public TethysLogDataBlock(TethysControl tethysControl) { + super(TethysLogDataUnit.class, "Tethys Log", null, 0); + this.tethysControl = tethysControl; + } + +} diff --git a/src/tethys/database/TethysLogDataUnit.java b/src/tethys/database/TethysLogDataUnit.java new file mode 100644 index 00000000..e1998c2b --- /dev/null +++ b/src/tethys/database/TethysLogDataUnit.java @@ -0,0 +1,59 @@ +package tethys.database; + +import PamguardMVC.PamDataUnit; + +public class TethysLogDataUnit extends PamDataUnit { + + private String collection; + private String documentId; + private TethysActions action; + private String comment; + private boolean success; + + public TethysLogDataUnit(long timeMilliseconds, String collection, String documentId, TethysActions action, boolean success, String comment) { + super(timeMilliseconds); + this.collection = collection; + this.documentId = documentId; + this.action = action; + this.success = success; + this.comment = comment; + + } + + /** + * @return the collection + */ + public String getCollection() { + return collection; + } + + /** + * @return the documentId + */ + public String getDocumentId() { + return documentId; + } + + /** + * @return the action + */ + public TethysActions getAction() { + return action; + } + + /** + * @return the comment + */ + public String getComment() { + return comment; + } + + /** + * @return the success + */ + public boolean isSuccess() { + return success; + } + + +} diff --git a/src/tethys/database/TethysLogger.java b/src/tethys/database/TethysLogger.java new file mode 100644 index 00000000..9e7ae2ad --- /dev/null +++ b/src/tethys/database/TethysLogger.java @@ -0,0 +1,128 @@ +package tethys.database; + +import java.sql.Types; + +import PamguardMVC.PamDataUnit; +import generalDatabase.DBControlUnit; +import generalDatabase.DBProcess; +import generalDatabase.PamConnection; +import generalDatabase.PamTableDefinition; +import generalDatabase.PamTableItem; +import generalDatabase.SQLLogging; +import generalDatabase.SQLTypes; +import tethys.TethysControl; + +/** + * Logging everything we put into Tethys in our own database. + * @author dg50 + * + */ +public class TethysLogger extends SQLLogging { + + private static TethysLogger tethysLogger; + + private TethysControl tethysControl; + + private TethysLogDataBlock logDataBlock; + + private PamTableDefinition tableDefinition; + + private PamTableItem collection, documentId, action, status, comment; + + private boolean tableChecked = false; + + private TethysLogger(TethysControl tethysControl, TethysLogDataBlock pamDataBlock) { + super(pamDataBlock); + this.tethysControl = tethysControl; + this.logDataBlock = pamDataBlock; + tableDefinition = new PamTableDefinition("TethysLog"); + tableDefinition.addTableItem(collection = new PamTableItem("Collection", Types.VARCHAR)); + tableDefinition.addTableItem(documentId = new PamTableItem("DocumentId", Types.VARCHAR)); + tableDefinition.addTableItem(action = new PamTableItem("Action", Types.VARCHAR)); + tableDefinition.addTableItem(status = new PamTableItem("Status", Types.VARCHAR)); + tableDefinition.addTableItem(comment = new PamTableItem("Comment", Types.VARCHAR)); + tableDefinition.setUpdatePolicy(UPDATE_POLICY_OVERWRITE); + setTableDefinition(tableDefinition); + } + + public static TethysLogger getTethysLogger(TethysControl tethysControl) { + if (tethysLogger == null) { + tethysLogger = createTethysLogger(tethysControl); + } + return tethysLogger; + } + + private boolean checkTable() { + if (tableChecked == true) { + return true; + } + if (findDBProcess() == null) { + return false; + } + else { + tableChecked = findDBProcess().checkTable(tableDefinition); + } + return tableChecked; + } + + public boolean logAction(String collection, String documentId, TethysActions action, boolean success, String comment) { + PamConnection con = findDBConnection(); + if (con == null) { + return false; + } + if (checkTable() == false) { + return false; + } + + TethysLogDataUnit dataUnit = new TethysLogDataUnit(System.currentTimeMillis(), collection, documentId, action, success, comment); + return this.logData(con, dataUnit); + } + + private PamConnection findDBConnection() { + return DBControlUnit.findConnection(); + } + + /** + * Find the database controlled unit.
Must exist in viewer mode surely, but perhaps + * created after the Tethys module if the user is really crafty ! + * @return the DB controlled unit. + */ + private DBControlUnit findDBControl() { + return DBControlUnit.findDatabaseControl(); + } + + /** + * Fine the database process. Should exist. + * @return + */ + private DBProcess findDBProcess() { + DBControlUnit dbControl = findDBControl(); + if (dbControl == null) { + return null; + } + return dbControl.getDbProcess(); + } + + private static TethysLogger createTethysLogger(TethysControl tethysControl) { + TethysLogDataBlock datablock = new TethysLogDataBlock(tethysControl); + TethysLogger newLogger = new TethysLogger(tethysControl, datablock); + return newLogger; + } + + + @Override + public void setTableData(SQLTypes sqlTypes, PamDataUnit pamDataUnit) { + TethysLogDataUnit tldu = (TethysLogDataUnit) pamDataUnit; + collection.setValue(tldu.getCollection()); + documentId.setValue(tldu.getDocumentId()); + action.setValue(tldu.getAction().toString()); + status.setValue(tldu.isSuccess() ? "Success" : "Fail"); + comment.setValue(tldu.getComment()); + } + +// public TethysLogger(TethysControl tethysControl) { +// this.tethysControl = tethysControl; +// } + + +} diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index 7af700d5..aee6dc19 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -19,6 +19,8 @@ import dbxml.Queries; import dbxml.uploader.Importer; import nilus.MarshalXML; import tethys.TethysControl; +import tethys.database.TethysActions; +import tethys.database.TethysLogger; import tethys.output.TethysExportParams; /** @@ -123,6 +125,28 @@ public class DBXMLConnect { return retFile; } + + public boolean postAndLog(Object nilusObject) throws TethysException + { + TethysException e = null; + boolean success = false; + try { + success = postToTethys(nilusObject); + } + catch (TethysException ex) { + e = ex; + } + TethysLogger logger = TethysLogger.getTethysLogger(tethysControl); + Class objClass = nilusObject.getClass(); + String collection = getTethysCollection(objClass.getName()); + String documentId = getDocumentId(nilusObject); + logger.logAction(collection, documentId, TethysActions.ADDDOCUMENT, success, ""); + if (e != null) { + throw (e); + } + return success; + } + /** * take a nilus object loaded with PamGuard data and post it to the Tethys database * @@ -130,7 +154,7 @@ public class DBXMLConnect { * @return error string, null string means there are no errors * @throws TethysException */ - public boolean postToTethys(Object nilusObject) throws TethysException + private boolean postToTethys(Object nilusObject) throws TethysException { Class objClass = nilusObject.getClass(); String collection = getTethysCollection(objClass.getName()); diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 48dd2b7b..5e6fe572 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -287,6 +287,23 @@ public class DBXMLQueries { return projectNames; } + /** + * Get project deployments that use a specific instrument id. More use than the call without this + * extra clause since it can handle overlapping deployments. + * @param projectName + * @param instrumentId + * @return + */ + public ArrayList getProjectDeployments(String projectName, String instrumentId) { + if (projectName == null) { + return null; + } + String qBase = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"%s\"],\"optype\":\"binary\"},{\"op\":\"=\"," + + "\"operands\":[\"Deployment/Instrument/InstrumentId\",\"%s\"],\"optype\":\"binary\"}],\"enclose\":1}"; + String qStr = String.format(qBase, projectName, instrumentId); + + return runProjectDeploymentsQuery(projectName, qStr); + } /** * Get some basic (not all) data for deployments associated with a project. Note that * this may include deployments which are NOT part of the current dataset. That requires @@ -300,7 +317,16 @@ public class DBXMLQueries { } String qBase = "{\"return\":[\"Deployment\"],\"select\":[{\"op\":\"=\",\"operands\":[\"Deployment/Project\",\"%s\"],\"optype\":\"binary\"}],\"enclose\":1}"; String qStr = String.format(qBase, projectName); + return runProjectDeploymentsQuery(projectName, qStr); + } + /** + * Run the actual projects query from either of the two above functions. + * @param projectName + * @param qStr + * @return + */ + private ArrayList runProjectDeploymentsQuery(String projectName, String qStr) { DBQueryResult result = null; try { result = executeQuery(qStr); diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 10f95acf..16b63fdb 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -123,7 +123,7 @@ public class DeploymentHandler implements TethysStateObserver { */ public boolean updateProjectDeployments() { DeploymentData projData = tethysControl.getGlobalDeplopymentData(); - ArrayList tethysDocs = tethysControl.getDbxmlQueries().getProjectDeployments(projData.getProject()); + ArrayList tethysDocs = tethysControl.getDbxmlQueries().getProjectDeployments(projData.getProject(), getInstrumentId()); if (tethysDocs == null) { return false; } @@ -326,7 +326,7 @@ public class DeploymentHandler implements TethysStateObserver { dbxmlConnect.updateDocument(deployment); } else { - dbxmlConnect.postToTethys(deployment); + dbxmlConnect.postAndLog(deployment); } } catch (TethysException e) { @@ -363,7 +363,7 @@ public class DeploymentHandler implements TethysStateObserver { dbxmlConnect.updateDocument(deployment); } else { - dbxmlConnect.postToTethys(deployment); + dbxmlConnect.postAndLog(deployment); } } catch (TethysException e) { diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index a28ce7e3..659b9c24 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -41,9 +41,15 @@ import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.pamdata.TethysDataProvider; +/** + * Functions for handling output of Detections documents. + * Works closely with a TethysDataProvider and DataBlockSpeciesManager + * to generate Detections elements for an xml doc to export to Tethys. + * @author dg50 + * + */ public class DetectionsHandler { - private TethysControl tethysControl; public int uniqueDetectionsId=1; @@ -53,6 +59,10 @@ public class DetectionsHandler { private ExportWorker exportWorker; + /** + * + * @param tethysControl + */ public DetectionsHandler(TethysControl tethysControl) { super(); this.tethysControl = tethysControl; @@ -96,159 +106,13 @@ public class DetectionsHandler { return new StreamDetectionsSummary(detectionsDocs); } - -// /** -// * Here is where we export data for a specific data stream to Tethys. -// * -// * @param aDataBlock -// * @param aDeployment -// * @param tethysExportParams -// * @param streamExportParams -// */ -// public boolean exportDetections(PamDataBlock aDataBlock, Deployment deployment, DetectionGranularity granularity, TethysExportParams tethysExportParams, -// StreamExportParams streamExportParams) { -// if (granularity == null || granularity.granularity == null) { -// granularity = new DetectionGranularity(GRANULARITY.TIME, 3600); -// } -// switch (granularity.granularity) { -// case BINARYFILE: -// return exportByBinaryFile(aDataBlock, deployment, tethysExportParams, streamExportParams); -// case NONE: -// return exportEverything(aDataBlock, deployment, tethysExportParams, streamExportParams); -// case TIME: -// return exportByTimeChunk(aDataBlock, deployment, granularity.granularityIntervalSeconds, tethysExportParams, streamExportParams); -// default: -// break; -// } -// -// return false; -// -// -// } -// -// private boolean exportByBinaryFile(PamDataBlock dataBlock, Deployment deployment, -// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { -// long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); -// long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); -// /* -// * there should be a pretty good correspondence between the start of a binary file and the deploymentStart -// * since they all derived from the same start clock. -// */ -// OfflineDataMap dataMap = dataBlock.getPrimaryDataMap(); -// if (dataMap == null) { -// return false; -// } -// List mapPoints = dataMap.getMapPoints(); -// boolean ok = true; -// for (OfflineDataMapPoint mapPoint : mapPoints) { -// if (mapPoint.getEndTime() < deploymentStart) { -// continue; -// } -// if (mapPoint.getStartTime() >= deploymentStop) { -// continue; -// } -// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, mapPoint.getStartTime()), -// Math.min(deploymentStop, mapPoint.getEndTime()), tethysExportParams, streamExportParams); -// } -// -// -// return ok; -// } -// -// private boolean exportEverything(PamDataBlock dataBlock, Deployment deployment, -// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { -// long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); -// long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); -// return loadAndExport(dataBlock, deployment, deploymentStart, deploymentStop, tethysExportParams, streamExportParams); -// } -// -// private boolean exportByTimeChunk(PamDataBlock dataBlock, Deployment deployment, long granularityIntervalSeconds, -// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { -// -// long deploymentStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); -// long deploymentStop = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getAudioTimeStamp()); -// long chunkMillis = granularityIntervalSeconds*1000; -// long exportStart = deploymentStart / chunkMillis; -// exportStart *= chunkMillis; -// boolean ok = true; -// while (exportStart < deploymentStop) { -// ok &= loadAndExport(dataBlock, deployment, Math.max(deploymentStart, exportStart), -// Math.min(deploymentStop, exportStart + chunkMillis), tethysExportParams, streamExportParams); -// exportStart += chunkMillis; -// } -// -// return ok; -// } -// -///** -// * Load and export data for a given time period. This may be a complete deployment, it may be a short section. Do as told ! -// * Hopefully data interval is small enough to hold all in memory - it needs to be if the document will fit in mempory, so should be OK -// * @param dataBlock -// * @param deployment -// * @param max -// * @param min -// * @param tethysExportParams -// * @param streamExportParams -// */ -// private boolean loadAndExport(PamDataBlock dataBlock, Deployment deployment, long startTimeMillis, long endTimeMillis, -// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { -// // load the data -// dataBlock.loadViewerData(startTimeMillis, endTimeMillis, null); -// DataSelector dataSelector = dataBlock.getDataSelector(tethysControl.getDataSelectName(), false); -// /* -// * for easier synching, get a copy of the data and also apply the data selector right away so that -// * we've a list of exactly the right data. -// */ -// ArrayList data = dataBlock.getDataCopy(startTimeMillis, endTimeMillis, true, dataSelector); -// /* -// * Here, make Detection object and add the DetectionEffort data. -// */ -// DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); -// TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(); -// Detections detections = new Detections(); -//// 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()); -//// dataSource.setEnsembleId(""); ToDo -// detections.setDataSource(dataSource); -// detections.setAlgorithm(dataProvider.getAlgorithm()); -// detections.setUserId("Unknown user"); -// detections.setEffort(getDetectorEffort(deployment, startTimeMillis, endTimeMillis)); -// DetectionGroup detectionGroup = new DetectionGroup(); -// detections.setOnEffort(detectionGroup); -// List detectionList = detectionGroup.getDetection(); -// for (int i = 0; i < data.size(); i++) { -// PamDataUnit dataUnit = data.get(i); -// Detection detection = dataProvider.createDetection(dataUnit, tethysExportParams, streamExportParams); -// if (detection != null) { -// detectionList.add(detection); -// } -// } -// System.out.printf("Exporting %d %s detections for time period %s to %s\n", detectionList.size(), dataBlock.getDataName(), -// detections.getEffort().getStart().toString(), detections.getEffort().getEnd().toString()); -// /* -// * We should now have a fully populated Detections object, so write it to the database -// * using functions in DBXMLConnect -// */ -// ArrayList detectionDocuments = new ArrayList(); -// detectionDocuments.add(detections); -// -//// tethysControl.getDbxmlConnect().postToTethys(detectionDocuments); // call whatever you need to call in here to write the Detections. -// -// -// return true; -// -// } - -// private boolean exportByTimeChunk(PamDataBlock aDataBlock, Deployment deployment, long granularityIntervalSeconds, -// TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { -// // TODO Auto-generated method stub -// return false; -// } - + /** + * Get the Detection Effort part of a Detections document + * @param pDeployment + * @param dataBlock + * @param exportParams + * @return + */ private DetectionEffort getDetectorEffort(PDeployment pDeployment, PamDataBlock dataBlock, StreamExportParams exportParams) { DetectionEffort effort = new DetectionEffort(); Deployment deployment = pDeployment.deployment; @@ -312,25 +176,24 @@ public class DetectionsHandler { } } + /** + * + * @param dataBlock + * @return default value is PAMGuard + */ public String getSupportSoftware(PamDataBlock dataBlock) { return "PAMGuard"; } + /** + * + * @param dataBlock + * @return PAMGuard version + */ public String getSupportSoftwareVersion(PamDataBlock dataBlock) { // should try to dig into the binary store and get the version from there. return PamguardVersionInfo.version; } -// /** -// * 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(); -// } /** * Detections will be exported in a separate worker thread since export may take some time and @@ -375,7 +238,9 @@ public class DetectionsHandler { streamExportParams.granularity = allowed[0]; } - + /** + * send a cancel command to export thread if it's running + */ public void cancelExport() { activeExport = false; } @@ -482,18 +347,6 @@ public class DetectionsHandler { break; } -// if (documentCount > 500000 && mapPoint != dataMap.getLastMapPoint()) { -// prog = new DetectionExportProgress(deployment, currentDetections, -// lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); -// exportObserver.update(prog); -// closeDetectionsDocument(currentDetections, mapPoint.getEndTime()); -// try { -// dbxmlConnect.postToTethys(currentDetections); -// } catch (TethysException e) { -// tethysControl.showException(e); -// } -// currentDetections = null; -// } } Detection dets[] = granularityHandler.cleanup(deployment.getAudioEnd()); if (dets != null) { @@ -504,9 +357,6 @@ public class DetectionsHandler { } -// prog = new DetectionExportProgress(null, null, -// lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_GATHERING); -// exportObserver.update(prog); return exportCount; }/** * Export detections in all deployments for this PAMGuard dataset. @@ -601,7 +451,7 @@ public class DetectionsHandler { exportObserver.update(prog); closeDetectionsDocument(currentDetections, mapPoint.getEndTime()); try { - dbxmlConnect.postToTethys(currentDetections); + dbxmlConnect.postAndLog(currentDetections); } catch (TethysException e) { tethysControl.showException(e); } @@ -627,7 +477,7 @@ public class DetectionsHandler { lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_WRITING); closeDetectionsDocument(currentDetections, deployment.getAudioEnd()); try { - dbxmlConnect.postToTethys(currentDetections); + dbxmlConnect.postAndLog(currentDetections); } catch (TethysException e) { tethysControl.showException(e); } @@ -640,6 +490,15 @@ public class DetectionsHandler { exportObserver.update(prog); return DetectionExportProgress.STATE_COMPLETE; } + + /** + * Start a new detections document for the deployment and datablock.
+ * Add all the standard information to the top of the Document + * @param deployment + * @param dataBlock + * @param exportParams + * @return new Detections document + */ private Detections startDetectionsDocument(PDeployment deployment, PamDataBlock dataBlock, StreamExportParams exportParams) { Detections detections = new Detections(); @@ -701,6 +560,14 @@ public class DetectionsHandler { detections.getEffort().setEnd(TethysTimeFuncs.xmlGregCalFromMillis(audioEnd)); } + /** + * Worker thread for exporting detections. + * Currently, it counts them first, then checks the user wants to export + * This requires going through the data twice, but may be sensible to avoid + * people outputting stupidly large documents. + * @author dg50 + * + */ private class ExportWorker extends SwingWorker implements DetectionExportObserver { private PamDataBlock dataBlock; diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index a3cc0def..7353f6ce 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -174,7 +174,7 @@ public class TethysExporter { // System.out.println(deployment.toString()); deploymentDocs.add(deployment); try { - tethysControl.getDbxmlConnect().postToTethys(deployment); + tethysControl.getDbxmlConnect().postAndLog(deployment); } catch (TethysException e) { tethysControl.showException(e); } diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 81c95644..0d7d46e8 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -60,9 +60,9 @@ import java.io.StringReader; import java.net.URISyntaxException; /** - * Automatically provides Tethys data based on the SQL database interface - * for a data block. does most of what needs to be done, though individual modules - * may want to override this, call the base createDetection function and then add a + * Automatically provides Tethys data for a PAMGuard datablock. + * Does most of what needs to be done, though individual modules + * will want to override this, call the base createDetection function and then add a * few more bespoke elements. * @author dg50 * @@ -86,25 +86,7 @@ abstract public class AutoTethysProvider implements TethysDataProvider { e.printStackTrace(); } } -// -// @Override -// public TethysSchema getSchema() { -// SQLLogging logging = pamDataBlock.getLogging(); -// if (logging == null) { -// return null; -// } -// DBSchemaWriter schemaWriter = new DBSchemaWriter(); -// Document doc = schemaWriter.generateDatabaseSchema(pamDataBlock, logging, logging.getTableDefinition()); -// TethysSchema schema = new TethysSchema(doc); -// return schema; -// } - - // @Override - // public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) { - // // TODO Auto-generated method stub - // return null; - // } - + @Override public DescriptionType getDescription(Deployment deployment, TethysExportParams tethysExportParams) { DescriptionType description = new DescriptionType(); @@ -155,7 +137,6 @@ abstract public class AutoTethysProvider implements TethysDataProvider { try { paramPacker = new TethysParameterPacker(tethysControl); } catch (JAXBException e) { - // TODO Auto-generated catch block e.printStackTrace(); } List genList = paramPacker.packParameters(pamDataBlock); @@ -164,70 +145,17 @@ abstract public class AutoTethysProvider implements TethysDataProvider { } paramList.addAll(genList); - // Document doc = XMLUtils.createBlankDoc(); - // PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); - // Element dummyEl = doc.createElement("MODULES"); - // doc.appendChild(dummyEl); - // PamSettings[] settingsObjs = getSettingsObjects(); - // if (settingsObjs == null) { - // return null; - // } - //// pamXMLWriter.setStaticNameSpace(TethysControl.xmlNameSpace); - // Element settingsEl = pamXMLWriter.writeUnitSettings(doc, dummyEl, pamSettings, settingsObjs); - // if (settingsEl == null) { - // return null; - // } - // - //// settingsEl = addNameSpaceToElements(doc, settingsEl, TethysControl.xmlNameSpace); - // - // - // dummyEl.appendChild(settingsEl); - // NodeList childs = settingsEl.getChildNodes(); - // for (int i = 0; i < childs.getLength(); i++) { - // Node el = childs.item(i); - // // System.out.println(el.getNodeName()); - // if (el instanceof Element) { - // paramList.add((Element) el); - // } - // } - // - // // Document doc = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); - // // String moduleXML = null; - // if (doc != null) { - // // this string should be XML of all the settings for the module controlling this - // // datablock. - // // moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml - // // System.out.printf("Module settings for datablock %s are:\n", moduleXML); - // // System.out.println(moduleXML); - // // Element pamguard = doc.get("PAMGUARD"); - // // Element modules = (Element) pamguard.getElementsByTagName("MODULES"); - // // doc.get - // // NodeList childs = doc.getChildNodes(); - // // for (int i = 0; i < childs.getLength(); i++) { - // // Node el = childs.item(i); - // // System.out.println(el.getNodeName()); - // // if (el instanceof Element) { - // // paramList.add((Element) el); - // // } - // // } - // // String moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml - // // System.out.printf("Module settings for datablock %s are:\n%s", this.pamDataBlock.getDataName(), moduleXML); - // } - // - // // // try the old say - // // Document doc2 = pamXMLWriter.writeOneModule((PamSettings) pamControlledUnit, System.currentTimeMillis()); - // // String moduleXML = null; - // // if (doc2 != null) { - // // // this string should be XML of all the settings for the module controlling this - // // // datablock. - // // moduleXML = pamXMLWriter.getAsString(doc2, true); // change to false to get smaller xml - // // System.out.printf("Module settings for datablock %s are:\n%s", pamDataBlock.getDataName(),moduleXML); - // // } - // // - return parameters; } + /** + * Not used. Was an attempt to automatically add name spaces to the PAMGuard settings + * XML I generate, but we found a better way. + * @param doc + * @param settingsEl + * @param xmlNameSpace + * @return + */ private Element addNameSpaceToElements(Document doc, Element settingsEl, String xmlNameSpace) { diff --git a/src/tethys/pamdata/TethysDataPoint.java b/src/tethys/pamdata/TethysDataPoint.java deleted file mode 100644 index 5eefdae1..00000000 --- a/src/tethys/pamdata/TethysDataPoint.java +++ /dev/null @@ -1,18 +0,0 @@ -package tethys.pamdata; - -import PamguardMVC.PamDataUnit; - -/** - * This will be a unit of Tethys Data, e.g. a Detection. - * Can it also be used for things like GPS data ? - * @author dg50 - * - */ -public class TethysDataPoint { - - public TethysDataPoint(PamDataUnit dataUnit) { - // TODO Auto-generated constructor stub - } - - -} diff --git a/src/tethys/pamdata/TethysParameterPacker.java b/src/tethys/pamdata/TethysParameterPacker.java index c4c7e6f7..892859e1 100644 --- a/src/tethys/pamdata/TethysParameterPacker.java +++ b/src/tethys/pamdata/TethysParameterPacker.java @@ -40,40 +40,6 @@ import tethys.TethysControl; */ public class TethysParameterPacker { -// /** -// * Parameters should look something like below. however, only packing them with a long -// schema name seems to work. -// * -// -//Analyst detections -//Triton -//unknown -// -//0.75 -//0.0 -//5000.0 -//30.0 -// -// - /* - * -// // this works. Can look at the source to see how it's done. -// // may have fun making this work for more complex structures. -// try { -// Helper helper = new Helper(); -// helper.AddAnyElement(paramList, "Threshold", "3.5"); -// -// * and see Matlab code for dbStruct2DOM at C:\Users\dg50\source\repos\TethysMatlab\db -// * for more complex structures -// * This looks like it may be possible to rewrite my functions for -// * writing structures to XML using the helper.AddAnyElement function as -// * an example and I should be able to output my complex structures. -// -// } catch (JAXBException | ParserConfigurationException e) { -// e.printStackTrace(); -// } - */ - private MarshalXML marshaller; private PamguardXMLWriter xmlWriter; @@ -94,6 +60,12 @@ public class TethysParameterPacker { xmlWriter = PamguardXMLWriter.getXMLWriter(); } + /** + * Get a list of elements of parameters for all modules feeding + * the given datablock. These are given in reverse order. + * @param pamDataBlock output datablock + * @return parameters of all modules feeding that datablock. + */ public List packParameters(PamDataBlock pamDataBlock) { PamProcess pamProcess = pamDataBlock.getParentProcess(); PamControlledUnit pamControlledUnit = pamProcess.getPamControlledUnit(); diff --git a/src/tethys/pamdata/TethysSchema.java b/src/tethys/pamdata/TethysSchema.java index 4ddb9b08..324260b8 100644 --- a/src/tethys/pamdata/TethysSchema.java +++ b/src/tethys/pamdata/TethysSchema.java @@ -7,6 +7,7 @@ import org.w3c.dom.Document; * wrapper around an XML string, or a JAXB object or something, * but may get more sophisticated. TBD in discussions with SDSU */ +@Deprecated public class TethysSchema { private Document schemaDoc; From 773f1f542b3d41842df6aa2cb77c857fa686705c Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sun, 24 Sep 2023 21:59:15 +0100 Subject: [PATCH 55/65] Species map i/o --- src/tethys/TethysControl.java | 13 ++ src/tethys/detection/DetectionsHandler.java | 4 + src/tethys/pamdata/AutoTethysProvider.java | 3 + .../species/DataBlockSpeciesManager.java | 25 +++ src/tethys/species/SpeciesMapManager.java | 150 +++++++++++++++++- .../species/swing/DataBlockSpeciesDialog.java | 16 ++ .../species/swing/DataBlockSpeciesPanel.java | 4 +- src/tethys/swing/DetectionsExportPanel.java | 17 ++ 8 files changed, 230 insertions(+), 2 deletions(-) diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index bd6e70f5..81270ce0 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -51,6 +51,7 @@ import tethys.output.TethysExportParams; import tethys.output.TethysExporter; import tethys.output.swing.TethysExportDialog; import tethys.species.ITISFunctions; +import tethys.species.SpeciesMapManager; import tethys.swing.ProjectDeploymentsDialog; import tethys.swing.TethysTabPanel; import tethys.swing.XMLStringView; @@ -200,6 +201,18 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } }); tethysMenu.add(showDeps); + + tethysMenu.addSeparator(); + JMenuItem mapItem = new JMenuItem("Export species maps ..."); + mapItem.setToolTipText("Export all species maps (PAMGuard codes to ITIS codes to file for import into other configurations"); + mapItem.addActionListener(SpeciesMapManager.getInstance().getExportAction(parentFrame)); + tethysMenu.add(mapItem); + + mapItem = new JMenuItem("Import species maps ..."); + mapItem.setToolTipText("Import species maps (PAMGuard codes to ITIS codes to file for import into other configurations"); + mapItem.addActionListener(SpeciesMapManager.getInstance().getImportAction(parentFrame)); + tethysMenu.add(mapItem); + return tethysMenu; } diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 659b9c24..7638c2d2 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -6,9 +6,11 @@ import java.util.List; import javax.swing.SwingWorker; import PamController.PamControlledUnit; +import PamController.PamController; import PamController.PamguardVersionInfo; import PamModel.PamPluginInterface; import PamUtils.PamCalendar; +import PamView.dialog.PamDialog; import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -40,6 +42,7 @@ import tethys.niluswraps.TethysCollections; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.pamdata.TethysDataProvider; +import tethys.species.DataBlockSpeciesManager; /** * Functions for handling output of Detections documents. @@ -203,6 +206,7 @@ public class DetectionsHandler { * @param exportWorkerCard */ public void startExportThread(PamDataBlock pamDataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { + checkGranularity(pamDataBlock, streamExportParams); tethysControl.getTethysExportParams().setStreamParams(pamDataBlock, streamExportParams); activeExport = true; diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 0d7d46e8..49ede503 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -247,6 +247,9 @@ abstract public class AutoTethysProvider implements TethysDataProvider { Detection detection = new Detection(); detection.setStart(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getTimeMilliseconds())); detection.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getEndTimeInMilliseconds())); + if (dataUnit.getEndTimeInMilliseconds() < dataUnit.getTimeMilliseconds()) { + System.out.printf("Error UID %d, end %s before start %s\n", dataUnit.getUID(), detection.getEnd(), detection.getStart()); + } DataBlockSpeciesManager speciesManager = pamDataBlock.getDatablockSpeciesManager(); SpeciesMapItem speciesItem = null; diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java index 6b08d0ce..35947776 100644 --- a/src/tethys/species/DataBlockSpeciesManager.java +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -27,6 +27,7 @@ abstract public class DataBlockSpeciesManager { /** * The serialised bit. Always exists (or should be created) even if there * are no real species, via a defaultdefaultSpecies. + * Don't keep a local copy though since it may have been */ private DataBlockSpeciesMap datablockSpeciesMap; @@ -194,4 +195,28 @@ abstract public class DataBlockSpeciesManager { public void setDefaultSpeciesCode(String defaultName) { this.defaultSpeciesCode = defaultName; } + + /** + * Check the species map. Only return true if every species code + * has a map item. Otherwise it's not safe to export. + * @return null if all codes have a lookup, otherwise some sort of useful error information + */ + public String checkSpeciesMapError() { + ArrayList codes = getAllSpeciesCodes(); + if (codes == null || codes.size() == 0) { + return "No defined species codes"; // I guess that's OK ? + } + DataBlockSpeciesMap spMap = getDatablockSpeciesMap(); + if (spMap == null) { + return "No species map"; + } + + for (String aCode : codes) { + SpeciesMapItem item = spMap.getItem(aCode); + if (item == null) { + return "No Species item for species code " + aCode; + } + } + return null; + } } diff --git a/src/tethys/species/SpeciesMapManager.java b/src/tethys/species/SpeciesMapManager.java index 670e1926..c88828f2 100644 --- a/src/tethys/species/SpeciesMapManager.java +++ b/src/tethys/species/SpeciesMapManager.java @@ -1,17 +1,32 @@ package tethys.species; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; +import javax.swing.JFileChooser; + import PamController.PamControlledUnitSettings; import PamController.PamController; +import PamController.PamFolders; import PamController.PamSettingManager; import PamController.PamSettings; +import PamUtils.PamFileFilter; +import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; /** * Master manager for species maps which will eventually allow for export and import from XML - * documents, databases and other things ... + * documents, databases and other things ...
+ * (Perhaps not as XML, will simply output the serialized map - easier. * @author dg50 * */ @@ -19,10 +34,23 @@ public class SpeciesMapManager implements PamSettings { private static SpeciesMapManager singleInstance = null; + /** + * Synch object to survive multithreading. + */ private static Object synch = new Object(); + /** + * Map of all species maps. + */ private GlobalSpeciesMap globalSpeciesMap; + private JFileChooser ioFileChooser; + + /** + * file end type for map files + */ + public static final String mapFileEnd = ".spmap"; + private SpeciesMapManager() { PamSettingManager.getInstance().registerSettings(this); } @@ -101,4 +129,124 @@ public class SpeciesMapManager implements PamSettings { return false; } } + + public ActionListener getExportAction(Window parentFrame) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportSpeciesMaps(parentFrame); + } + }; + } + + public ActionListener getImportAction(Window parentFrame) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + importSpeciesMaps(parentFrame); + } + }; + } + + /** + * Export all species maps to a serialized object file. + * @param parentFrame + * @return + */ + public boolean exportSpeciesMaps(Window parentFrame) { + JFileChooser chooser = getFileChooser(); + int ans = chooser.showSaveDialog(parentFrame); + if (ans != JFileChooser.APPROVE_OPTION) { + return false; + } + File opFile = chooser.getSelectedFile(); + opFile = PamFileFilter.checkFileEnd(opFile, mapFileEnd, true); + // write it. + try { + ObjectOutputStream op = new ObjectOutputStream(new FileOutputStream(opFile)); + op.writeObject(getSettingsReference()); + op.close(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + + return true; + } + + /** + * Get a file chooser, which will remember folders, etc. while PAMGuard is open + * @return file chooser. + */ + private JFileChooser getFileChooser() { + if (ioFileChooser != null) { + return ioFileChooser; + } + PamFileFilter fileFilter = new PamFileFilter("Species map files", mapFileEnd); + ioFileChooser = new JFileChooser(); + ioFileChooser.setFileFilter(fileFilter); + ioFileChooser.setCurrentDirectory(new File(PamFolders.getDefaultProjectFolder())); + return ioFileChooser; + } + + /** + * Import global species maps from selected file. + * @param parentFrame + * @return + */ + public boolean importSpeciesMaps(Window parentFrame) { + JFileChooser chooser = getFileChooser(); + int ans = chooser.showOpenDialog(parentFrame); + if (ans != JFileChooser.APPROVE_OPTION) { + return false; + } + File ipFile = chooser.getSelectedFile(); + ipFile = PamFileFilter.checkFileEnd(ipFile, mapFileEnd, true); + GlobalSpeciesMap readSpeciesMap = null; + // read it. + try { + ObjectInputStream ip = new ObjectInputStream(new FileInputStream(ipFile)); + readSpeciesMap = (GlobalSpeciesMap) ip.readObject(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return false; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return handleNewSpeciesMap(readSpeciesMap); + } + + private boolean handleNewSpeciesMap(GlobalSpeciesMap readSpeciesMap) { + if (readSpeciesMap == null) { + return false; + } + // could put in a dialog to only select parts of the map if we wanted to ? + int ans = WarnOnce.showWarning("Global Species Map", + "Do you want to overwrite ALL PAMGaurd species maps with the imported data ?", + WarnOnce.YES_NO_OPTION); + if (ans == WarnOnce.CANCEL_OPTION) { + return false; + } + globalSpeciesMap = readSpeciesMap; + // no wupdate all datablock maps since they keep their own copies. + ArrayList allDatablocks = PamController.getInstance().getDataBlocks(); + for (PamDataBlock aBlock : allDatablocks) { + DataBlockSpeciesManager spManager = aBlock.getDatablockSpeciesManager(); + if (spManager == null) { + continue; + } + DataBlockSpeciesMap blockMap = globalSpeciesMap.get(aBlock); + if (blockMap != null) { + spManager.setDatablockSpeciesMap(blockMap); + } + } + + return true; + } } diff --git a/src/tethys/species/swing/DataBlockSpeciesDialog.java b/src/tethys/species/swing/DataBlockSpeciesDialog.java index d0c44791..adb84646 100644 --- a/src/tethys/species/swing/DataBlockSpeciesDialog.java +++ b/src/tethys/species/swing/DataBlockSpeciesDialog.java @@ -1,17 +1,20 @@ package tethys.species.swing; import java.awt.BorderLayout; +import java.awt.FlowLayout; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JPanel; +import javax.swing.border.TitledBorder; import PamController.PamController; import PamView.PamGui; import PamView.dialog.PamDialog; import PamguardMVC.PamDataBlock; +import tethys.species.SpeciesMapManager; public class DataBlockSpeciesDialog extends PamDialog { @@ -24,6 +27,7 @@ public class DataBlockSpeciesDialog extends PamDialog { JPanel mainPanel = new JPanel(new BorderLayout()); speciesPanel = new DataBlockSpeciesPanel(dataBlock); mainPanel.add(BorderLayout.CENTER, speciesPanel.getDialogComponent()); + JButton itisButton = new JButton("Go to ITIS web site"); itisButton.setToolTipText("Go to ITIS website to search for species codes"); itisButton.addActionListener(new ActionListener() { @@ -33,9 +37,21 @@ public class DataBlockSpeciesDialog extends PamDialog { } }); JPanel nPanel = new JPanel(new BorderLayout()); + nPanel.setBorder(new TitledBorder("Code management")); nPanel.add(BorderLayout.EAST, itisButton); +// JPanel nwBit = new JPanel(new FlowLayout()); +// JButton exportButton = new JButton("Export"); +// exportButton.addActionListener(SpeciesMapManager.getInstance().getExportAction(parentFrame)); +// nwBit.add(exportButton); +// JButton importButton = new JButton("Import"); +// importButton.addActionListener(SpeciesMapManager.getInstance().getImportAction(parentFrame)); +// nwBit.add(importButton); +// nPanel.add(BorderLayout.WEST, nwBit); + + mainPanel.add(BorderLayout.NORTH, nPanel); setDialogComponent(mainPanel); + setResizable(true); } protected void gotoITIS() { diff --git a/src/tethys/species/swing/DataBlockSpeciesPanel.java b/src/tethys/species/swing/DataBlockSpeciesPanel.java index e2649f74..075cb080 100644 --- a/src/tethys/species/swing/DataBlockSpeciesPanel.java +++ b/src/tethys/species/swing/DataBlockSpeciesPanel.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JPanel; +import javax.swing.JScrollPane; import javax.swing.border.TitledBorder; import PamView.dialog.PamDialogPanel; @@ -30,7 +31,8 @@ public class DataBlockSpeciesPanel implements PamDialogPanel { this.dataBlock = dataBlock; mainPanel = new JPanel(new BorderLayout()); speciesPanel = new JPanel(); - mainPanel.add(speciesPanel, BorderLayout.CENTER); + JScrollPane scrollPane = new JScrollPane(speciesPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + mainPanel.add(scrollPane, BorderLayout.CENTER); mainPanel.setBorder(new TitledBorder(dataBlock.getDataName())); } diff --git a/src/tethys/swing/DetectionsExportPanel.java b/src/tethys/swing/DetectionsExportPanel.java index b790e7f8..6598f68e 100644 --- a/src/tethys/swing/DetectionsExportPanel.java +++ b/src/tethys/swing/DetectionsExportPanel.java @@ -11,10 +11,13 @@ import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.border.TitledBorder; +import PamController.PamController; +import PamView.dialog.PamDialog; import PamView.dialog.PamGridBagContraints; import PamView.panel.PamAlignmentPanel; import PamguardMVC.PamDataBlock; import tethys.TethysControl; +import tethys.species.DataBlockSpeciesManager; import tethys.swing.export.DetectionsExportWizard; public class DetectionsExportPanel extends TethysGUIPanel implements StreamTableObserver { @@ -53,6 +56,20 @@ public class DetectionsExportPanel extends TethysGUIPanel implements StreamTable if (selectedDataBlock == null) { return; } + + /** + * Check the species map is OK before doing anything. + */ + DataBlockSpeciesManager spManager = selectedDataBlock.getDatablockSpeciesManager(); + if (spManager != null) { + String error = spManager.checkSpeciesMapError(); + if (error != null) { + PamDialog.showWarning(PamController.getMainFrame(), "Datablock species manager error", error); + spManager.showSpeciesDialog(); + return; + } + } + DetectionsExportWizard.showDialog(getTethysControl().getGuiFrame(), getTethysControl(), selectedDataBlock); } From 1f8e790ae5c8008df4967146d4977ec82d2c8a0d Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:43:49 +0100 Subject: [PATCH 56/65] Collection enum Made an enum for the different collections. More robust than passing the names around as strings. Contains functions for converting between the collection name and the documents names within those collections. --- src/Array/PamArray.java | 9 +- src/tethys/Collection.java | 136 +++++++++++++ src/tethys/DocumentInfo.java | 48 +++++ src/tethys/TethysControl.java | 130 ++++++------ .../calibration/CalibrationHandler.java | 192 ++++++++++++++++++ .../calibration/swing/CalibrationsPanel.java | 5 + src/tethys/dbxml/DBXMLConnect.java | 75 +++---- src/tethys/dbxml/DBXMLQueries.java | 165 ++++++++++----- src/tethys/deployment/DeploymentHandler.java | 9 +- src/tethys/species/GlobalSpeciesMap.java | 22 +- src/tethys/species/SpeciesMapManager.java | 71 +++++-- .../species/swing/SpeciesMapIODialog.java | 144 +++++++++++++ src/tethys/swing/FancyClientButton.java | 9 +- .../swing/documents/TethysDocumentTable.java | 83 ++++---- .../swing/documents/TethysDocumentsFrame.java | 7 +- 15 files changed, 886 insertions(+), 219 deletions(-) create mode 100644 src/tethys/Collection.java create mode 100644 src/tethys/DocumentInfo.java create mode 100644 src/tethys/calibration/CalibrationHandler.java create mode 100644 src/tethys/calibration/swing/CalibrationsPanel.java create mode 100644 src/tethys/species/swing/SpeciesMapIODialog.java diff --git a/src/Array/PamArray.java b/src/Array/PamArray.java index 43593464..a116108d 100644 --- a/src/Array/PamArray.java +++ b/src/Array/PamArray.java @@ -97,6 +97,11 @@ public class PamArray implements Serializable, Cloneable, ManagedParameters { */ private String instrumentType; + /** + * Array Id. Can be anything. Compulsory for Tethys. + */ + private String instrumentId; + public String getInstrumentType() { return instrumentType; } @@ -113,10 +118,6 @@ public class PamArray implements Serializable, Cloneable, ManagedParameters { 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/tethys/Collection.java b/src/tethys/Collection.java new file mode 100644 index 00000000..41bff1c7 --- /dev/null +++ b/src/tethys/Collection.java @@ -0,0 +1,136 @@ +package tethys; + +/** + * Names of Tethys Collections. These are the plural names, though contain functionality + * to get the document names, which are generally the singular of the enum + * @author dg50 + * + */ +public enum Collection { + + Deployments, Detections, Calibrations, Localizations, SpeciesAbbreviations, Ensembles, SourceMaps, ITIS, ITIS_ranks; + + /** + * A list of the main collections in the database, i.e. ones the user will + * possibly want to interract with through the GUI. + * @return list of main collections. + */ + public static Collection[] mainList() { + Collection[] cs = {Deployments, Detections, Calibrations, Localizations, SpeciesAbbreviations, Ensembles}; + return cs; + } + /** + * Get the name of a document in this collection, this is generally the singular + * of the collection name. + * @return Document name, e.g. Detection for Detections + */ + public String documentName() { + switch (this) { + case Calibrations: + return "Calibration"; + case Deployments: + return "Deployment"; + case Detections: + return "Detections"; // this one is plural ! + case Localizations: + return "Localization"; + case SpeciesAbbreviations: + return "SpeciesAbbreviation"; + case Ensembles: + return "Ensemble"; + default: + break; + } + return null; + } + + public String collectionName() { + return this.toString(); + } + + /** + * Find a collection for the given name. This does + * a bit more than the simple 'valueof' since it also + * allows the user to input a documentname in place, which + * is just the collection name without the plural 's' on the end + * @param name Collection name. + * @return Collection or null. + */ + public static Collection fromName(String name) { + Collection c = Collection.valueOf(name); + if (c != null) { + return c; + } + /** + * Otherwise, may need to do a longer search to see if the user has passed + * the singular document name. + */ + if (name.endsWith("s") == false) { + c = Collection.valueOf(name+"s"); + if (c != null) { + return c; + } + } + return null; + } + /** + * get Tethys collection name from nilus collection objects + * @param className nilus object Class Name + * @return name of Tethys collection + */ + public static Collection fromClass(Class nilusClass) { + String className = nilusClass.getName(); + switch(className) { + case "nilus.Deployment": + return Deployments; + case "nilus.Detections": + return Detections; + case "nilus.Calibration": + return Calibrations; + case "nilus.Ensemble": + return Ensembles; + case "nilus.Localization": + return Localizations; + case "nilus.SpeciesAbbreviation": + return SpeciesAbbreviations; + case "nilus.SourceMap": + return SourceMaps; + case "nilus.ITIS": + return ITIS; + case "nilus.ranks": + return ITIS_ranks; + default: + return null; + } + } +// /** +// * get Tethys collection name from nilus collection objects +// * @param className nilus object Class Name +// * @return name of Tethys collection +// */ +// public static String getCollection(Class nilusClass) { +// String className = nilusClass.getName(); +// switch(className) { +// case "nilus.Deployment": +// return "Deployments"; +// case "nilus.Detections": +// return "Detections"; +// case "nilus.Calibration": +// return "Calibrations"; +// case "nilus.Ensemble": +// return "Ensembles"; +// case "nilus.Localization": +// return "Localizations"; +// case "nilus.SpeciesAbbreviation": +// return "SpeciesAbbreviations"; +// case "nilus.SourceMap": +// return "SourceMaps"; +// case "nilus.ITIS": +// return "ITIS"; +// case "nilus.ranks": +// return "ITIS_ranks"; +// default: +// return ""; +// } +// } +} diff --git a/src/tethys/DocumentInfo.java b/src/tethys/DocumentInfo.java new file mode 100644 index 00000000..438aefb7 --- /dev/null +++ b/src/tethys/DocumentInfo.java @@ -0,0 +1,48 @@ +package tethys; + +/** + * Basic information about a document that can be used to + * make document lists. + * @author dg50 + * + */ +public class DocumentInfo implements Comparable { + + private Collection collection; + private String documentName; + private String documentId; + /** + * @param collection + * @param documentName + * @param documentId + */ + public DocumentInfo(Collection collection, String documentName, String documentId) { + this.collection = collection; + this.documentName = documentName; + this.documentId = documentId; + } + @Override + public int compareTo(DocumentInfo o) { + return this.documentName.compareTo(o.documentName); + } + /** + * @return the collection + */ + public Collection getCollection() { + return collection; + } + /** + * @return the documentName + */ + public String getDocumentName() { + return documentName; + } + /** + * @return the documentId + */ + public String getDocumentId() { + return documentId; + } + + +} diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 81270ce0..426e867e 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -1,6 +1,7 @@ package tethys; import java.awt.Desktop; +import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedOutputStream; @@ -37,6 +38,7 @@ import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; import metadata.deployment.DeploymentData; import tethys.TethysState.StateType; +import tethys.calibration.CalibrationHandler; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.DBXMLQueries; import tethys.dbxml.ServerStatus; @@ -88,6 +90,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet private DeploymentHandler deploymentHandler; private DetectionsHandler detectionsHandler; + private CalibrationHandler calibrationHandler; private ITISFunctions itisFunctions; @@ -98,6 +101,8 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet dbxmlQueries = new DBXMLQueries(this, dbxmlConnect); deploymentHandler = new DeploymentHandler(this); detectionsHandler = new DetectionsHandler(this); + calibrationHandler = new CalibrationHandler(this); + serverCheckTimer = new Timer(10000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -127,9 +132,23 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet public DBXMLConnect getDbxmlConnect() { return dbxmlConnect; } + @Override + public JMenuItem createDetectionMenu(Frame parentFrame) { + return createTethysMenu(parentFrame); + } @Override public JMenuItem createFileMenu(JFrame parentFrame) { + // TODO Auto-generated method stub + return super.createFileMenu(parentFrame); + } + + /** + * Make a menu. Can go either in File or Settings. TBD. + * @param parentFrame + * @return + */ + public JMenuItem createTethysMenu(Frame parentFrame) { JMenu tethysMenu = new JMenu("Tethys"); // JMenuItem tethysExport = new JMenuItem("Export ..."); // tethysMenu.add(tethysExport); @@ -150,47 +169,19 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet tethysMenu.add(menuItem); JMenuItem collections = new JMenu("Collections"); + Collection[] mainCollections = Collection.mainList(); + for (int i = 0; i < mainCollections.length; i++) { + Collection col = mainCollections[i]; + menuItem = new JMenuItem("Open " + col.collectionName() + " collection in browser"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTethysCollection(col); + } + }); + collections.add(menuItem); + } - menuItem = new JMenuItem("Open Deployments collection in browser"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - openTethysCollection("Deployments"); - } - }); - collections.add(menuItem); - menuItem = new JMenuItem("Open Detections collection in browser"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - openTethysCollection("Detections"); - } - }); - collections.add(menuItem); - menuItem = new JMenuItem("Open Localizations collection in browser"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - openTethysCollection("Localizations"); - } - }); - collections.add(menuItem); - menuItem = new JMenuItem("Open Calibrations collection in browser"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - openTethysCollection("Calibrations"); - } - }); - collections.add(menuItem); - menuItem = new JMenuItem("Open Species Abbreviations collection in browser"); - menuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - openTethysCollection("SpeciesAbbreviations"); - } - }); - collections.add(menuItem); tethysMenu.add(collections); tethysMenu.addSeparator(); JMenuItem showDeps = new JMenuItem("Show project deployments"); @@ -202,6 +193,16 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet }); tethysMenu.add(showDeps); + JMenuItem cals = new JMenuItem("Export calibrations"); + cals.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + calibrationHandler.exportAllCalibrations(); + } + }); + tethysMenu.add(cals); + tethysMenu.addSeparator(); JMenuItem mapItem = new JMenuItem("Export species maps ..."); mapItem.setToolTipText("Export all species maps (PAMGuard codes to ITIS codes to file for import into other configurations"); @@ -292,24 +293,24 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet // } catch (URISyntaxException e) { // e.printStackTrace(); // } - openTethysCollection("Client"); + openCollectionInBrowser("Client"); } /** * open client in the default web browser */ - public void openTethysCollection(String collectionName) { - if (collectionName == null) { + public void openTethysCollection(Collection collection) { + if (collection == null) { return; } - if (getTethysExportParams().listDocsInPamguard && collectionName.equals("Client") == false) { - openCollectionInPAMGuard(collectionName); + if (getTethysExportParams().listDocsInPamguard) { + openCollectionInPAMGuard(collection); } else { - openCollectionInBrowser(collectionName); + openCollectionInBrowser(collection.collectionName()); } } - public void openCollectionInPAMGuard(String collectionName) { - TethysDocumentsFrame.showTable(getGuiFrame(), this, collectionName); + public void openCollectionInPAMGuard(Collection collection) { + TethysDocumentsFrame.showTable(getGuiFrame(), this, collection); } public void openCollectionInBrowser(String collectionName) { @@ -524,19 +525,17 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet ArrayList matchedDeployments = deploymentHandler.getMatchedDeployments(); for (DatablockSynchInfo synchInfo : dataBlockSynchInfos) { // dataPrefixes[i] = DetectionsHandler.getDetectionsDocIdPrefix(deplData.getProject(), synchInfo.getDataBlock()); - int count = 0; + int detectionCount = 0; + int documentCount = 0; for (PDeployment pDepl : matchedDeployments) { - count += dbxmlQueries.countData(synchInfo.getDataBlock(), pDepl.deployment.getId()); - } - synchInfo.setDataCount(count); - // also count the actual number of Detectoin documents - ArrayList someNames = getDbxmlQueries().getDetectionsDocuments(synchInfo.getDataBlock(), null); - if (someNames == null) { - synchInfo.setDetectionDocumentCount(0); - } - else { - synchInfo.setDetectionDocumentCount(someNames.size()); + detectionCount += dbxmlQueries.countData(synchInfo.getDataBlock(), pDepl.deployment.getId()); + ArrayList detectionsNames = getDbxmlQueries().getDetectionsDocuments(synchInfo.getDataBlock(), pDepl.deployment.getId()); + if (detectionsNames != null) { + documentCount += detectionsNames.size(); + } } + synchInfo.setDataCount(detectionCount); + synchInfo.setDetectionDocumentCount(documentCount); i++; } @@ -590,6 +589,11 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet WarnOnce.showWarning(title, msg, WarnOnce.WARNING_MESSAGE); } + public void displayDocument(DocumentInfo docInfo) { + String collectionName = docInfo.getCollection().collectionName(); + String docId = docInfo.getDocumentId(); + displayDocument(collectionName, docId); + } /** * Load a document from the database and display it in a popup window * @param collection @@ -679,4 +683,12 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION)); } + /** + * @return the calibrationHandler + */ + public CalibrationHandler getCalibrationHandler() { + return calibrationHandler; + } + + } diff --git a/src/tethys/calibration/CalibrationHandler.java b/src/tethys/calibration/CalibrationHandler.java new file mode 100644 index 00000000..22418f47 --- /dev/null +++ b/src/tethys/calibration/CalibrationHandler.java @@ -0,0 +1,192 @@ +package tethys.calibration; + +import java.util.List; + +import Acquisition.AcquisitionControl; +import Acquisition.AcquisitionParameters; +import Acquisition.AcquisitionProcess; +import Array.ArrayManager; +import Array.Hydrophone; +import Array.PamArray; +import PamController.PamController; +import PamController.soundMedium.GlobalMedium; +import PamController.soundMedium.GlobalMedium.SoundMedium; +import PamController.soundMedium.GlobalMediumManager; +import nilus.Calibration; +import nilus.Calibration.FrequencyResponse; +import nilus.Calibration.QualityAssurance; +import nilus.Helper; +import nilus.MetadataInfo; +import nilus.QualityValueBasic; +import nilus.ResponsibleParty; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysStateObserver; +import tethys.TethysTimeFuncs; +import tethys.dbxml.DBXMLConnect; +import tethys.dbxml.TethysException; +import tethys.niluswraps.PDeployment; +import tethys.pamdata.AutoTethysProvider; + +public class CalibrationHandler implements TethysStateObserver { + + private TethysControl tethysControl; + + /** + * @param tethysControl + */ + public CalibrationHandler(TethysControl tethysControl) { + this.tethysControl = tethysControl; + tethysControl.addStateObserver(this); + } + + @Override + public void updateState(TethysState tethysState) { + // TODO Auto-generated method stub + + } + + public int exportAllCalibrations() { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + int nPhone = array.getHydrophoneCount(); + DBXMLConnect dbxml = tethysControl.getDbxmlConnect(); + int nExport = 0; + for (int i = 0; i < nPhone; i++) { +// String docName = getHydrophoneId(i); + Calibration calDoc = createCalibrationDocument(i); + boolean ok = false; + try { + ok = dbxml.postAndLog(calDoc); + } catch (TethysException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + tethysControl.showException(e); + ok = false; + } + if (ok) { + nExport++; + } + } + return nExport; + } + + /** + * Create a calibration document for a single hydrophone channel. + * @param pDeployment deployment, for cross referencing. + * @param channelIndex channel id. One document per channel for a multi hydrophone array. + * @return Calibration document. + */ + public Calibration createCalibrationDocument(int channelIndex) { + AcquisitionControl daqControl = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.unitType); + return createCalibrationDocument(daqControl, channelIndex); + } + + /** + * Get an id based on the instrument identifiers and channel number. + * @param channelIndex + * @return id string - instrument type + instrument id + channel + */ + public String getHydrophoneId(int channelIndex) { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + if (array == null) { + return null; + } + String id = String.format("%s_%s_ch%02d", array.getInstrumentType(), array.getInstrumentId(), channelIndex); + id = id.replace(" ", "_"); + return id; + } + + /** + * Create a calibration document for a single hydrophone channel. + * @param pDeployment deployment, for cross referencing. + * @param soundAcquisition Daq information - needed to get the ADC calibration information. + * @param channelIndex channel id. One document per channel for a multi hydrophone array. + * @return Calibration document. + */ + public Calibration createCalibrationDocument(AcquisitionControl soundAcquisition, int channelIndex) { + /** + * Calibrations document id and cross referencing to Deploymnet documents: + * Identifier of instrument, preamplifier, or hydrophone. + * Corresponds to elements in Deployment: + * Deployment/Instrument/Id, + * Deployment/Sensors/Audio/HydrophoneId, + * Deployment/Sensors/Audio[i]/PreampId. + * As instruments may be calibrated multiple times, it is not an error for duplicate Id values to appear. + * It is recommended that the three different types of identifiers (instrument, hydrophone, preamp) be distinct, + * but the Type element may be used to distinguish them if they are not. + */ + + /* + * very remote possibility that DAQ doesn't exist. What to do in this case ? It's also possible that some configurations may + * have to have >1 DAQ's ? + */ + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + if (array == null) { + return null; + } + if (channelIndex < 0 || channelIndex >= array.getHydrophoneCount()) { + return null; + } +// ArrayManager.getArrayManager().get +// hydrophones = array. + Hydrophone hydrophone = array.getHydrophoneArray().get(channelIndex); + double hSens = hydrophone.getSensitivity(); + double preampGain = hydrophone.getPreampGain(); + + GlobalMediumManager mediumManager = PamController.getInstance().getGlobalMediumManager(); + SoundMedium currentMedium = mediumManager.getCurrentMedium(); + double dbRef = GlobalMedium.getdBreference(currentMedium); // probably in Pa, so multiply by 1e6. + + /** + * The calibration id can be a bit tricky, it will need to be cross referenced from the + * Deployment document, and it is likely that a deployment document will have to reference several + * calibration documents for different channels. + * Make the name from the Array name (new), the array Instrument Id (unique to the array) + * and the channel number. These will then all have to go into the Deployment document in + * the list of audio devices, cross referenced as the SensorId field. + * + */ + + Calibration calibration = new Calibration(); + + try { + Helper.createRequiredElements(calibration); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + e.printStackTrace(); + } + String id = getHydrophoneId(channelIndex); +// id = String.format("%d", channelIndex); + calibration.setId(id); + calibration.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); + calibration.setType(GlobalMedium.getRecieverString(currentMedium, false, false)); + calibration.setIntensityReferenceUPa(AutoTethysProvider.roundSignificantFigures(dbRef*1e6,3)); + String sensRef = GlobalMedium.getdBRefString(currentMedium); + // it doesn't like this since it has a unicode character. Leave it or change the micro to 'u' +// calibration.setSensitivityReference(sensRef); + calibration.setSensitivityDBV(hSens+preampGain); + if (soundAcquisition != null) { + AcquisitionProcess daqProcess = soundAcquisition.getAcquisitionProcess(); + double fullScale = daqProcess.rawAmplitude2dB(1, channelIndex, false); + calibration.setSensitivityDBFS(fullScale); + } + FrequencyResponse frs = calibration.getFrequencyResponse(); + List hz = frs.getHz(); + List db = frs.getDB(); + hz.add(Double.valueOf(0)); + db.add(Double.valueOf(hSens+preampGain)); + + MetadataInfo metaInf = calibration.getMetadataInfo(); + metaInf.setDate(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); + metaInf.setUpdateFrequency("as-needed"); + ResponsibleParty contact = metaInf.getContact(); + contact.setIndividualName("Unknown"); + contact.setOrganizationName("unknown"); + + QualityAssurance qa = calibration.getQualityAssurance(); + qa.setQuality(QualityValueBasic.VALID); + qa.setComment("Unknown calibration"); + + + return calibration; + } +} diff --git a/src/tethys/calibration/swing/CalibrationsPanel.java b/src/tethys/calibration/swing/CalibrationsPanel.java new file mode 100644 index 00000000..7dace048 --- /dev/null +++ b/src/tethys/calibration/swing/CalibrationsPanel.java @@ -0,0 +1,5 @@ +package tethys.calibration.swing; + +public class CalibrationsPanel { + +} diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index aee6dc19..a00098a5 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -18,6 +18,7 @@ import dbxml.JerseyClient; import dbxml.Queries; import dbxml.uploader.Importer; import nilus.MarshalXML; +import tethys.Collection; import tethys.TethysControl; import tethys.database.TethysActions; import tethys.database.TethysLogger; @@ -40,7 +41,7 @@ public class DBXMLConnect { private String currentSiteURL; - public static String[] collections = {"Deployments", "Detections", "Localizations", "Calibrations", "SpeciesAbbreviations"}; +// public static String[] collections = {"Deployments", "Detections", "Localizations", "Calibrations", "SpeciesAbbreviations"}; public DBXMLConnect(TethysControl tethysControl) { this.tethysControl = tethysControl; @@ -128,19 +129,32 @@ public class DBXMLConnect { public boolean postAndLog(Object nilusObject) throws TethysException { + return postAndLog(nilusObject, null); + } + + /** + * I don't think this should ever be used since everything goes a bit pear + * shaped if the documentName isn't the same as the Id. + * @param nilusObject + * @param documentName + * @return + * @throws TethysException + */ + private boolean postAndLog(Object nilusObject, String documentName) throws TethysException + { TethysException e = null; boolean success = false; try { - success = postToTethys(nilusObject); + success = postToTethys(nilusObject, documentName); } catch (TethysException ex) { e = ex; } TethysLogger logger = TethysLogger.getTethysLogger(tethysControl); Class objClass = nilusObject.getClass(); - String collection = getTethysCollection(objClass.getName()); + Collection collection = Collection.fromClass(objClass); String documentId = getDocumentId(nilusObject); - logger.logAction(collection, documentId, TethysActions.ADDDOCUMENT, success, ""); + logger.logAction(collection.collectionName(), documentId, TethysActions.ADDDOCUMENT, success, ""); if (e != null) { throw (e); } @@ -154,22 +168,24 @@ public class DBXMLConnect { * @return error string, null string means there are no errors * @throws TethysException */ - private boolean postToTethys(Object nilusObject) throws TethysException + private boolean postToTethys(Object nilusObject, String documentName) throws TethysException { Class objClass = nilusObject.getClass(); - String collection = getTethysCollection(objClass.getName()); + Collection collection = Collection.fromClass(nilusObject.getClass()); TethysExportParams params = new TethysExportParams(); String importReturn = null; - String tempName = getTempFileName(nilusObject); - tempName = tempDirectory.getAbsolutePath() + File.separator + tempName + ".xml"; - File tempFile = new File(tempName); - String bodgeName = tempName;//"C:\\Users\\dg50\\AppData\\Local\\Temp\\PAMGuardTethys\\Meygen2022_10a.xml"; + if (documentName == null) { + documentName = getTempFileName(nilusObject); + } + documentName = tempDirectory.getAbsolutePath() + File.separator + documentName + ".xml"; + File tempFile = new File(documentName); + String bodgeName = documentName;//"C:\\Users\\dg50\\AppData\\Local\\Temp\\PAMGuardTethys\\Meygen2022_10a.xml"; try { MarshalXML marshal = new MarshalXML(); marshal.createInstance(objClass); marshal.marshal(nilusObject, tempFile.toString()); // tempFile = stripXMLHeader(tempFile); - importReturn = Importer.ImportFiles(params.getFullServerName(), collection, + importReturn = Importer.ImportFiles(params.getFullServerName(), collection.collectionName(), new String[] { bodgeName }, "", "", false); @@ -207,7 +223,7 @@ public class DBXMLConnect { */ public boolean updateDocument(Object nilusDocument) throws TethysException { deleteDocument(nilusDocument); - return postToTethys(nilusDocument); + return postToTethys(nilusDocument, null); } /** @@ -221,11 +237,11 @@ public class DBXMLConnect { public boolean deleteDocument(Object nilusDocument) throws TethysException { Class objClass = nilusDocument.getClass(); - String collection = getTethysCollection(objClass.getName()); + Collection collection = Collection.fromClass(objClass); String docId = getDocumentId(nilusDocument); String result = null; try { - result = jerseyClient.removeDocument(collection, docId ); + result = jerseyClient.removeDocument(collection.collectionName(), docId ); /** * Return from a sucessful delete is something like * @@ -274,6 +290,7 @@ An error will throw an exception. */ public boolean removeDocument(String collection, String docId) throws TethysException { try { +// docId = "SoundTrap_600_HF_7129_ch00"; Object result = jerseyClient.removeDocument(collection, docId ); /** * Return from a sucessful delete is something like @@ -459,35 +476,7 @@ C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xmlnot: 0 b } - /** - * get Tethys collection name from nilus collection objects - * @param className nilus object Class Name - * @return name of Tethys collection - */ - public String getTethysCollection(String className) { - switch(className) { - case "nilus.Deployment": - return "Deployments"; - case "nilus.Detections": - return "Detections"; - case "nilus.Calibration": - return "Calibrations"; - case "nilus.Ensemble": - return "Ensembles"; - case "nilus.Localization": - return "Localizations"; - case "nilus.SpeciesAbbreviation": - return "SpeciesAbbreviations"; - case "nilus.SourceMap": - return "SourceMaps"; - case "nilus.ITIS": - return "ITIS"; - case "nilus.ranks": - return "ITIS_ranks"; - default: - return ""; - } - } + public synchronized boolean openConnections() { TethysExportParams params = tethysControl.getTethysExportParams(); diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 5e6fe572..0b5fc9f8 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -31,6 +31,8 @@ import nilus.Detections; import nilus.GranularityEnumType; import nilus.GranularityType; import nilus.Helper; +import tethys.Collection; +import tethys.DocumentInfo; import tethys.TethysControl; import tethys.TethysTimeFuncs; import tethys.output.TethysExportParams; @@ -139,7 +141,7 @@ public class DBXMLQueries { // Queries queries = new Queries(jerseyClient); queryResult = jerseyClient.queryJSON(jsonQueryString, 0); - schemaPlan = jerseyClient.queryJSON(jsonQueryString, 1); +// schemaPlan = jerseyClient.queryJSON(jsonQueryString, 1); } catch (Exception e) { @@ -149,75 +151,138 @@ public class DBXMLQueries { return new DBQueryResult(System.currentTimeMillis()-t1, queryResult, schemaPlan); } - /** - * Check whether or not to strip of the s of one of the collection names. - * This is caused by some daft thing whereby the Deployments colleciton is called Deployments - * byt the Detections collection is called Detection - * @param collection - * @return - */ - public String checkCollectionPlural(String collection) { - switch (collection) { - case "Deployments": - return "Deployment"; - case "Localizations": - return "Localize"; - case "Calibrations": - return "Calibration"; - case "SpeciesAbbreviations": - return "SpeciesAbbreviations"; - } - return collection; - } +// /** +// * Check whether or not to strip of the s of one of the collection names. +// * This is caused by some daft thing whereby the Deployments colleciton is called Deployments +// * byt the Detections collection is called Detection +// * @param collection +// * @return +// */ +// public String checkCollectionPlural(String collection) { +// switch (collection) { +// case "Deployments": +// return "Deployment"; +// case "Localizations": +// return "Localize"; +// case "Calibrations": +// return "Calibration"; +// case "SpeciesAbbreviations": +// return "SpeciesAbbreviations"; +// } +// return collection; +// } /** * Get a list of all documents in a collection. * @param collection * @return list of all documents in a collection, or null if no collection. */ - public ArrayList getCollectionDocumentList(String collection) { + public ArrayList getCollectionDocumentList(Collection collection) { if (collection == null) { return null; } - collection = checkCollectionPlural(collection); - // if (collection.endsWith("s")) { - // collection = collection.substring(0, collection.length()-1); - // } - String baseQuery = "{\"return\":[\"COLLECTIONNAME/Id\"],\"select\":[],\"enclose\":1}"; - baseQuery = baseQuery.replace("COLLECTIONNAME", collection); - String tagName = "Id"; - - if (collection.equals("SpeciesAbbreviations")) { - baseQuery = "{\"return\":[\"Abbreviations/Name\"],\"select\":[],\"enclose\":1}"; - tagName = "Name"; - } - - DBQueryResult result; + + /** + * xQuery string based on examples in email from MR on 27/9/2023 + */ +// String baseQuery = " {\r\n" + String baseQuery = " {\r\n" + + " for $doc in collection(\"COLLECTIONAME\")/DOCUMENTNAME\r\n" + + " return\r\n" + + " {\r\n" + + " base-uri($doc), \r\n" + + " $doc/Id\r\n" + + " }\r\n" + + " \r\n" + + "} \r\n" + + ""; + String xQuery = baseQuery.replace("COLLECTIONAME", collection.collectionName()); + xQuery = xQuery.replace("DOCUMENTNAME", collection.documentName()); + + Queries queries = dbXMLConnect.getTethysQueries(); + String result = null; try { - result = executeQuery(baseQuery); - } catch (TethysQueryException e) { - System.out.println("Error with query: " + baseQuery); - tethysControl.showException(e); + result = queries.QueryTethys(xQuery); + } + catch (Exception e) { + e.printStackTrace(); + } + if (result == null) { return null; } +// System.out.println(result); + ArrayList documentInfos = new ArrayList<>(); - if (result == null || result.queryResult == null) { - return null; - } - Document doc = convertStringToXMLDocument(result.queryResult); + Document doc = convertStringToXMLDocument(result); if (doc == null) { return null; } - NodeList returns = doc.getElementsByTagName(tagName); - ArrayList docIds = new ArrayList<>(); +// PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); +// System.out.println(pamXMLWriter.getAsString(doc)); + /** + * lots of elements along lines of + * dbxml:///Deployments/Meygen20229Meygen20229 + */ + NodeList returns = doc.getElementsByTagName("doc"); int n = returns.getLength(); + String toStrip = "dbxml:///"+collection.collectionName()+"/"; for (int i = 0; i < n; i++) { Node aNode = returns.item(i); - String docId = aNode.getTextContent(); - docIds.add(docId); + // this is the doc name with a load of stuff in front, + // e.g. dbxml:///Deployments/1705_Array-2017-09-261705_Array-2017-09-26 + String nameStr = aNode.getTextContent(); + nameStr = nameStr.replaceFirst(toStrip, ""); + String id = null; + if (aNode instanceof Element) { + id = getElementData((Element) aNode, "Id"); + } + + DocumentInfo docInfo = new DocumentInfo(collection, nameStr, id); + documentInfos.add(docInfo); +// System.out.println(nameStr + " : " + id); } - - return docIds; + return documentInfos; + + + + // if (collection.endsWith("s")) { + // collection = collection.substring(0, collection.length()-1); + // } +// String baseQuery = "{\"return\":[\"COLLECTIONNAME/Id\"],\"select\":[],\"enclose\":1}"; +// baseQuery = baseQuery.replace("COLLECTIONNAME", collection); +// String tagName = "Id"; +// +// if (collection.equals("SpeciesAbbreviations")) { +// baseQuery = "{\"return\":[\"Abbreviations/Name\"],\"select\":[],\"enclose\":1}"; +// tagName = "Name"; +// } +// +// DBQueryResult result; +// try { +// result = executeQuery(baseQuery); +// } catch (TethysQueryException e) { +// System.out.println("Error with query: " + baseQuery); +// tethysControl.showException(e); +// return null; +// } +// +// if (result == null || result.queryResult == null) { +// return null; +// } +// Document doc = convertStringToXMLDocument(result.queryResult); +// if (doc == null) { +// return null; +// } +// NodeList returns = doc.getElementsByTagName(tagName); +// ArrayList docIds = new ArrayList<>(); +// int n = returns.getLength(); +// for (int i = 0; i < n; i++) { +// Node aNode = returns.item(i); +// String docId = aNode.getTextContent(); +// docIds.add(docId); +// } +// +// return docIds; } public ArrayList getProjectNames() { diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 16b63fdb..93a499fa 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -58,6 +58,7 @@ import tethys.TethysLocationFuncs; import tethys.TethysState; import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; +import tethys.calibration.CalibrationHandler; import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.TethysException; @@ -979,6 +980,7 @@ public class DeploymentHandler implements TethysStateObserver { private String getInstrumentType() { return ArrayManager.getArrayManager().getCurrentArray().getInstrumentType(); } + /** * Get a geometry type string for Tethys based on information in the array manager. * @return @@ -1009,12 +1011,15 @@ public class DeploymentHandler implements TethysStateObserver { ArrayList phones = array.getHydrophoneArray(); int iPhone = 0; long timeMillis = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getAudioTimeStamp()); - + CalibrationHandler calibrationHandler = tethysControl.getCalibrationHandler(); + for (Hydrophone aPhone : phones) { PamVector hydLocs = array.getAbsHydrophoneVector(iPhone, timeMillis); Audio audio = new Audio(); audio.setNumber(BigInteger.valueOf(iPhone)); - audio.setSensorId(String.format("Hydrophone %d", iPhone)); // shold replace with serial number if it exists. + String id = calibrationHandler.getHydrophoneId(iPhone); +// audio.setSensorId(String.format("Hydrophone %d", iPhone)); // should replace with serial number if it exists. + audio.setSensorId(id); GeometryTypeM geom = new GeometryTypeM(); geom.setXM(hydLocs.getCoordinate(0)); geom.setYM(hydLocs.getCoordinate(1)); diff --git a/src/tethys/species/GlobalSpeciesMap.java b/src/tethys/species/GlobalSpeciesMap.java index cc8c84f3..c60f6f85 100644 --- a/src/tethys/species/GlobalSpeciesMap.java +++ b/src/tethys/species/GlobalSpeciesMap.java @@ -5,7 +5,7 @@ import java.util.HashMap; import PamguardMVC.PamDataBlock; -public class GlobalSpeciesMap implements Serializable { +public class GlobalSpeciesMap implements Serializable, Cloneable { public static final long serialVersionUID = 1L; @@ -14,7 +14,7 @@ public class GlobalSpeciesMap implements Serializable { /** * @return the datablockMaps */ - private synchronized HashMap getDatablockMaps() { + public synchronized HashMap getDatablockMaps() { if (datablockMaps == null) { datablockMaps = new HashMap<>(); } @@ -28,5 +28,23 @@ public class GlobalSpeciesMap implements Serializable { public DataBlockSpeciesMap get(PamDataBlock pamDataBlock) { return getDatablockMaps().get(pamDataBlock.getLongDataName()); } + + public DataBlockSpeciesMap removeBlock(PamDataBlock pamDataBlock) { + return getDatablockMaps().remove(pamDataBlock.getLongDataName()); + } + + @Override + public GlobalSpeciesMap clone() { + GlobalSpeciesMap clone; + try { + clone = (GlobalSpeciesMap) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + clone.datablockMaps = new HashMap<>(); + clone.datablockMaps.putAll(this.getDatablockMaps()); + return clone; + } } diff --git a/src/tethys/species/SpeciesMapManager.java b/src/tethys/species/SpeciesMapManager.java index c88828f2..e8aec0dc 100644 --- a/src/tethys/species/SpeciesMapManager.java +++ b/src/tethys/species/SpeciesMapManager.java @@ -11,6 +11,9 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; import javax.swing.JFileChooser; @@ -22,6 +25,7 @@ import PamController.PamSettings; import PamUtils.PamFileFilter; import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; +import tethys.species.swing.SpeciesMapIODialog; /** * Master manager for species maps which will eventually allow for export and import from XML @@ -154,6 +158,17 @@ public class SpeciesMapManager implements PamSettings { * @return */ public boolean exportSpeciesMaps(Window parentFrame) { + // gather the species maps from the data blocks... + gatherSpeciesMaps(); + GlobalSpeciesMap toExport = SpeciesMapIODialog.showDialog(parentFrame, globalSpeciesMap, true); + if (toExport == null) { + return false; + } + if (toExport.getDatablockMaps().size() == 0) { + return false; + } + + JFileChooser chooser = getFileChooser(); int ans = chooser.showSaveDialog(parentFrame); if (ans != JFileChooser.APPROVE_OPTION) { @@ -164,7 +179,7 @@ public class SpeciesMapManager implements PamSettings { // write it. try { ObjectOutputStream op = new ObjectOutputStream(new FileOutputStream(opFile)); - op.writeObject(getSettingsReference()); + op.writeObject(toExport); op.close(); } catch (IOException e) { e.printStackTrace(); @@ -218,35 +233,67 @@ public class SpeciesMapManager implements PamSettings { e.printStackTrace(); return false; } + + GlobalSpeciesMap keptMaps = SpeciesMapIODialog.showDialog(parentFrame, readSpeciesMap, false); + if (keptMaps == null) { + return false; + } + if (keptMaps.getDatablockMaps().size() == 0) { + return false; + } - return handleNewSpeciesMap(readSpeciesMap); + return handleNewSpeciesMap(keptMaps); } private boolean handleNewSpeciesMap(GlobalSpeciesMap readSpeciesMap) { if (readSpeciesMap == null) { return false; } + + // could put in a dialog to only select parts of the map if we wanted to ? int ans = WarnOnce.showWarning("Global Species Map", - "Do you want to overwrite ALL PAMGaurd species maps with the imported data ?", + "Do you want to overwrite PAMGaurd species maps with the imported data ?", WarnOnce.YES_NO_OPTION); if (ans == WarnOnce.CANCEL_OPTION) { return false; } - globalSpeciesMap = readSpeciesMap; - // no wupdate all datablock maps since they keep their own copies. - ArrayList allDatablocks = PamController.getInstance().getDataBlocks(); - for (PamDataBlock aBlock : allDatablocks) { - DataBlockSpeciesManager spManager = aBlock.getDatablockSpeciesManager(); - if (spManager == null) { + + Set> mapSet = readSpeciesMap.getDatablockMaps().entrySet(); + Iterator> iter = mapSet.iterator(); + while (iter.hasNext()) { + Entry entry = iter.next(); + PamDataBlock dataBlock = PamController.getInstance().getDataBlockByLongName(entry.getKey()); + if (dataBlock == null) { + String err = String.format("Data block %s does not exist in the current configuration", entry.getKey()); + WarnOnce.showWarning("Missing data block", err, WarnOnce.WARNING_MESSAGE); continue; } - DataBlockSpeciesMap blockMap = globalSpeciesMap.get(aBlock); - if (blockMap != null) { - spManager.setDatablockSpeciesMap(blockMap); + globalSpeciesMap.put(dataBlock, entry.getValue()); + DataBlockSpeciesManager spManager = dataBlock.getDatablockSpeciesManager(); + if (spManager == null) { + String err = String.format("Data block %s does not have a species manager", entry.getKey()); + WarnOnce.showWarning("Missing species manager", err, WarnOnce.WARNING_MESSAGE); + continue; } + spManager.setDatablockSpeciesMap(entry.getValue()); } + +// globalSpeciesMap = readSpeciesMap; +// // no wupdate all datablock maps since they keep their own copies. +// ArrayList allDatablocks = PamController.getInstance().getDataBlocks(); +// for (PamDataBlock aBlock : allDatablocks) { +// DataBlockSpeciesManager spManager = aBlock.getDatablockSpeciesManager(); +// if (spManager == null) { +// continue; +// } +// DataBlockSpeciesMap blockMap = globalSpeciesMap.get(aBlock); +// if (blockMap != null) { +// spManager.setDatablockSpeciesMap(blockMap); +// } +// } + return true; } } diff --git a/src/tethys/species/swing/SpeciesMapIODialog.java b/src/tethys/species/swing/SpeciesMapIODialog.java new file mode 100644 index 00000000..64d7c22e --- /dev/null +++ b/src/tethys/species/swing/SpeciesMapIODialog.java @@ -0,0 +1,144 @@ +package tethys.species.swing; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +import javax.swing.JCheckBox; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamDialog; +import PamView.dialog.PamGridBagContraints; +import tethys.species.DataBlockSpeciesMap; +import tethys.species.GlobalSpeciesMap; + +/** + * dialog to select which species maps to import / export. + * @author dg50 + * + */ +public class SpeciesMapIODialog extends PamDialog { + + private static SpeciesMapIODialog singleInstance = null; + private GlobalSpeciesMap speciesMap; + + private JCheckBox everything; + private JCheckBox[] blockBoxes; + + private JPanel boxesPanel; + + /** + * @param parentFrame + * @param title + * @param hasDefault + */ + private SpeciesMapIODialog(Window parentFrame) { + super(parentFrame, "Map IO", true); + + boxesPanel = new JPanel(); + boxesPanel.setBorder(new TitledBorder("Select datablocks")); + + setDialogComponent(boxesPanel); + } + + public static GlobalSpeciesMap showDialog(Window parentFrame, GlobalSpeciesMap speciesMap, boolean export) { + if (singleInstance == null) { + singleInstance = new SpeciesMapIODialog(parentFrame); + } + if (speciesMap.getDatablockMaps().size() == 0) { + singleInstance.showWarning("No Data block species maps are defined"); + return speciesMap; + } + singleInstance.setTitle(export ? "Export species maps" : "Import species maps"); + singleInstance.setParams(speciesMap); + singleInstance.setVisible(true); + + return singleInstance.speciesMap; + } + + private void setParams(GlobalSpeciesMap speciesMap) { + this.speciesMap = speciesMap.clone(); + boxesPanel.removeAll(); + HashMap blockMaps = speciesMap.getDatablockMaps(); + Set> mapSet = blockMaps.entrySet(); + Iterator> iter = mapSet.iterator(); + + boxesPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + boxesPanel.add(everything = new JCheckBox("Select All"), c); + everything.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + enableControls(); + } + }); + blockBoxes = new JCheckBox[mapSet.size()]; + int iBox = 0; + while (iter.hasNext()) { + Entry item = iter.next(); + c.gridy++; + blockBoxes[iBox] = new JCheckBox(item.getKey()); + boxesPanel.add(blockBoxes[iBox], c); + iBox++; + } + + enableControls(); + } + + protected void enableControls() { + if (blockBoxes == null) { + return; + } + boolean selAll = everything.isSelected(); + for (int i = 0; i < blockBoxes.length; i++) { + blockBoxes[i].setEnabled(!selAll); + if (selAll) { + blockBoxes[i].setSelected(true); + } + } + } + + @Override + public boolean getParams() { + if (everything.isSelected()) { + return true; + } + HashMap blockMaps = speciesMap.getDatablockMaps(); + Set> mapSet = blockMaps.entrySet(); + Iterator> iter = mapSet.iterator(); + for(int i = 0; i < blockBoxes.length; i++) { + if (blockBoxes[i].isSelected() == false) { + String name = blockBoxes[i].getText(); + blockMaps.remove(name); + } + } +// int iBox = 0; +// while (iter.hasNext()) { +// if (blockBoxes[iBox].isSelected() == false) { +// iter.remove(); +// } +// iBox++; +// } + + return true; + } + + @Override + public void cancelButtonPressed() { + speciesMap = null; + } + + @Override + public void restoreDefaultSettings() { + everything.setSelected(true); + enableControls(); + } + +} diff --git a/src/tethys/swing/FancyClientButton.java b/src/tethys/swing/FancyClientButton.java index 9a72b16a..266e9811 100644 --- a/src/tethys/swing/FancyClientButton.java +++ b/src/tethys/swing/FancyClientButton.java @@ -18,6 +18,7 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.border.EmptyBorder; +import tethys.Collection; import tethys.TethysControl; import tethys.dbxml.DBXMLConnect; @@ -71,7 +72,7 @@ public class FancyClientButton extends JPanel { dropButton.setBorder(new EmptyBorder(dInsets)); } - String[] collections = DBXMLConnect.collections; + Collection[] collections = Collection.mainList(); collectionsMenu = new JPopupMenu(); boolean isP = tethysControl.getTethysExportParams().listDocsInPamguard; showBrowser = new JCheckBoxMenuItem("Show in Browser", isP == false); @@ -100,7 +101,7 @@ public class FancyClientButton extends JPanel { collectionsMenu.addSeparator(); for (int i = 0; i < collections.length; i++) { - JMenuItem menuItem = new JMenuItem(collections[i]); + JMenuItem menuItem = new JMenuItem(collections[i].collectionName()); menuItem.addActionListener(new OpenCollection(collections[i])); collectionsMenu.add(menuItem); } @@ -128,9 +129,9 @@ public class FancyClientButton extends JPanel { private class OpenCollection implements ActionListener { - private String collection; + private Collection collection; - public OpenCollection(String collection) { + public OpenCollection(Collection collection) { super(); this.collection = collection; } diff --git a/src/tethys/swing/documents/TethysDocumentTable.java b/src/tethys/swing/documents/TethysDocumentTable.java index 96cf8ef5..8b3b6a47 100644 --- a/src/tethys/swing/documents/TethysDocumentTable.java +++ b/src/tethys/swing/documents/TethysDocumentTable.java @@ -21,6 +21,8 @@ import PamController.PamController; import PamView.dialog.PamDialogPanel; import PamView.dialog.warn.WarnOnce; import PamView.tables.SwingTableColumnWidths; +import tethys.Collection; +import tethys.DocumentInfo; import tethys.TethysControl; import tethys.dbxml.TethysException; @@ -33,13 +35,13 @@ public class TethysDocumentTable implements PamDialogPanel { private TethysControl tethysControl; - private String collectionName; + private Collection collection; private JTable mainTable; private TableModel tableModel; - private ArrayList documentNames; + private ArrayList documentInfos; private JPanel mainPanel; @@ -49,24 +51,24 @@ public class TethysDocumentTable implements PamDialogPanel { * @param tethysControl * @param collectionName */ - public TethysDocumentTable(TethysControl tethysControl, String collectionName) { + public TethysDocumentTable(TethysControl tethysControl, Collection collection) { this.tethysControl = tethysControl; + this.collection = collection; mainPanel = new JPanel(new BorderLayout()); tableModel = new TableModel(); mainTable = new JTable(tableModel); scrollPane = new JScrollPane(mainTable); mainPanel.add(BorderLayout.CENTER, scrollPane); new SwingTableColumnWidths(tethysControl.getUnitName()+"TethysDocumentsTable", mainTable); - this.setCollectionName(collectionName); mainTable.addMouseListener(new TableMouse()); mainTable.setRowSelectionAllowed(true); mainTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); } public void updateTableData() { - documentNames = tethysControl.getDbxmlQueries().getCollectionDocumentList(collectionName); - if (documentNames != null) { - Collections.sort(documentNames); + documentInfos = tethysControl.getDbxmlQueries().getCollectionDocumentList(collection); + if (documentInfos != null) { + Collections.sort(documentInfos); } tableModel.fireTableDataChanged(); } @@ -90,21 +92,21 @@ public class TethysDocumentTable implements PamDialogPanel { } public void showPopupMenu(MouseEvent e) { - if (documentNames == null) { + if (documentInfos == null) { return; } int row = mainTable.getSelectedRow(); - if (row < 0|| row >= documentNames.size()) { + if (row < 0|| row >= documentInfos.size()) { return; } - String docName = documentNames.get(row); + DocumentInfo docInfo = documentInfos.get(row); JPopupMenu popMenu = new JPopupMenu(); - JMenuItem menuItem = new JMenuItem("Show document " + docName); + JMenuItem menuItem = new JMenuItem("Show document " + docInfo); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - showDocument(docName); + showDocument(docInfo); } }); popMenu.add(menuItem); @@ -113,11 +115,11 @@ public class TethysDocumentTable implements PamDialogPanel { int[] rows = mainTable.getSelectedRows(); if (rows != null && rows.length == 1) { // docName = documentNames.get(rows[0]); - menuItem = new JMenuItem("Delete document " + docName); + menuItem = new JMenuItem("Delete document " + docInfo); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - deleteDocument(docName); + deleteDocument(docInfo); } }); popMenu.add(menuItem); @@ -137,18 +139,18 @@ public class TethysDocumentTable implements PamDialogPanel { popMenu.show(e.getComponent(), e.getX(), e.getY()); } - private void showDocument(String docName) { - tethysControl.displayDocument(collectionName, docName); + private void showDocument(DocumentInfo docInfo) { + tethysControl.displayDocument(docInfo); } - private void deleteDocument(String docName) { - int ans = WarnOnce.showNamedWarning("deletedoc"+collectionName, PamController.getMainFrame(), "Delete document", - "Are you sure you want to delete the document " + docName, WarnOnce.OK_CANCEL_OPTION); + private void deleteDocument(DocumentInfo docInfo) { + int ans = WarnOnce.showNamedWarning("deletedoc "+ collection.collectionName(), PamController.getMainFrame(), "Delete document", + "Are you sure you want to delete the document " + docInfo, WarnOnce.OK_CANCEL_OPTION); if (ans == WarnOnce.OK_OPTION) { try { - tethysControl.getDbxmlConnect().removeDocument(collectionName, docName); + tethysControl.getDbxmlConnect().removeDocument(docInfo.getCollection().collectionName(), docInfo.getDocumentId()); } catch (TethysException e) { - System.out.println("Failed to delete " + docName); + System.out.println("Failed to delete " + docInfo); System.out.println(e.getMessage()); } } @@ -156,7 +158,7 @@ public class TethysDocumentTable implements PamDialogPanel { } private void deleteDocuments(int[] rows) { - int ans = WarnOnce.showNamedWarning("deletedoc"+collectionName, PamController.getMainFrame(), "Delete documents", + int ans = WarnOnce.showNamedWarning("deletedoc "+collection.collectionName(), PamController.getMainFrame(), "Delete documents", "Are you sure you want to delete multiple documents ", WarnOnce.OK_CANCEL_OPTION); if (ans != WarnOnce.OK_OPTION) { return; @@ -165,16 +167,16 @@ public class TethysDocumentTable implements PamDialogPanel { * make a new list before anything is deleted since the * man list will get updated during deletion and be out of date. */ - String[] docNames = new String[rows.length]; + DocumentInfo[] docInfos = new DocumentInfo[rows.length]; for (int i = 0; i < rows.length; i++) { - docNames[i] = documentNames.get(rows[i]); + docInfos[i] = documentInfos.get(rows[i]); } // now it's safe to delete them. - for (int i = 0; i < docNames.length; i++) { + for (int i = 0; i < docInfos.length; i++) { try { - tethysControl.getDbxmlConnect().removeDocument(collectionName, docNames[i]); + tethysControl.getDbxmlConnect().removeDocument(docInfos[i].getCollection().collectionName(), docInfos[i].getDocumentId()); } catch (TethysException e) { - System.out.println("Failed to delete " + docNames[i]); + System.out.println("Failed to delete " + docInfos[i]); System.out.println(e.getMessage()); } } @@ -183,14 +185,14 @@ public class TethysDocumentTable implements PamDialogPanel { private class TableModel extends AbstractTableModel { - private String[] columnNames = {"", "Document Id/Name"}; + private String[] columnNames = {"", "Document Name", "Document Id"}; @Override public int getRowCount() { - if (documentNames == null) { + if (documentInfos == null) { return 0; } - return documentNames.size(); + return documentInfos.size(); } @Override @@ -200,14 +202,17 @@ public class TethysDocumentTable implements PamDialogPanel { @Override public Object getValueAt(int rowIndex, int columnIndex) { - if (documentNames == null) { + if (documentInfos == null) { return null; } + DocumentInfo docInfo = documentInfos.get(rowIndex); switch (columnIndex) { case 0: - return rowIndex+1; + return rowIndex; case 1: - return documentNames.get(rowIndex); + return docInfo.getDocumentName(); + case 2: + return docInfo.getDocumentId(); } return null; } @@ -241,15 +246,13 @@ public class TethysDocumentTable implements PamDialogPanel { /** * @return the collectionName */ - public String getCollectionName() { - return collectionName; + public Collection getCollection() { + return collection; } - /** - * @param collectionName the collectionName to set - */ - public void setCollectionName(String collectionName) { - this.collectionName = collectionName; + public void setCollection(Collection collection) { + this.collection = collection; updateTableData(); } + } diff --git a/src/tethys/swing/documents/TethysDocumentsFrame.java b/src/tethys/swing/documents/TethysDocumentsFrame.java index b08ba36e..a376d6f1 100644 --- a/src/tethys/swing/documents/TethysDocumentsFrame.java +++ b/src/tethys/swing/documents/TethysDocumentsFrame.java @@ -3,6 +3,7 @@ package tethys.swing.documents; import java.awt.Window; import PamView.dialog.PamDialog; +import tethys.Collection; import tethys.TethysControl; public class TethysDocumentsFrame extends PamDialog { @@ -23,12 +24,12 @@ public class TethysDocumentsFrame extends PamDialog { getCancelButton().setText("Close"); } - public static void showTable(Window parentFrame, TethysControl tethysControl, String collectionName) { + public static void showTable(Window parentFrame, TethysControl tethysControl, Collection collection) { if (singleInstance == null) { singleInstance = new TethysDocumentsFrame(parentFrame, tethysControl); } - singleInstance.documentsTable.setCollectionName(collectionName); - singleInstance.setTitle(collectionName + " Documents"); + singleInstance.documentsTable.setCollection(collection); + singleInstance.setTitle(collection.collectionName() + " Documents"); singleInstance.setVisible(true); } From 275a53c042d0ced3761433a11b2783bc3d05878f Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sun, 3 Dec 2023 18:21:09 +0000 Subject: [PATCH 57/65] Calibrations display display of calibrations documents + some general tidying up of document management and display. --- .classpath | 3 +- src/tethys/Collection.java | 2 +- src/tethys/DocumentInfo.java | 1 + src/tethys/DocumentNilusObject.java | 32 + src/tethys/TethysControl.java | 28 +- .../calibration/CalibrationHandler.java | 200 +++++- .../calibration/swing/CalibrationsPanel.java | 249 ++++++- src/tethys/dbxml/DBXMLConnect.java | 20 +- src/tethys/dbxml/DBXMLQueries.java | 50 +- src/tethys/deployment/DeploymentHandler.java | 11 + src/tethys/detection/DetectionsHandler.java | 4 +- src/tethys/niluswraps/NilusUnpacker.java | 608 ++++++++++++++++++ src/tethys/niluswraps/PDeployment.java | 4 +- src/tethys/niluswraps/TethysCollections.java | 13 - src/tethys/pamdata/AutoTethysProvider.java | 4 +- .../swing/DatablockDetectionsPanel.java | 6 +- .../swing/PAMGuardDeploymentsTable.java | 6 +- src/tethys/swing/TethysMainPanel.java | 9 +- .../swing/documents/TethysDocumentTable.java | 2 +- 19 files changed, 1182 insertions(+), 70 deletions(-) create mode 100644 src/tethys/DocumentNilusObject.java create mode 100644 src/tethys/niluswraps/NilusUnpacker.java delete mode 100644 src/tethys/niluswraps/TethysCollections.java diff --git a/.classpath b/.classpath index 7737aa09..8f3f9191 100644 --- a/.classpath +++ b/.classpath @@ -6,8 +6,9 @@ - + + diff --git a/src/tethys/Collection.java b/src/tethys/Collection.java index 41bff1c7..0b9b1d58 100644 --- a/src/tethys/Collection.java +++ b/src/tethys/Collection.java @@ -33,7 +33,7 @@ public enum Collection { case Detections: return "Detections"; // this one is plural ! case Localizations: - return "Localization"; + return "Localize"; case SpeciesAbbreviations: return "SpeciesAbbreviation"; case Ensembles: diff --git a/src/tethys/DocumentInfo.java b/src/tethys/DocumentInfo.java index 438aefb7..7ad2d750 100644 --- a/src/tethys/DocumentInfo.java +++ b/src/tethys/DocumentInfo.java @@ -11,6 +11,7 @@ public class DocumentInfo implements Comparable { private Collection collection; private String documentName; private String documentId; + /** * @param collection * @param documentName diff --git a/src/tethys/DocumentNilusObject.java b/src/tethys/DocumentNilusObject.java new file mode 100644 index 00000000..c6b5a402 --- /dev/null +++ b/src/tethys/DocumentNilusObject.java @@ -0,0 +1,32 @@ +package tethys; + +/** + * information about a document AND the nilus object to go with it. + * @author dg50 + * + * @param + */ +public class DocumentNilusObject extends DocumentInfo { + + private T nilusObject; + + public DocumentNilusObject(Collection collection, String documentName, String documentId, T nilusObject) { + super(collection, documentName, documentId); + this.nilusObject = nilusObject; + } + + /** + * @return the nilusObject + */ + public T getNilusObject() { + return nilusObject; + } + + /** + * @param nilusObject the nilusObject to set + */ + public void setNilusObject(T nilusObject) { + this.nilusObject = nilusObject; + } + +} diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 426e867e..4db92596 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -166,7 +166,17 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet openTethysClient(); } }); + tethysMenu.add(menuItem); + menuItem = new JMenuItem("Open temp document folder"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + openTempDocuments(); + } + }); + tethysMenu.add(menuItem); + JMenuItem collections = new JMenu("Collections"); Collection[] mainCollections = Collection.mainList(); @@ -217,6 +227,22 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet return tethysMenu; } + protected void openTempDocuments() { + File tempFolder = dbxmlConnect.checkTempFolder(); + if (tempFolder == null) { + WarnOnce.showWarning("Tethys Error", "Unable to obtain a temporary folder name", WarnOnce.WARNING_MESSAGE); + return; + } + try { +// String cmd = "explorer.exe /select," + tempFolder.getAbsolutePath() + File.separator; +// Runtime.getRuntime().exec(cmd); + Desktop.getDesktop().open(tempFolder); + } + catch(Exception e) { + e.printStackTrace(); + } + } + public void showProjectDeploymentsDialog() { ProjectDeploymentsDialog.showDialog(getGuiFrame(), this); } @@ -591,7 +617,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet public void displayDocument(DocumentInfo docInfo) { String collectionName = docInfo.getCollection().collectionName(); - String docId = docInfo.getDocumentId(); + String docId = docInfo.getDocumentName(); displayDocument(collectionName, docId); } /** diff --git a/src/tethys/calibration/CalibrationHandler.java b/src/tethys/calibration/CalibrationHandler.java index 22418f47..1823afbf 100644 --- a/src/tethys/calibration/CalibrationHandler.java +++ b/src/tethys/calibration/CalibrationHandler.java @@ -1,9 +1,18 @@ package tethys.calibration; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; import java.util.List; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.w3c.dom.Document; + import Acquisition.AcquisitionControl; -import Acquisition.AcquisitionParameters; import Acquisition.AcquisitionProcess; import Array.ArrayManager; import Array.Hydrophone; @@ -11,6 +20,8 @@ import Array.PamArray; import PamController.PamController; import PamController.soundMedium.GlobalMedium; import PamController.soundMedium.GlobalMedium.SoundMedium; +import PamUtils.PamCalendar; +import dbxml.Queries; import PamController.soundMedium.GlobalMediumManager; import nilus.Calibration; import nilus.Calibration.FrequencyResponse; @@ -19,31 +30,92 @@ import nilus.Helper; import nilus.MetadataInfo; import nilus.QualityValueBasic; import nilus.ResponsibleParty; +import tethys.Collection; +import tethys.DocumentInfo; +import tethys.DocumentNilusObject; import tethys.TethysControl; import tethys.TethysState; import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.TethysException; -import tethys.niluswraps.PDeployment; +import tethys.niluswraps.NilusUnpacker; import tethys.pamdata.AutoTethysProvider; public class CalibrationHandler implements TethysStateObserver { private TethysControl tethysControl; + private ArrayList> calibrationDataBlock; + /** * @param tethysControl */ public CalibrationHandler(TethysControl tethysControl) { this.tethysControl = tethysControl; + calibrationDataBlock = new ArrayList(); tethysControl.addStateObserver(this); } @Override public void updateState(TethysState tethysState) { - // TODO Auto-generated method stub + switch (tethysState.stateType) { + case EXPORTING: + break; + case NEWPAMGUARDSELECTION: + case NEWPROJECTSELECTION: + case TRANSFERDATA: + case UPDATEMETADATA: + case UPDATESERVER: + updateDocumentsList(); + default: + break; + } + } + + private void updateDocumentsList() { + ArrayList docsList = getArrayCalibrations(); + // now immediately read the calibrations in again. + calibrationDataBlock.clear();; + NilusUnpacker unpacker = new NilusUnpacker(); + for (DocumentInfo aDoc : docsList) { + Queries queries = tethysControl.getDbxmlConnect().getTethysQueries(); + String result = null; + Calibration calObj = null; + try { + result = queries.getDocument(Collection.Calibrations.toString(), aDoc.getDocumentName()); + if (result != null) { + // create a document and convert it into a Nilus calibrations document. + Document doc = tethysControl.getDbxmlQueries().convertStringToXMLDocument(result); + if (doc == null) { + System.out.println("Unable to convert Calibration result to Document\n " + result); + continue; + } + calObj = (Calibration) unpacker.unpackDocument(doc, Calibration.class); + if (calObj == null) { + System.out.println("Unable to convert Calibration document to nilus object\n " + result); + continue; + } + } + long t = System.currentTimeMillis(); + try { + XMLGregorianCalendar gt = calObj.getMetadataInfo().getDate(); + if (gt != null) { + t = TethysTimeFuncs.millisFromGregorianXML(gt); + } + } + catch (Exception e) { + + } + DocumentNilusObject calDataUnit = new DocumentNilusObject(Collection.Calibrations, aDoc.getDocumentName(), calObj.getId(), calObj); + calibrationDataBlock.add(calDataUnit); +// System.out.println(result); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } } public int exportAllCalibrations() { @@ -54,9 +126,10 @@ public class CalibrationHandler implements TethysStateObserver { for (int i = 0; i < nPhone; i++) { // String docName = getHydrophoneId(i); Calibration calDoc = createCalibrationDocument(i); + String calDocName = getDocumentName(calDoc, i); boolean ok = false; try { - ok = dbxml.postAndLog(calDoc); + ok = dbxml.postAndLog(calDoc, calDocName); } catch (TethysException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -70,6 +143,54 @@ public class CalibrationHandler implements TethysStateObserver { return nExport; } + /** + * Get a name for the document, which is a bit like the id within + * the document, but also contain a yymmdd data string. + * @param calDoc + * @param i channel + * @return document name + */ + private String getDocumentName(Calibration calDoc, int iChan) { + long docDate = System.currentTimeMillis(); + XMLGregorianCalendar date = calDoc.getMetadataInfo().getDate(); + if (date != null) { + docDate = TethysTimeFuncs.millisFromGregorianXML(date); + } + String dateStr = formatDate(docDate); + String name = String.format("%s_%s_ch%d", getCalibrationDocumentRoot(), dateStr, iChan); + return name; + } + /** + * Format the data in the dd MMMM yyyy format + * @param timeInMillis time in milliseconds + * @return formatted string. + */ + public static String formatDate(long timeInMillis) { + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(timeInMillis); + c.setTimeZone(PamCalendar.defaultTimeZone); + DateFormat df = new SimpleDateFormat("yyMMdd"); + df.setTimeZone(PamCalendar.defaultTimeZone); + Date d = c.getTime(); + return df.format(d); + } + + + /** + * Get a start of name for a calibration document. This will be used in the document name + * with a date and a channel, and the document Id just of the root and the channel. + * @return root string for document names and document id's. + */ + public String getCalibrationDocumentRoot() { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + if (array == null) { + return null; + } + String root = String.format("%s %s", array.getInstrumentType(), array.getInstrumentId()); + root = root.replace(" ", "_"); + return root; + } + /** * Create a calibration document for a single hydrophone channel. * @param pDeployment deployment, for cross referencing. @@ -80,21 +201,6 @@ public class CalibrationHandler implements TethysStateObserver { AcquisitionControl daqControl = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.unitType); return createCalibrationDocument(daqControl, channelIndex); } - - /** - * Get an id based on the instrument identifiers and channel number. - * @param channelIndex - * @return id string - instrument type + instrument id + channel - */ - public String getHydrophoneId(int channelIndex) { - PamArray array = ArrayManager.getArrayManager().getCurrentArray(); - if (array == null) { - return null; - } - String id = String.format("%s_%s_ch%02d", array.getInstrumentType(), array.getInstrumentId(), channelIndex); - id = id.replace(" ", "_"); - return id; - } /** * Create a calibration document for a single hydrophone channel. @@ -135,7 +241,7 @@ public class CalibrationHandler implements TethysStateObserver { GlobalMediumManager mediumManager = PamController.getInstance().getGlobalMediumManager(); SoundMedium currentMedium = mediumManager.getCurrentMedium(); - double dbRef = GlobalMedium.getdBreference(currentMedium); // probably in Pa, so multiply by 1e6. + double dbRef = GlobalMedium.getdBreference(currentMedium); // probably in Pa, so multiply by 1e6. 20 (air) or 0 (water) /** * The calibration id can be a bit tricky, it will need to be cross referenced from the @@ -158,9 +264,10 @@ public class CalibrationHandler implements TethysStateObserver { // id = String.format("%d", channelIndex); calibration.setId(id); calibration.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); - calibration.setType(GlobalMedium.getRecieverString(currentMedium, false, false)); +// calibration.setType(GlobalMedium.getRecieverString(currentMedium, false, false)); + calibration.setType("end-to-end"); calibration.setIntensityReferenceUPa(AutoTethysProvider.roundSignificantFigures(dbRef*1e6,3)); - String sensRef = GlobalMedium.getdBRefString(currentMedium); +// String sensRef = GlobalMedium.getdBRefString(currentMedium); // it doesn't like this since it has a unicode character. Leave it or change the micro to 'u' // calibration.setSensitivityReference(sensRef); calibration.setSensitivityDBV(hSens+preampGain); @@ -176,17 +283,66 @@ public class CalibrationHandler implements TethysStateObserver { db.add(Double.valueOf(hSens+preampGain)); MetadataInfo metaInf = calibration.getMetadataInfo(); + if (metaInf == null) { + metaInf = new MetadataInfo(); + calibration.setMetadataInfo(metaInf); + } metaInf.setDate(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); metaInf.setUpdateFrequency("as-needed"); ResponsibleParty contact = metaInf.getContact(); + if (contact == null) { + contact = new ResponsibleParty(); + metaInf.setContact(contact); + } contact.setIndividualName("Unknown"); contact.setOrganizationName("unknown"); QualityAssurance qa = calibration.getQualityAssurance(); + if (qa == null) { + qa = new QualityAssurance(); + calibration.setQualityAssurance(qa); + } qa.setQuality(QualityValueBasic.VALID); qa.setComment("Unknown calibration"); return calibration; } + + /** + * Get an id based on the instrument identifiers and channel number. + * This is the internal id of the document, not the document name which + * includes an additional date part in the name. + * @param channelIndex + * @return id string - instrument type + instrument id + channel + */ + public String getHydrophoneId(int channelIndex) { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + if (array == null) { + return null; + } + String id = String.format("%s_ch%02d", getCalibrationDocumentRoot(), channelIndex); + id = id.replace(" ", "_"); + return id; + } + + /** + * @return the calibrationDataBlock + */ + public ArrayList> getCalibrationDataList() { + return calibrationDataBlock; + } + + private ArrayList getArrayCalibrations() { + ArrayList allCals = tethysControl.getDbxmlQueries().getCollectionDocumentList(Collection.Calibrations); + String prefix = getCalibrationDocumentRoot(); + // find doc names that have that root. + ArrayList theseCals = new ArrayList<>(); + for (DocumentInfo aDoc : allCals) { + if (aDoc.getDocumentName().startsWith(prefix)) { + theseCals.add(aDoc); + } + } + return theseCals; + } } diff --git a/src/tethys/calibration/swing/CalibrationsPanel.java b/src/tethys/calibration/swing/CalibrationsPanel.java index 7dace048..b084c5d6 100644 --- a/src/tethys/calibration/swing/CalibrationsPanel.java +++ b/src/tethys/calibration/swing/CalibrationsPanel.java @@ -1,5 +1,252 @@ package tethys.calibration.swing; -public class CalibrationsPanel { +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +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 javax.xml.datatype.XMLGregorianCalendar; + +import PamController.PamController; +import PamUtils.PamCalendar; +import PamView.dialog.warn.WarnOnce; +import PamView.panel.PamPanel; +import PamView.tables.SwingTableColumnWidths; +import nilus.Calibration; +import tethys.Collection; +import tethys.DocumentNilusObject; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysState.StateType; +import tethys.TethysTimeFuncs; +import tethys.calibration.CalibrationHandler; +import tethys.dbxml.TethysException; +import tethys.swing.TethysGUIPanel; + +public class CalibrationsPanel extends TethysGUIPanel { + + private CalibrationHandler calibrationHandler; + + private CalibrationsTableModel calTableModel; + + private JPanel mainPanel; + + private JTable calTable; + + private TethysControl tethysControl; + + /** + * @param calibrationHandler + */ + public CalibrationsPanel(TethysControl tethysControl, CalibrationHandler calibrationHandler) { + super(tethysControl); + this.tethysControl = tethysControl; + this.calibrationHandler = calibrationHandler; + calTableModel = new CalibrationsTableModel(); + calTable = new JTable(calTableModel); + calTable.setRowSelectionAllowed(true); + calTable.addMouseListener(new TableMouse()); + + JScrollPane scrollPane = new JScrollPane(calTable); + + mainPanel = new PamPanel(new BorderLayout()); + mainPanel.setBorder(new TitledBorder("Instrument calibration information")); + mainPanel.add(BorderLayout.CENTER, scrollPane); + + calTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + new SwingTableColumnWidths(tethysControl.getUnitName()+"CalibrationsTable", calTable); + + } + + + @Override + public JComponent getComponent() { + return mainPanel; + } + + + @Override + public void updateState(TethysState tethysState) { + super.updateState(tethysState); + calTableModel.fireTableDataChanged(); + } + + 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); + } + } + + } + + public void showPopupMenu(MouseEvent e) { + int[] rows = calTable.getSelectedRows(); + if (rows == null || rows.length == 0) { + return; + } + int n = rows.length; + DocumentNilusObject doc = calibrationHandler.getCalibrationDataList().get(rows[0]); + + JPopupMenu popMenu = new JPopupMenu(); + JMenuItem menuItem; + if (n == 1) { + menuItem = new JMenuItem("Show document " + doc.getDocumentName()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showCalibration(doc); + } + }); + popMenu.add(menuItem); + } + if (n > 1) { + menuItem = new JMenuItem("Delete selected documents"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteCalibrations(rows); + } + }); + popMenu.add(menuItem); + } + else { + menuItem = new JMenuItem("Delete document " + doc.getDocumentName()); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + deleteCalibration(doc); + } + }); + popMenu.add(menuItem); + } + popMenu.show(e.getComponent(), e.getX(), e.getY()); + } + + protected void deleteCalibration(DocumentNilusObject doc) { + String docName = doc.getDocumentName(); + int ans = WarnOnce.showNamedWarning("delete doc " + Collection.Calibrations.collectionName(), + PamController.getMainFrame(), "Delete document", + "Are you sure you want to delete the document " + docName, WarnOnce.OK_CANCEL_OPTION); + if (ans == WarnOnce.OK_OPTION) { + try { + tethysControl.getDbxmlConnect().removeDocument(Collection.Calibrations.collectionName(), docName); + } catch (TethysException e) { + System.out.println("Failed to delete " + docName); + System.out.println(e.getMessage()); + } + } + updateEverything(); + calTableModel.fireTableDataChanged(); + } + + + protected void showCalibration(DocumentNilusObject docInfo) { + tethysControl.displayDocument(docInfo); + + } + + + protected void deleteCalibrations(int[] rows) { + String msg = String.format("Are you sure you want to delete %d calibrations documents ?", rows.length); + int ans = WarnOnce.showNamedWarning("Deletemanycalibrations", PamController.getMainFrame(), "Delete multiple documents", msg, WarnOnce.OK_CANCEL_OPTION); + if (ans != WarnOnce.OK_OPTION) { + return; + } + for (int i = 0; i < rows.length; i++) { + String docName = null; + try { + DocumentNilusObject doc = calibrationHandler.getCalibrationDataList().get(rows[i]); + docName = doc.getDocumentName(); + tethysControl.getDbxmlConnect().removeDocument(Collection.Calibrations.collectionName(), docName); + } catch (TethysException e) { + System.out.println("Failed to delete " + docName); + System.out.println(e.getMessage()); + } + } + + updateEverything(); + + } + + private void updateEverything() { + calibrationHandler.updateState(new TethysState(StateType.TRANSFERDATA)); + } + + class CalibrationsTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + + private String[] columnNames = {"Document", "Id", "Date", "End to End", "Hydrophone", "Preamp"}; + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + DocumentNilusObject dataUnit = null; + try { + dataUnit = calibrationHandler.getCalibrationDataList().get(rowIndex); + } + catch (Exception e) { + return null; + } + if (dataUnit == null) { + return null; + } + Calibration cal = dataUnit.getNilusObject(); + switch (columnIndex) { + case 0: + return dataUnit.getDocumentName(); + case 1: + return cal.getId(); + case 2: + XMLGregorianCalendar ts = cal.getTimeStamp(); + if (ts == null) { + return null; + } + long ms = TethysTimeFuncs.millisFromGregorianXML(ts); + return PamCalendar.formatDBDate(ms); + case 3: + return cal.getSensitivityDBFS(); + case 4: + return cal.getType(); +// return String.format("%3.1fdB %s", cal.getSensitivityV(), cal.getType()); + } + return null; + } + + @Override + public int getRowCount() { + return calibrationHandler.getCalibrationDataList().size(); + } + + @Override + public int getColumnCount() { + return columnNames.length; + } + + @Override + public String getColumnName(int column) { + return columnNames[column]; + } + + + } } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index a00098a5..baea57f7 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -134,13 +134,15 @@ public class DBXMLConnect { /** * I don't think this should ever be used since everything goes a bit pear - * shaped if the documentName isn't the same as the Id. + * shaped if the documentName isn't the same as the Id. However, for Calibration + * documents this is no longer the case, since a Calibration can have multiple + * entries on different dates, so allow it ! * @param nilusObject * @param documentName * @return * @throws TethysException */ - private boolean postAndLog(Object nilusObject, String documentName) throws TethysException + public boolean postAndLog(Object nilusObject, String documentName) throws TethysException { TethysException e = null; boolean success = false; @@ -283,15 +285,15 @@ An error will throw an exception. /** * Remove a document based on a collection name and a cdocument Id. - * @param collection - * @param docId + * @param collection collection name. + * @param documentName document name (not the internal Document Id) * @return * @throws TethysException */ - public boolean removeDocument(String collection, String docId) throws TethysException { + public boolean removeDocument(String collection, String documentName) throws TethysException { try { // docId = "SoundTrap_600_HF_7129_ch00"; - Object result = jerseyClient.removeDocument(collection, docId ); + Object result = jerseyClient.removeDocument(collection, documentName ); /** * Return from a sucessful delete is something like * @@ -304,7 +306,7 @@ An error will throw an exception. } catch (Exception e) { // System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); - String msg = String.format("Error deleting %s:%s", collection, docId); + String msg = String.format("Error deleting %s:%s", collection, documentName); throw new TethysException(msg, e.getLocalizedMessage()); } return true; @@ -419,7 +421,7 @@ C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xmlnot: 0 b * temp folder + /PAMGuardTethys. Files will be left here until PAMGUard * exits then should delete automatically */ - private void checkTempFolder() { + public File checkTempFolder() { String javaTmpDirs = System.getProperty("java.io.tmpdir") + File.separator + "PAMGuardTethys"; File tempDir = new File(javaTmpDirs); @@ -432,7 +434,7 @@ C:\Users\dg50\AppData\Local\Temp\PAMGuardTethys\20080311_2DSimplex_0.xmlnot: 0 b if (tempDirectory == null) { tempDirectory = new File(System.getProperty("java.io.tmpdir")); } - + return tempDirectory; } /** diff --git a/src/tethys/dbxml/DBXMLQueries.java b/src/tethys/dbxml/DBXMLQueries.java index 0b5fc9f8..b0ce438f 100644 --- a/src/tethys/dbxml/DBXMLQueries.java +++ b/src/tethys/dbxml/DBXMLQueries.java @@ -228,15 +228,45 @@ public class DBXMLQueries { String toStrip = "dbxml:///"+collection.collectionName()+"/"; for (int i = 0; i < n; i++) { Node aNode = returns.item(i); + String nameStr = null; + String id = null; + NodeList kids = aNode.getChildNodes(); + for (int k = 0; k < kids.getLength(); k++) { + Node kidNode = kids.item(k); + String name = kidNode.getNodeName(); + String cont = kidNode.getTextContent(); + switch(name) { + case "#text": + nameStr = cont; + nameStr = nameStr.replaceFirst(toStrip, ""); + break; + case "Id": + id = kidNode.getTextContent(); + break; + default: + System.out.printf("Uknonwn node in Collection list %s item %d, Node %d name %s content %s\n", + collection, i, k, name, cont); + } + } +// if (i > 428) { +// System.out.println("MARU cal doc"); +// } // this is the doc name with a load of stuff in front, // e.g. dbxml:///Deployments/1705_Array-2017-09-261705_Array-2017-09-26 - String nameStr = aNode.getTextContent(); - nameStr = nameStr.replaceFirst(toStrip, ""); - String id = null; - if (aNode instanceof Element) { - id = getElementData((Element) aNode, "Id"); + if (nameStr == null) { + nameStr = aNode.getTextContent(); + nameStr = nameStr.replaceFirst(toStrip, ""); } - +// if (aNode instanceof Element) { + // nameStr = getElementData((Element) aNode, "#text"); + // } + + if (id == null) { + if (aNode instanceof Element) { + id = getElementData((Element) aNode, "Id"); + } + } + DocumentInfo docInfo = new DocumentInfo(collection, nameStr, id); documentInfos.add(docInfo); // System.out.println(nameStr + " : " + id); @@ -285,6 +315,10 @@ public class DBXMLQueries { // return docIds; } + /** + * Get a list of project names. + * @return + */ public ArrayList getProjectNames() { String projectQuery = "{\"return\":[\"Deployment/Project\"],\"select\":[],\"enclose\":1}"; @@ -957,12 +991,12 @@ public class DBXMLQueries { String encounterGap_m = getElementAttribute(result, "Effort.Kind.Granularity", "EncounterGap_m"); String firstBinStart = getElementAttribute(result, "Effort.Kind.Granularity", "FirstBinStart"); try { - granularityType.setBinSizeM(Double.valueOf(binSize_m)); + granularityType.setBinSizeMin(Double.valueOf(binSize_m)); } catch (NumberFormatException e) { } try { - granularityType.setEncounterGapM(Double.valueOf(encounterGap_m)); + granularityType.setEncounterGapMin(Double.valueOf(encounterGap_m)); } catch (NumberFormatException e) { } diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 93a499fa..3c0a4dfe 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -48,6 +48,7 @@ import nilus.Deployment.Instrument; import nilus.Deployment.SamplingDetails; import nilus.Deployment.Sensors; import nilus.DeploymentRecoveryDetails; +import nilus.DescriptionType; import nilus.GeometryTypeM; import nilus.Helper; import nilus.UnknownSensor; @@ -764,6 +765,16 @@ public class DeploymentHandler implements TethysStateObserver { TethysLocationFuncs.getTrackAndPositionData(deployment); getProjectData(deployment); + + DescriptionType description = deployment.getDescription(); + if (description == null ) { + description = new DescriptionType(); + deployment.setDescription(description); + description.setAbstract("No abstract"); + description.setMethod("no methods"); + description.setObjectives("No objectives"); + } +// description.set addSamplingDetails(deployment, recordingPeriod); diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 7638c2d2..b7c77add 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -31,6 +31,7 @@ import nilus.DetectionGroup; import nilus.Detections; import nilus.GranularityEnumType; import nilus.Helper; +import tethys.Collection; import tethys.TethysControl; import tethys.TethysTimeFuncs; import tethys.dbxml.DBXMLConnect; @@ -38,7 +39,6 @@ import tethys.dbxml.TethysException; import tethys.deployment.DeploymentHandler; import tethys.niluswraps.PDeployment; import tethys.niluswraps.PDetections; -import tethys.niluswraps.TethysCollections; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.pamdata.TethysDataProvider; @@ -521,7 +521,7 @@ public class DetectionsHandler { */ while (true) { fullId = String.format("%s_%d", prefix, uniqueDetectionsId++); - if (!tethysControl.getDbxmlQueries().documentExists(TethysCollections.Detections.toString(), fullId)) { + if (!tethysControl.getDbxmlQueries().documentExists(Collection.Detections.toString(), fullId)) { break; } } diff --git a/src/tethys/niluswraps/NilusUnpacker.java b/src/tethys/niluswraps/NilusUnpacker.java new file mode 100644 index 00000000..2b5b4a46 --- /dev/null +++ b/src/tethys/niluswraps/NilusUnpacker.java @@ -0,0 +1,608 @@ +package tethys.niluswraps; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Parameter; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import nilus.ChannelInfo.Sampling.Regimen; +import nilus.Deployment; +import nilus.Helper; +import nilus.QualityValueBasic; +import tethys.TethysTimeFuncs; + +/** + * Functions to automatically unpack a document from Tethys into a nilus object. + * @author dg50 + * + */ +public class NilusUnpacker { + + public Object unpackDocument(Document doc, Class nilusClass) throws SecurityException { + + Object nilusObject = null; + nilusObject = unpackNilusClass(nilusClass, doc.getDocumentElement(), true); + return nilusObject; + + } + + /** + * Unpack an xml element into a nilus class. Should recursively work through + * all sub elements and lists, etc. + * @param nilusClass class to unpack to + * @param nilusElement xml element + * @return nilus object. + */ + private Object unpackNilusClass(Class nilusClass, Node nilusElement, boolean useHelper) { + + Object nilusObject = null; + /* + * First, find the constructor. Every class should have a zero argument + * constructor. + */ + QualityValueBasic qb = null; + + Constructor nilusConstructor = null;; + try { + nilusConstructor = nilusClass.getConstructor(null); + nilusObject = nilusConstructor.newInstance(null); + } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { +// e.printStackTrace(); +// return null; + } + + if (useHelper) { + try { + Helper.createRequiredElements(nilusObject); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + e.printStackTrace(); + return null; + } + } + + return unpackNilusObject(nilusObject, nilusElement); + } + private Object unpackNilusObject(Object nilusObject, Node nilusElement) { + +// if (nilusConstructor == null) { +// // try to find any constructor and see what it takes. +// Constructor[] allConstructors = nilusClass.getConstructors(); +// if (allConstructors.length == 0) { +// System.out.println("Nilus unpacker: Unable to find constructor for class " + nilusClass.toString()); +// return null; +// } +// nilusConstructor = allConstructors[0]; // only try the first one for now. +// Parameter[] params = nilusConstructor.getParameters(); +// if (params.length == 1 && params[0].getType() == String.class) { +// try { +// nilusObject = nilusConstructor.newInstance(nilusElement.getTextContent()); +// } catch (InstantiationException | IllegalAccessException | IllegalArgumentException +// | InvocationTargetException | DOMException e) { +// e.printStackTrace(); +//// return null; +// } +// } +// } +// if (nilusObject == null) { +// System.out.println("Nilus unpacker: Unable to construct nilus object " + nilusClass.toString()); +// return null; +// } + if (nilusObject == null) { + return null; + } + + Class nilusClass = nilusObject.getClass(); + + /** + * Get the declared fields for this class. May need to worry about + * inherited fields ? . + */ + Field[] fields = nilusClass.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + if (isFieldFinal(fields[i])) { + continue; + } + String fieldName = fields[i].getName(); + String elementName = fieldName; + // now try to find an annotation for the name, which might be + // different. + XmlElement an = fields[i].getAnnotation(XmlElement.class); + if (an == null) { +// System.out.printf("No XmlElement annotation found for field %s in class %s\n", +// nilusClass.getName(), fieldName); + } + boolean required = false; + if (an != null) { + required = an.required(); + elementName = an.name(); + } + // find the xml element for it in parent + Element child = findChild(nilusElement, elementName); + /** + * Here we can put in some bespoke 'fixes', e.g. if the name in the xml has + * changed for some reason. + */ + if (child == null) { + String altName = alternateElName(fieldName); + if (altName != null) { + child = findChild(nilusElement, altName); + } + } + + /** + * It is OK for a child not to exist, since not all elements are required, so + * it's possible that they are simply not there. + */ + if (child == null) { + if (required) { + System.out.printf("Field %s in class %s is required but cannot be found\n", fieldName, nilusClass.getName()); + } + continue; + } + String childName = child.getNodeName(); + + Object exObject = null; // this is the object (which may be a primitive) we're going to give to the setter. + + if (List.class.isAssignableFrom(fields[i].getType())) { + exObject = getNilusList(nilusObject, fields[i], (Element) nilusElement); + } + else { + // find a setter for it. + Method setter = findSetter(nilusClass, fieldName); + // System.out.printf("Field %s with element %s and setter %s\n", fieldName, childName, setter); + if (setter == null) { + System.out.printf("No setter available for field %s and element %s\n", fieldName, elementName); + continue; // eventually do something more intelligent here. + } + Parameter[] params = setter.getParameters(); + Parameter setterParam = params[0]; + Class paramClass = setterParam.getType(); + + exObject = getElementObject(nilusObject, fieldName, paramClass, child); + if (exObject != null) { + try { + // every nilus setter should have a single argument. + setter.invoke(nilusObject, exObject); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + e.printStackTrace(); + } + } + } + } + + return nilusObject; + } + + private boolean isFieldFinal(Field field) { + int mods = field.getModifiers(); + return Modifier.isFinal(mods); + } + + /** + * Unpack the child element into the given parameter class. The element will + * either be a primitive type, or a class, which has to be one of the nulus classes + * so should follow nilus rules of constructors, setters, etc. + * @param fieldName + * @param nilusObject + * @param paramClass + * @param child + * @return + */ + private Object getElementObject(Object nilusObject, String fieldName, Class paramClass, Node child) { + String className = paramClass.getName(); + + switch(className) { + case "int": + case "java.lang.Integer": + return unpackInteger(child); + case "java.math.BigInteger": + return unpackBigInteger(child); + case "java.lang.String": + return unpackString(child); + case "double": + case "java.lang.Double": + return unpackDouble(child); + case "javax.xml.datatype.XMLGregorianCalendar": + return unpackGregorianCalendar(child); + } + if (className.startsWith("nilus.")) { + Object gotObject = null; + gotObject = handleFunnys(nilusObject, fieldName, paramClass, child); + if (gotObject == null) { + // the helper should have made most required objects. so try to find a getter. + // and get a pre created version of the object. + Method getter = findGetter(nilusObject.getClass(), fieldName); + if (getter != null) { + try { + gotObject = getter.invoke(nilusObject, null); + if (gotObject == null) { + Helper.createElement(nilusObject, fieldName); + gotObject = getter.invoke(nilusObject, null); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | InstantiationException e) { + // e.printStackTrace(); + } + } + } + if (gotObject != null) { + return unpackNilusObject(gotObject, child); + } + else { + return unpackNilusClass(paramClass, child, false); + } + } + + System.out.println("Unnown or unhandled data type: " + className); + return null; + } + + private Object handleFunnys(Object nilusObject, String fieldName, Class paramClass, Node child) { + Method setter = findSetter(nilusObject.getClass(), fieldName); + if (setter == null) { + return null; + } + if (paramClass == QualityValueBasic.class) { + String val = child.getTextContent(); + QualityValueBasic qb = QualityValueBasic.fromValue(val); + try { + setter.invoke(nilusObject, qb); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + e.printStackTrace(); + } + return qb; + } + return null; + } + + /** + * Unpack a list of nilus objects (or primatives?) + * @param parentObject parent object that will contain the list + * @param field field name + * @param parentEl parent element that contains the listed items. + * @return + */ + private Object getNilusList(Object parentObject, Field field, Element parentEl) { + // + String fieldName = field.getName(); + Method setter = findSetter(parentObject.getClass(), fieldName); + Method getter = findGetter(parentObject.getClass(), fieldName); + List nilusList = null; + try { + nilusList = (List) getter.invoke(parentObject, null); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + +// System.out.println(nilusList); + // need to work out what type of object is in the list from the getter signaturel + Class retType = getter.getReturnType(); + AnnotatedType aRet = getter.getAnnotatedReturnType(); + String nm = aRet.getType().getTypeName(); + int n1 = nm.indexOf("<"); + int n2 = nm.indexOf(">"); + if (n1 < 1) { + System.out.println("Invalid class"); + } + String clsName = nm.substring(n1+1, n2); + Class listCls = null; + try { + listCls = Class.forName(clsName); + } catch (ClassNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + if (listCls == null) { + System.out.printf("Unable to find list class %s for nilus element %s\n", clsName, fieldName); + return null; + } + + Element el = (Element) parentEl; +// el.get +// System.out.println("Unpack children of " + parentEl.getNodeName()); + + NodeList nodeList = parentEl.getChildNodes(); + int n = nodeList.getLength(); + int m = 0; + for (int i = 0; i < n; i++) { + Node aNode = nodeList.item(i); + if (aNode.getNodeType() != Node.ELEMENT_NODE) { + continue; + } +// System.out.println("Unpack node: " + aNode.getNodeName()); + Object listObject = getElementObject(parentObject, field.getName(), listCls, aNode); + if (listObject != null) { + nilusList.add(listObject); + m++; + } + } +// System.out.printf("Added %d children to list\n", m); +// Node aChild = parentEl.getFirstChild(); +// while (aChild != null) { +// System.out.println("Unpack node: " + aChild.getNodeName()); +// Object listObject = getElementObject(listCls, aChild); +// if (listObject != null) { +// nilusList.add(listObject); +// } +// aChild.getNextSibling(); +// } +// + + return nilusList; + } + + /** + * Unpack an element as a String + * @param child + * @return + */ + private Object unpackString(Node child) { + if (child == null) { + return null; + } + return child.getTextContent(); + } + + /** + * Unpack an element as a BigInteger + * @param child + * @return long integer converted to BigInteger + */ + private BigInteger unpackBigInteger(Node child) { + if (child == null) { + return null; + } + Long value = null; + value = Long.valueOf(child.getTextContent()); + return BigInteger.valueOf(value); + } + + /** + * Unpack an element as am int32 (Integer) + * @param child + * @return int32 value + */ + private Integer unpackInteger(Node child) { + if (child == null) { + return null; + } + Integer value = null; + value = Integer.valueOf(child.getTextContent()); + return value; + } + + + /** + * Unpack an element as a Double + * @param child + * @return double precision value or null + */ + private Double unpackDouble(Node child) { + if (child == null) { + return null; + } + Double value = null; + value = Double.valueOf(child.getTextContent()); + return value; + } + + /** + * Unpack an element as a GregorianCalendar + * @param child + * @return GregorianCalendar value + */ + private XMLGregorianCalendar unpackGregorianCalendar(Node child) { + if (child == null) { + return null; + } + String calString = child.getTextContent(); + XMLGregorianCalendar xCal = TethysTimeFuncs.fromGregorianXML(calString); + return xCal; + } + + /** + * Find a child element with a given name. Assumes there is only one + * so not used with lists. + * @param parentNode parent XML node + * @param childName name of child node. + * @return + */ + Element findChild(Node parentNode, String childName) { + if (parentNode instanceof Element == false) { + return null; + } + Element parent = (Element) parentNode; + NodeList children = parent.getElementsByTagName(childName); + if (children == null || children.getLength() == 0) { + String ch1 = childName.substring(0,1).toUpperCase(); + childName = ch1+childName.substring(1); + children = parent.getElementsByTagName(childName); + if (children == null) { + return null; + } + } + int n = children.getLength(); + for (int i = 0; i < n; i++) { + Node child = children.item(i); + String childNodeName = child.getNodeName(); + if (child.getNodeName().equals(childName)) { + return (Element) child; + } + } + return null; + } + + /** + * Get an alternative element name (for old databases ?) + * @param fieldName + * @return + */ + private String alternateElName(String fieldName) { + switch(fieldName) { + case "sampleRateKHz": + return "sampleRate_kHz"; + } + return null; + } + + /** + * Return all the setters in a class; + * @param nilusClass + * @return + */ + private ArrayList findSetters(Class nilusClass) { + Method[] methods = nilusClass.getMethods(); + ArrayList methodList = new ArrayList<>(); + for (int i = 0; i < methods.length; i++) { + String name = methods[i].getName(); + if (name.startsWith("set")) { + methodList.add(methods[i]); + } + } + return methodList; + } + /** + * Find setter functions for a given field name. Generally + * this is a capitalization of the first character and 'set' + * in front of it. + * @param nilusClass class containing the method + * @param fieldName field name + * @return Method or null + */ + private Method findSetter(Class nilusClass, String fieldName) { + String setterName = fieldName; + if (setterName.startsWith("set") == false) { + setterName = "set" + setterName; + } + Method[] methods = nilusClass.getMethods(); + if (methods == null) { + return null; + } + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equalsIgnoreCase(setterName)) { + return methods[i]; + } + } + + return null; + } + + /** + * Find getter functions for a given field name. Generally + * this is a capitalization of the first character and 'get' + * in front of it. + * @param nilusClass class containing the method + * @param fieldName field name + * @return Method or null + */ + private Method findGetter(Class nilusClass, String fieldName) { + String setterName = fieldName; + if (setterName.startsWith("get") == false) { + setterName = "get" + setterName; + } + Method[] methods = nilusClass.getMethods(); + if (methods == null) { + return null; + } + for (int i = 0; i < methods.length; i++) { + if (methods[i].getName().equalsIgnoreCase(setterName)) { + return methods[i]; + } + } + return null; + } + + + public static void main(String[] args) { + String demoFile = "C:\\PAMGuardTest\\Tethys\\Meygen20223.xml"; + File file = new File(demoFile); + System.out.printf("Unpacking file %s exists %s\n" , demoFile, file.exists()); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db; + Document doc = null; + try { + db = dbf.newDocumentBuilder(); + doc = db.parse(file); + } catch (ParserConfigurationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (SAXException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + NilusUnpacker unpacker = new NilusUnpacker(); + Class nilusClass = Regimen.class; + + ArrayList setters = null; + try { + setters = unpacker.findSetters(nilusClass); + } catch (Exception e1) { + // TODO Auto-generated catch block + e1.printStackTrace(); + } +// for (Method aSetter : setters) { +// XmlElement an = aSetter.getAnnotation(XmlElement.class); +// System.out.printf("Class %s Setter %s has xmlElement %s\n", nilusClass.getName(), aSetter.getName(), an); +// Annotation[] anns = aSetter.getAnnotations(); +// for (int i = 0; i < anns.length; i++) { +// +// System.out.printf("Class %s Setter %s has xmlElement %s\n", nilusClass.getName(), aSetter.getName(), anns[i]); +// } +// } +// + +// Field[] fields = nilusClass.getDeclaredFields(); +// for (int i = 0; i < fields.length; i++) { +// XmlElement an = fields[i].getAnnotation(XmlElement.class); +// String fieldName = "unk"; +// if (an != null) { +// fieldName = an.name(); +// } +// System.out.printf("Class %s Field %s has xmlElement %s\n", nilusClass.getName(), fields[i].getName(), fieldName); +// } +// BeanInfo beanInfo = null; +// try { +// beanInfo = Introspector.getBeanInfo(aClass); +// } catch (IntrospectionException e1) { +// // TODO Auto-generated catch block +// e1.printStackTrace(); +// } + + + + + Object obj = null; + try { + obj = unpacker.unpackDocument(doc, Deployment.class); + } catch (SecurityException e) { + e.printStackTrace(); + } + System.out.println(obj); + } + +} diff --git a/src/tethys/niluswraps/PDeployment.java b/src/tethys/niluswraps/PDeployment.java index d1633793..78e0f60b 100644 --- a/src/tethys/niluswraps/PDeployment.java +++ b/src/tethys/niluswraps/PDeployment.java @@ -67,11 +67,11 @@ public class PDeployment { return null; } String str = String.format("%s", granularity.getValue()); - Double bin = granularity.getBinSizeM(); + Double bin = granularity.getBinSizeMin(); if (bin != null) { str += String.format(" (%3.1f s)", bin*60); } - Double gap = granularity.getEncounterGapM(); + Double gap = granularity.getEncounterGapMin(); if (gap != null) { str += String.format( " (%3.1f s)", gap*60.); } diff --git a/src/tethys/niluswraps/TethysCollections.java b/src/tethys/niluswraps/TethysCollections.java deleted file mode 100644 index 7c321cac..00000000 --- a/src/tethys/niluswraps/TethysCollections.java +++ /dev/null @@ -1,13 +0,0 @@ -package tethys.niluswraps; - -public enum TethysCollections { - - Deployments, Detections, Localizations, Calibrations, SpeciesAbreviations; - - @Override - public String toString() { - return super.toString(); - } - - -} diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 49ede503..1c31671e 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -407,14 +407,14 @@ abstract public class AutoTethysProvider implements TethysDataProvider { // nilus.DetectionEffortKind.Parameters granularityParams = kind.getParameters(); switch (exportParams.granularity) { case BINNED: - kind.getGranularity().setBinSizeM(exportParams.binDurationS/60.); + kind.getGranularity().setBinSizeMin(exportParams.binDurationS/60.); long firstBin = DetectionsHandler.roundDownBinStart(pDeployment.getAudioStart(), (long) (exportParams.binDurationS*1000)); kind.getGranularity().setFirstBinStart(TethysTimeFuncs.xmlGregCalFromMillis(firstBin)); break; case CALL: break; case ENCOUNTER: - kind.getGranularity().setEncounterGapM(exportParams.encounterGapS/60.); + kind.getGranularity().setEncounterGapMin(exportParams.encounterGapS/60.); break; case GROUPED: break; diff --git a/src/tethys/swing/DatablockDetectionsPanel.java b/src/tethys/swing/DatablockDetectionsPanel.java index 36d2b6ee..30ce74be 100644 --- a/src/tethys/swing/DatablockDetectionsPanel.java +++ b/src/tethys/swing/DatablockDetectionsPanel.java @@ -26,13 +26,13 @@ import PamguardMVC.PamDataBlock; import nilus.DetectionEffortKind; import nilus.Detections; import nilus.GranularityType; +import tethys.Collection; import tethys.TethysControl; import tethys.TethysState; import tethys.dbxml.TethysException; import tethys.detection.StreamDetectionsSummary; import tethys.niluswraps.PDeployment; import tethys.niluswraps.PDetections; -import tethys.niluswraps.TethysCollections; /** * Table of Detections documents for a single PAMGuard datablock. @@ -211,12 +211,12 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa } private void displayDocument(PDetections pDets) { - getTethysControl().displayDocument(TethysCollections.Detections.toString(), pDets.detections.getId()); + getTethysControl().displayDocument(Collection.Detections.collectionName(), pDets.detections.getId()); } private void exportDocument(PDetections pDets) { - getTethysControl().exportDocument(TethysCollections.Detections.toString(), pDets.detections.getId()); + getTethysControl().exportDocument(Collection.Detections.toString(), pDets.detections.getId()); } diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 688c949b..628fe1c9 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -25,6 +25,7 @@ import PamView.dialog.warn.WarnOnce; import PamView.panel.PamPanel; import PamView.tables.SwingTableColumnWidths; import nilus.Deployment; +import tethys.Collection; import tethys.TethysControl; import tethys.TethysState; import tethys.TethysState.StateType; @@ -33,7 +34,6 @@ import tethys.deployment.DeploymentHandler; import tethys.deployment.DeploymentOverview; import tethys.deployment.RecordingPeriod; import tethys.niluswraps.PDeployment; -import tethys.niluswraps.TethysCollections; /** * Table view of PAMGuard deployments. For a really simple deployment, this may have only @@ -174,11 +174,11 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } protected void exportDeployment(PDeployment pDeployment) { - getTethysControl().exportDocument(TethysCollections.Deployments.toString(), pDeployment.deployment.getId()); + getTethysControl().exportDocument(Collection.Deployments.collectionName(), pDeployment.deployment.getId()); } protected void displayDeployment(PDeployment pDeployment) { - getTethysControl().displayDocument(TethysCollections.Deployments.toString(), pDeployment.deployment.getId()); + getTethysControl().displayDocument(Collection.Deployments.collectionName(), pDeployment.deployment.getId()); } protected void deleteDeployment(PDeployment pDeployment) { diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index 59c34d15..56ba1b20 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -8,6 +8,7 @@ import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import tethys.TethysControl; +import tethys.calibration.swing.CalibrationsPanel; public class TethysMainPanel extends TethysGUIPanel { @@ -24,6 +25,8 @@ public class TethysMainPanel extends TethysGUIPanel { private DatablockDetectionsPanel datablockDetectionsPanel; private DetectionsExportPanel detectionsExportPanel; + + private CalibrationsPanel calibrationPanel; public TethysMainPanel(TethysControl tethysControl) { super(tethysControl); @@ -36,6 +39,7 @@ public class TethysMainPanel extends TethysGUIPanel { detectionsExportPanel = new DetectionsExportPanel(tethysControl); datablockSynchPanel.addTableObserver(detectionsExportPanel); datablockSynchPanel.addTableObserver(datablockDetectionsPanel); + calibrationPanel = new CalibrationsPanel(tethysControl, tethysControl.getCalibrationHandler()); JSplitPane southwestSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); JPanel southEastPanel = new JPanel(new BorderLayout()); @@ -48,7 +52,10 @@ public class TethysMainPanel extends TethysGUIPanel { // splitPane.set mainPanel.add(BorderLayout.CENTER, splitPane); // mainPanel.add(BorderLayout.CENTER, datablockSynchPanel.getComponent()); - splitPane.add(deploymentsPanel.getComponent()); + JPanel splitNorth = new JPanel(new BorderLayout()); + splitNorth.add(BorderLayout.WEST, calibrationPanel.getComponent()); + splitNorth.add(deploymentsPanel.getComponent()); + splitPane.add(splitNorth); southwestSplit.add(datablockSynchPanel.getComponent()); southwestSplit.add(southEastPanel); southEastPanel.add(datablockDetectionsPanel.getComponent(), BorderLayout.CENTER); diff --git a/src/tethys/swing/documents/TethysDocumentTable.java b/src/tethys/swing/documents/TethysDocumentTable.java index 8b3b6a47..40700d70 100644 --- a/src/tethys/swing/documents/TethysDocumentTable.java +++ b/src/tethys/swing/documents/TethysDocumentTable.java @@ -148,7 +148,7 @@ public class TethysDocumentTable implements PamDialogPanel { "Are you sure you want to delete the document " + docInfo, WarnOnce.OK_CANCEL_OPTION); if (ans == WarnOnce.OK_OPTION) { try { - tethysControl.getDbxmlConnect().removeDocument(docInfo.getCollection().collectionName(), docInfo.getDocumentId()); + tethysControl.getDbxmlConnect().removeDocument(docInfo.getCollection().collectionName(), docInfo.getDocumentName()); } catch (TethysException e) { System.out.println("Failed to delete " + docInfo); System.out.println(e.getMessage()); From 9939e8aa859b537685e07ede21c2898622c8fc62 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Mon, 4 Dec 2023 21:27:00 +0000 Subject: [PATCH 58/65] Calibrations Export Functional Calibrations export wizard and display --- src/PamView/wizard/PamWizard.java | 160 ++++++++++++ src/PamView/wizard/PamWizardCard.java | 45 ++++ src/tethys/Collection.java | 2 +- src/tethys/TethysControl.java | 4 +- src/tethys/TethysState.java | 33 ++- src/tethys/TethysStateObserver.java | 2 +- .../calibration/CalibrationHandler.java | 233 ++++++++++++++++-- .../swing/CalibrationProcessCard.java | 164 ++++++++++++ .../calibration/swing/CalibrationsCard.java | 13 + .../swing/CalibrationsContactCard.java | 181 ++++++++++++++ .../swing/CalibrationsExportWizard.java | 42 ++++ .../swing/CalibrationsMainPanel.java | 66 +++++ ...tionsPanel.java => CalibrationsTable.java} | 9 +- src/tethys/dbxml/DBXMLConnect.java | 27 +- src/tethys/deployment/DeploymentHandler.java | 7 +- .../swing/PAMGuardDeploymentsTable.java | 2 +- src/tethys/swing/TethysMainPanel.java | 7 +- src/tethys/swing/export/AlgorithmCard.java | 2 +- src/tethys/swing/export/DescriptionCard.java | 2 +- .../swing/export/DetectionsExportWizard.java | 99 +------- src/tethys/swing/export/ExportWizardCard.java | 16 +- src/tethys/swing/export/ExportWorkerCard.java | 2 +- src/tethys/swing/export/GranularityCard.java | 2 +- 23 files changed, 962 insertions(+), 158 deletions(-) create mode 100644 src/PamView/wizard/PamWizard.java create mode 100644 src/PamView/wizard/PamWizardCard.java create mode 100644 src/tethys/calibration/swing/CalibrationProcessCard.java create mode 100644 src/tethys/calibration/swing/CalibrationsCard.java create mode 100644 src/tethys/calibration/swing/CalibrationsContactCard.java create mode 100644 src/tethys/calibration/swing/CalibrationsExportWizard.java create mode 100644 src/tethys/calibration/swing/CalibrationsMainPanel.java rename src/tethys/calibration/swing/{CalibrationsPanel.java => CalibrationsTable.java} (95%) diff --git a/src/PamView/wizard/PamWizard.java b/src/PamView/wizard/PamWizard.java new file mode 100644 index 00000000..9ba67e01 --- /dev/null +++ b/src/PamView/wizard/PamWizard.java @@ -0,0 +1,160 @@ +package PamView.wizard; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JButton; +import javax.swing.JPanel; + +import PamView.dialog.PamDialog; +import tethys.swing.export.ExportStreamInfoPanel; +import tethys.swing.export.ExportWizardCard; + +abstract public class PamWizard extends PamDialog { + + private static final long serialVersionUID = 1L; + + private JPanel cardPanel; + + private CardLayout cardLayout; + + private JPanel mainPanel; + + private JButton prevButton; + + private ArrayList wizardCards = new ArrayList(); + + public PamWizard(Window parentFrame, String title) { + super(parentFrame, title, false); + + cardLayout = new CardLayout(); + mainPanel = new JPanel(new BorderLayout()); + cardPanel = new JPanel(cardLayout); + mainPanel.add(BorderLayout.CENTER, cardPanel); + + setDialogComponent(mainPanel); + + getOkButton().setText("Finish"); + prevButton = new JButton("Previous"); + getButtonPanel().add(prevButton, 0); + prevButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + previousButton(); + } + }); + + setResizable(true); + } + + public void addCard(PamWizardCard wizPanel) { + cardPanel.add(wizPanel, wizPanel.getTitle()); + wizardCards.add(wizPanel); + } + + /** + * Get the main panel. This is the main dialog panel and uses a borderlayout + * with the cards in the CENTER of the panel. Additional information panels + * (generally fixed and not changing with the dialog) can be added NORTH, SOUTH, WEST and EAST. + * @return main Panel. + */ + public JPanel getMainPanel() { + return mainPanel; + } + + /** + * Called when 'previous' button is clicked. + */ + protected void previousButton() { + cardLayout.previous(cardPanel); + enableControls(); + } + + public void enableControls() { + int iCard = getCardIndex(); + prevButton.setEnabled(iCard > 0); + boolean isLast = iCard == wizardCards.size()-1; +// getOkButton().setEnabled(!isLast); + getOkButton().setText(isLast ? "Finish" : "Next"); + } + + private boolean checkCurrentCard() { + int iCard = getCardIndex(); + if (iCard < 0) { + return true; + } + return getCardParams(wizardCards.get(iCard)); + } + + abstract public void setCardParams(PamWizardCard wizardCard); + + abstract public boolean getCardParams(PamWizardCard wizardCard); + + public int getCardIndex() { + for (int i = 0; i < cardPanel.getComponentCount(); i++) { + Component component = cardPanel.getComponent(i); + if (component.isVisible()) { + return i; + } + } + return -1; + } + + public JButton getPreviousButton() { + return prevButton; + } + + public void setParams() { + for (PamWizardCard wizCard : wizardCards) { + setCardParams(wizCard); + } + enableControls(); + } + + @Override + public boolean getParams() { + /** + * This is the OK button, so we need to NOT return OK, which would close the + * dialog until we're on the last card. + */ + if (checkCurrentCard() == false) { + return false; + } + int iCard = getCardIndex(); + if (iCard < wizardCards.size()-1) { + cardLayout.next(cardPanel); + enableControls(); + return false; + } + + return true; + } + + + + @Override + public void restoreDefaultSettings() { + // TODO Auto-generated method stub + + } + + /** + * Move to the first card in the stack + */ + public void moveFirst() { + cardLayout.first(cardPanel); + } + + /** + * Move to the last card in the stack + */ + public void moveLast() { + cardLayout.last(cardPanel); + } + +} diff --git a/src/PamView/wizard/PamWizardCard.java b/src/PamView/wizard/PamWizardCard.java new file mode 100644 index 00000000..bc0d9e14 --- /dev/null +++ b/src/PamView/wizard/PamWizardCard.java @@ -0,0 +1,45 @@ +package PamView.wizard; + +import java.io.Serializable; + +import javax.swing.JPanel; + + +/** + * Base class for PAMGuard wizard cards. + * @author dg50 + * + * @param class type for parameters to set and get. + */ +abstract public class PamWizardCard extends JPanel { + + private static final long serialVersionUID = 1L; + + private String title; + + private PamWizard pamWizard; + + /** + * @param title + */ + public PamWizardCard(PamWizard pamWizard, String title) { + this.pamWizard = pamWizard; + this.title = title; + } + + public abstract boolean getParams(T cardParams); + + public abstract void setParams(T cardParams); + + public String getTitle() { + return title; + } + + /** + * @return the pamWizard + */ + public PamWizard getPamWizard() { + return pamWizard; + } + +} diff --git a/src/tethys/Collection.java b/src/tethys/Collection.java index 0b9b1d58..93a6acb2 100644 --- a/src/tethys/Collection.java +++ b/src/tethys/Collection.java @@ -8,7 +8,7 @@ package tethys; */ public enum Collection { - Deployments, Detections, Calibrations, Localizations, SpeciesAbbreviations, Ensembles, SourceMaps, ITIS, ITIS_ranks; + Deployments, Detections, Calibrations, Localizations, SpeciesAbbreviations, Ensembles, SourceMaps, ITIS, ITIS_ranks, OTHER; /** * A list of the main collections in the database, i.e. ones the user will diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 4db92596..1a5f2b7d 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -704,9 +704,9 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet * @param dataBlock */ public void exportedDetections(PamDataBlock dataBlock) { - sendStateUpdate(new TethysState(StateType.TRANSFERDATA)); + sendStateUpdate(new TethysState(StateType.EXPORTRDATA, Collection.Detections)); countProjectDetections(); - sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION)); + sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION, Collection.Detections)); } /** diff --git a/src/tethys/TethysState.java b/src/tethys/TethysState.java index 0430af6b..ea2f2746 100644 --- a/src/tethys/TethysState.java +++ b/src/tethys/TethysState.java @@ -9,27 +9,46 @@ package tethys; public class TethysState { public enum StateType {UPDATESERVER, // Server connection or status has changed - TRANSFERDATA, // data have been transferred from PAMGuard to Tethys + EXPORTRDATA, // 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!) - EXPORTING // currently exporting data. may be a while ... + EXPORTING, // currently exporting data. may be a while ... + DELETEDATA // data were deleted } public StateType stateType; - private Object stateObject; + + public Collection collection; public TethysState(StateType stateType) { super(); this.stateType = stateType; + collection = Collection.OTHER; } - public TethysState(StateType stateType, Object stateObject) { + public TethysState(StateType stateType, Collection collection) { this.stateType = stateType; - this.stateObject = stateObject; + this.collection = collection; + if (this.collection == null) { + this.collection = Collection.OTHER; + } } - public Object getStateObject() { - return stateObject; + /** + * @return the collection associated with this notification. Note that there is + * an OTHER category in Collections which is used for server / project updates, making + * it easier to switch on the collection type when notifications are received. + */ + public Collection getCollection() { + return collection; } + + /** + * @return the stateType + */ + public StateType getStateType() { + return stateType; + } + } diff --git a/src/tethys/TethysStateObserver.java b/src/tethys/TethysStateObserver.java index 7c8aa11d..33a9de5c 100644 --- a/src/tethys/TethysStateObserver.java +++ b/src/tethys/TethysStateObserver.java @@ -5,7 +5,7 @@ public interface TethysStateObserver { /** * Receive state updates when Tethys has done something (made a connection, moved some data, etc.)
* Note that this is for RECEIVING state updates, not for sending them. To avoid infinite notifications - * loops, use tethysControl.sendStateUpdate(TethysState) if this component knows something. + * loops, use tethysControl.sendStateUpdate(TethysState) to send out state notifications. * @param tethysState */ public void updateState(TethysState tethysState); diff --git a/src/tethys/calibration/CalibrationHandler.java b/src/tethys/calibration/CalibrationHandler.java index 1823afbf..0c283a31 100644 --- a/src/tethys/calibration/CalibrationHandler.java +++ b/src/tethys/calibration/CalibrationHandler.java @@ -8,21 +8,27 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import javax.xml.bind.JAXBException; import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import Acquisition.AcquisitionControl; +import Acquisition.AcquisitionParameters; import Acquisition.AcquisitionProcess; import Array.ArrayManager; import Array.Hydrophone; import Array.PamArray; +import Array.Preamplifier; import PamController.PamController; import PamController.soundMedium.GlobalMedium; import PamController.soundMedium.GlobalMedium.SoundMedium; import PamUtils.PamCalendar; +import PamView.dialog.warn.WarnOnce; import dbxml.Queries; import PamController.soundMedium.GlobalMediumManager; +import nilus.AlgorithmType.Parameters; import nilus.Calibration; import nilus.Calibration.FrequencyResponse; import nilus.Calibration.QualityAssurance; @@ -37,6 +43,7 @@ import tethys.TethysControl; import tethys.TethysState; import tethys.TethysStateObserver; import tethys.TethysTimeFuncs; +import tethys.calibration.swing.CalibrationsExportWizard; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.TethysException; import tethys.niluswraps.NilusUnpacker; @@ -46,15 +53,26 @@ public class CalibrationHandler implements TethysStateObserver { private TethysControl tethysControl; - private ArrayList> calibrationDataBlock; + private ArrayList> calibrationsList; + public static final String[] updateOptions = {"as-needed", "unplanned", "yearly"}; + + public static final String[] calibrationMethods = {"Reference hydrophone", "Manufacturers specification", "Piston phone", "Other calibrated source", "Unknown"}; + + public static final String[] qaTypes = {"unverified", "valid", "invalid"}; + + private Helper nilusHelper; /** * @param tethysControl */ public CalibrationHandler(TethysControl tethysControl) { this.tethysControl = tethysControl; - calibrationDataBlock = new ArrayList(); - tethysControl.addStateObserver(this); + calibrationsList = new ArrayList(); + tethysControl.addStateObserver(this); try { + nilusHelper = new Helper(); + } catch (JAXBException e) { + e.printStackTrace(); + } } @Override @@ -64,20 +82,43 @@ public class CalibrationHandler implements TethysStateObserver { break; case NEWPAMGUARDSELECTION: case NEWPROJECTSELECTION: - case TRANSFERDATA: + case EXPORTRDATA: + case DELETEDATA: case UPDATEMETADATA: case UPDATESERVER: - updateDocumentsList(); + if (isWantedState(tethysState)) { + updateDocumentsList(); + } default: break; } } + + /** + * Is it a state notification we want to respond to + * @param state + * @return true if worth it. + */ + protected boolean isWantedState(TethysState state) { + if (state.collection == null) { + return true; + } + switch (state.collection) { + case OTHER: + case Calibrations: + return true; + } + return false; + } + /** + * Update the list of documents associated with the selected instrument. + */ private void updateDocumentsList() { ArrayList docsList = getArrayCalibrations(); // now immediately read the calibrations in again. - calibrationDataBlock.clear();; + calibrationsList.clear();; NilusUnpacker unpacker = new NilusUnpacker(); for (DocumentInfo aDoc : docsList) { Queries queries = tethysControl.getDbxmlConnect().getTethysQueries(); @@ -109,7 +150,7 @@ public class CalibrationHandler implements TethysStateObserver { } DocumentNilusObject calDataUnit = new DocumentNilusObject(Collection.Calibrations, aDoc.getDocumentName(), calObj.getId(), calObj); - calibrationDataBlock.add(calDataUnit); + calibrationsList.add(calDataUnit); // System.out.println(result); } catch (Exception e) { // TODO Auto-generated catch block @@ -119,47 +160,105 @@ public class CalibrationHandler implements TethysStateObserver { } public int exportAllCalibrations() { + + Calibration sampleCal = new Calibration(); + try { + Helper.createRequiredElements(sampleCal); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e1) { + e1.printStackTrace(); + } + sampleCal = CalibrationsExportWizard.showWizard(tethysControl.getGuiFrame(), sampleCal); + if (sampleCal == null) { + return 0; + } + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); int nPhone = array.getHydrophoneCount(); DBXMLConnect dbxml = tethysControl.getDbxmlConnect(); int nExport = 0; + boolean overwrite = false; + boolean exists; for (int i = 0; i < nPhone; i++) { // String docName = getHydrophoneId(i); Calibration calDoc = createCalibrationDocument(i); - String calDocName = getDocumentName(calDoc, i); + if (sampleCal != null) { + calDoc.setMetadataInfo(sampleCal.getMetadataInfo()); + MetadataInfo oldMeta = calDoc.getMetadataInfo(); + MetadataInfo newMeta = sampleCal.getMetadataInfo(); + + + calDoc.setProcess(sampleCal.getProcess()); + calDoc.setQualityAssurance(sampleCal.getQualityAssurance()); + calDoc.setResponsibleParty(sampleCal.getResponsibleParty()); + calDoc.setTimeStamp(sampleCal.getTimeStamp()); + } + + addParameterDetails(calDoc, i); + + String calDocName = createDocumentName(calDoc, i); + exists = calDocumentExists(calDocName); + if (exists && overwrite == false) { + String msg = String.format("Calibration document %s already exists. Do you want to overwrite it and other documents from this date?", calDocName); + int ans = WarnOnce.showWarning("Calibration Export", msg, WarnOnce.OK_CANCEL_OPTION); + if (ans == WarnOnce.OK_OPTION) { + overwrite = true; + } + else { + return nExport; + } + } boolean ok = false; + if (exists == true && overwrite == false) { + continue; + } try { + if (exists) { + ok = dbxml.removeDocument(Collection.Calibrations, calDocName); + } ok = dbxml.postAndLog(calDoc, calDocName); } catch (TethysException e) { - // TODO Auto-generated catch block e.printStackTrace(); tethysControl.showException(e); ok = false; + break; } if (ok) { nExport++; } } + tethysControl.sendStateUpdate(new TethysState(TethysState.StateType.EXPORTRDATA, Collection.Calibrations)); return nExport; } /** - * Get a name for the document, which is a bit like the id within - * the document, but also contain a yymmdd data string. + * Add the separate pamguard parameters to the document which are used + * to make up the overall calibration. * @param calDoc - * @param i channel - * @return document name + * @param i */ - private String getDocumentName(Calibration calDoc, int iChan) { - long docDate = System.currentTimeMillis(); - XMLGregorianCalendar date = calDoc.getMetadataInfo().getDate(); - if (date != null) { - docDate = TethysTimeFuncs.millisFromGregorianXML(date); + private void addParameterDetails(Calibration calDoc, int i) { + Parameters params = calDoc.getProcess().getParameters(); + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + AcquisitionControl daqControl = (AcquisitionControl) PamController.getInstance().findControlledUnit(AcquisitionControl.unitType); + AcquisitionParameters daqParams = daqControl.getAcquisitionParameters(); + Hydrophone phone = array.getHydrophoneArray().get(i); + try { + nilusHelper.AddAnyElement(params.getAny(), "HydrophoneType", phone.getType()); + nilusHelper.AddAnyElement(params.getAny(), "Sensitivity", String.format("%3.1f", phone.getSensitivity())); + nilusHelper.AddAnyElement(params.getAny(), "PreampGain", String.format("%3.1f", phone.getPreampGain())); + nilusHelper.AddAnyElement(params.getAny(), "ADCp-p", String.format("%3.2fV", daqParams.getVoltsPeak2Peak())); + Preamplifier preamp = daqParams.preamplifier; + if (preamp != null) { + nilusHelper.AddAnyElement(params.getAny(), "ADCAmplifier", String.format("%3.2fdB", preamp.getGain())); + } + } catch (JAXBException e) { + e.printStackTrace(); + } catch (ParserConfigurationException e) { + e.printStackTrace(); } - String dateStr = formatDate(docDate); - String name = String.format("%s_%s_ch%d", getCalibrationDocumentRoot(), dateStr, iChan); - return name; + } + /** * Format the data in the dd MMMM yyyy format * @param timeInMillis time in milliseconds @@ -176,12 +275,30 @@ public class CalibrationHandler implements TethysStateObserver { } + /** + * Get a name for the document, which is a bit like the id within + * the document, but also contain a yymmdd data string. + * @param calDoc + * @param i channel + * @return document name + */ + private String createDocumentName(Calibration calDoc, int iChan) { + long docDate = System.currentTimeMillis(); + XMLGregorianCalendar date = calDoc.getMetadataInfo().getDate(); + if (date != null) { + docDate = TethysTimeFuncs.millisFromGregorianXML(date); + } + String dateStr = formatDate(docDate); + String name = String.format("%s_%s_ch%d", createCalibrationDocumentRoot(), dateStr, iChan); + return name; + } + /** * Get a start of name for a calibration document. This will be used in the document name * with a date and a channel, and the document Id just of the root and the channel. * @return root string for document names and document id's. */ - public String getCalibrationDocumentRoot() { + public String createCalibrationDocumentRoot() { PamArray array = ArrayManager.getArrayManager().getCurrentArray(); if (array == null) { return null; @@ -308,6 +425,59 @@ public class CalibrationHandler implements TethysStateObserver { return calibration; } + + /** + * See if a document already exists. This should only occur if you + * try to export the same document twice with the same calibration date. + * @param documentName + * @return true if a document already exists. + */ + public boolean calDocumentExists(String documentName) { + if (calibrationsList == null) { + return false; + } + for (int i = 0; i < calibrationsList.size(); i++) { + if (calibrationsList.get(i).getDocumentName().equalsIgnoreCase(documentName)) { + return true; + } + } + return false; + } + + /** + * Return if we have at least one document for every channel. + * @return true if all cal documents exist. + */ + public boolean haveAllChannelCalibrations() { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + int nPhone = array.getHydrophoneCount(); + for (int i = 0; i < nPhone; i++) { + if (haveChannelCalibration(i) == false) { + return false; + } + } + return true; + } + + /** + * Find whether we have a document for this instrument and channel. + * @param iChan + * @return true if we have an appropriate doc. + */ + public boolean haveChannelCalibration(int iChan) { + if (calibrationsList == null) { + return false; + } + String seachPattern = makeChannelNamePart(iChan); + for (int i = 0; i < calibrationsList.size(); i++) { + String docName = calibrationsList.get(i).getDocumentName(); + if (docName.endsWith(seachPattern)) { + return true; + } + } + + return false; + } /** * Get an id based on the instrument identifiers and channel number. @@ -321,21 +491,34 @@ public class CalibrationHandler implements TethysStateObserver { if (array == null) { return null; } - String id = String.format("%s_ch%02d", getCalibrationDocumentRoot(), channelIndex); + String id = String.format("%s_%s", createCalibrationDocumentRoot(), makeChannelNamePart(channelIndex)); id = id.replace(" ", "_"); return id; } + + /** + * Make the final part of the document name / id which is the channel number. + * @param channelIndex channel index + * @return string in the form ch%02d (e.g. ch03) + */ + public String makeChannelNamePart(int channelIndex) { + return String.format("ch%02d", channelIndex); + } /** * @return the calibrationDataBlock */ public ArrayList> getCalibrationDataList() { - return calibrationDataBlock; + return calibrationsList; } + /** + * Make a list of document names associated with this instrument. + * @return list of calibration documents using this instrument, based on the start of the document name. + */ private ArrayList getArrayCalibrations() { ArrayList allCals = tethysControl.getDbxmlQueries().getCollectionDocumentList(Collection.Calibrations); - String prefix = getCalibrationDocumentRoot(); + String prefix = createCalibrationDocumentRoot(); // find doc names that have that root. ArrayList theseCals = new ArrayList<>(); for (DocumentInfo aDoc : allCals) { diff --git a/src/tethys/calibration/swing/CalibrationProcessCard.java b/src/tethys/calibration/swing/CalibrationProcessCard.java new file mode 100644 index 00000000..7b9ef3f3 --- /dev/null +++ b/src/tethys/calibration/swing/CalibrationProcessCard.java @@ -0,0 +1,164 @@ +package tethys.calibration.swing; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.List; + +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamGridBagContraints; +import PamView.wizard.PamWizard; +import nilus.AlgorithmType; +import nilus.AlgorithmType.Parameters; +import nilus.AlgorithmType.SupportSoftware; +import nilus.Calibration; +import nilus.Calibration.QualityAssurance; +import nilus.QualityValueBasic; +import tethys.calibration.CalibrationHandler; + +public class CalibrationProcessCard extends CalibrationsCard { + + private JPanel processPanel; + + private JComboBox calMethod; + + private JTextArea software; + + private JTextField version; + + private JComboBox qaQuality; + + private JTextField qaComment; + + public CalibrationProcessCard(PamWizard pamWizard) { + super(pamWizard, "Calibration Process"); + this.setLayout(new BorderLayout()); + processPanel = new JPanel(new GridBagLayout()); + processPanel.setBorder(new TitledBorder("Calibration Process")); + this.add(BorderLayout.NORTH, processPanel); + GridBagConstraints c = new PamGridBagContraints(); + + calMethod = new JComboBox(); + String[] meths = CalibrationHandler.calibrationMethods; + for (int i = 0; i < meths.length; i++) { + calMethod.addItem(meths[i]); + } + + qaQuality = new JComboBox<>(); + String[] vals = CalibrationHandler.qaTypes; + for (int i = 0; i < vals.length; i++) { + qaQuality.addItem(vals[i]); + } + + software = new JTextArea(5, 25); + software.setLineWrap(true); + software.setWrapStyleWord(true); + software.setToolTipText("Details of calibration method and software used"); + + version = new JTextField(20); + version.setToolTipText("Serial number of calibration device"); + + qaComment = new JTextField(20); + qaComment.setToolTipText("Comment on calibration quality"); + + processPanel.add(new JLabel("Method ", JLabel.RIGHT), c); + c.gridx++; + processPanel.add(calMethod, c); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + processPanel.add(new JLabel("Serial number ", JLabel.RIGHT), c); + c.gridx++; + processPanel.add(version, c); + c.gridx = 0; + c.gridy++; + processPanel.add(new JLabel("Quality ", JLabel.RIGHT), c); + c.gridx++; + processPanel.add(qaQuality, c); + c.gridx = 0; + c.gridy++; + processPanel.add(new JLabel("QA Comment ", JLabel.RIGHT), c); + c.gridx++; + processPanel.add(qaComment, c); + + this.add(BorderLayout.CENTER, makeScrollablePanel(software, "Calibration method")); + + } + + private JScrollPane makeScrollablePanel(JTextArea textArea, String title) { + // TODO Auto-generated method stub +// mainPanel.add(new Label(title, JLabel.LEFT)); +// textArea.setMinimumSize(new Dimension(200, 200)); + JScrollPane scrollPane = new JScrollPane(textArea, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setBorder(new TitledBorder(title)); + scrollPane.setPreferredSize(new Dimension(scrollPane.getPreferredSize().height/2, 0)); + return scrollPane; + } + + + @Override + public boolean getParams(Calibration calibration) { + if (calibration == null) { + return false; + } + AlgorithmType process = calibration.getProcess(); + if (process == null) { + process = new AlgorithmType(); + calibration.setProcess(process); + } + process.setMethod((String) calMethod.getSelectedItem()); + process.setVersion(version.getText()); + process.setSoftware(software.getText()); + if (software.getText() == null) { + getPamWizard().showWarning("You must specify the calibratin method used"); + } + + QualityAssurance qa = calibration.getQualityAssurance(); + if (qa == null) { + qa = new QualityAssurance(); + calibration.setQualityAssurance(qa); + } + qa.setComment(qaComment.getText()); + qa.setQuality(QualityValueBasic.fromValue((String) qaQuality.getSelectedItem())); + + // need to add a few fixed things for this to work... +// List supportSoftware = process.getSupportSoftware(); + Parameters params = process.getParameters(); + if (params == null) { + params = new Parameters(); + process.setParameters(params); + } + + return true; + } + + @Override + public void setParams(Calibration calibration) { + if (calibration == null) { + return; + } + AlgorithmType process = calibration.getProcess(); + if (process != null) { + calMethod.setSelectedItem(process.getMethod()); + version.setText(process.getVersion()); + software.setText(process.getSoftware()); + } + QualityAssurance qa = calibration.getQualityAssurance(); + if (qa != null) { + QualityValueBasic qb = qa.getQuality(); + if (qb != null) { + qaQuality.setSelectedItem(qb.value()); + } + qaComment.setText(qa.getComment()); + } + } +} diff --git a/src/tethys/calibration/swing/CalibrationsCard.java b/src/tethys/calibration/swing/CalibrationsCard.java new file mode 100644 index 00000000..b97221d3 --- /dev/null +++ b/src/tethys/calibration/swing/CalibrationsCard.java @@ -0,0 +1,13 @@ +package tethys.calibration.swing; + +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; +import nilus.Calibration; + +abstract public class CalibrationsCard extends PamWizardCard { + + public CalibrationsCard(PamWizard pamWizard, String title) { + super(pamWizard, title); + } + +} diff --git a/src/tethys/calibration/swing/CalibrationsContactCard.java b/src/tethys/calibration/swing/CalibrationsContactCard.java new file mode 100644 index 00000000..95d5b577 --- /dev/null +++ b/src/tethys/calibration/swing/CalibrationsContactCard.java @@ -0,0 +1,181 @@ +package tethys.calibration.swing; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Date; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.jdesktop.swingx.JXDatePicker; + +import PamView.dialog.PamDialog; +import PamView.dialog.PamGridBagContraints; +import PamView.wizard.PamWizard; +import nilus.Calibration; +import nilus.ContactInfo; +import nilus.MetadataInfo; +import nilus.ResponsibleParty; +import tethys.TethysTimeFuncs; +import tethys.calibration.CalibrationHandler; + +public class CalibrationsContactCard extends CalibrationsCard { + + private JXDatePicker datePicker; + + private JTextField individual, organisation, position, email; + + private JComboBox updateInterval; + + public CalibrationsContactCard(PamWizard pamWizard) { + super(pamWizard, "Contact Details"); + // TODO Auto-generated constructor stub +// setBorder(new TitledBorder("Contact")); + setLayout(new BorderLayout()); + + updateInterval = new JComboBox<>(); + String[] vals = CalibrationHandler.updateOptions; + for (int i = 0; i < vals.length; i++) { + updateInterval.addItem(vals[i]); + } + + JPanel datePanel = new JPanel(new GridBagLayout()); + datePanel.setBorder(new TitledBorder("Calibration date")); + add(BorderLayout.NORTH, datePanel); + GridBagConstraints c = new PamGridBagContraints(); + datePanel.add(new JLabel("Calibration date: ", JLabel.RIGHT), c); + datePicker = new JXDatePicker(); + c.gridx++; + datePanel.add(datePicker, c); + c.gridx = 0; + c.gridy++; + datePanel.add(new JLabel("Update Frequency", JLabel.RIGHT), c); + c.gridx++; + datePanel.add(updateInterval, c); + + + JPanel contactPanel = new JPanel(new GridBagLayout()); + contactPanel.setBorder(new TitledBorder("Contact")); + this.add(BorderLayout.CENTER, contactPanel); + c = new PamGridBagContraints(); + contactPanel.add(new JLabel("Individual Name "), c); + c.gridx++; + contactPanel.add(individual = new JTextField(15), c); + c.gridx = 0; + c.gridy++; + contactPanel.add(new JLabel("Organisation "), c); + c.gridx++; + contactPanel.add(organisation = new JTextField(15), c); + c.gridx = 0; + c.gridy++; + contactPanel.add(new JLabel("Position "), c); + c.gridx++; + contactPanel.add(position = new JTextField(15), c); + c.gridx = 0; + c.gridy++; + contactPanel.add(new JLabel("Email "), c); + c.gridx++; + contactPanel.add(email = new JTextField(15), c); + c.gridx = 0; + c.gridy++; + + } + + @Override + public boolean getParams(Calibration cardParams) { + if (cardParams == null) { + return false; + } + MetadataInfo metaInf = cardParams.getMetadataInfo(); + if (metaInf == null) { + metaInf = new MetadataInfo(); + cardParams.setMetadataInfo(metaInf); + } + ResponsibleParty contact = metaInf.getContact(); + if (contact == null) { + contact = new ResponsibleParty(); + metaInf.setContact(contact); + } + ContactInfo contactInfo = contact.getContactInfo(); + if (contactInfo == null) { + contactInfo = new ContactInfo(); + contact.setContactInfo(contactInfo); + } + + // so far as I'm aware, the meta info contains the time we create this record + // and the other timestamp is the data the calibration was donw. + metaInf.setDate(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); + metaInf.setUpdateFrequency((String) updateInterval.getSelectedItem()); + + contact.setIndividualName(individual.getText()); + contact.setOrganizationName(organisation.getText()); + contact.setPositionName(position.getText()); + contactInfo.setContactInstructions(email.getText()); + + // and set this both as the RepsonsiblePArty and in the metadata. + cardParams.setResponsibleParty(contact); + + Date date = datePicker.getDate(); + if (date == null) { + return getPamWizard().showWarning("You must specify the data of the calibration"); + } + long millis = date.getTime(); + cardParams.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(millis)); + + return true; + } + + private ResponsibleParty findResponsibleParty(Calibration cal) { + if (cal == null) { + return null; + } + MetadataInfo metaInfo = cal.getMetadataInfo(); + if (metaInfo != null) { + ResponsibleParty resp = metaInfo.getContact(); + if (resp != null && resp.getIndividualName() != null) { + return resp; + } + } + return cal.getResponsibleParty(); + + } + + @Override + public void setParams(Calibration cardParams) { + // fill in as much as possible from the existing Calibration + ResponsibleParty resp = findResponsibleParty(cardParams); + if (resp != null) { + individual.setText(resp.getIndividualName()); + organisation.setText(resp.getOrganizationName()); + position.setText(resp.getPositionName()); + ContactInfo cInf = resp.getContactInfo(); + if (cInf != null) { + email.setText(cInf.getContactInstructions()); + } + } + + MetadataInfo metaInf = cardParams.getMetadataInfo(); + if (metaInf != null) { + String uf = metaInf.getUpdateFrequency(); + if (uf != null) { + updateInterval.setSelectedItem(uf); + } + } + + XMLGregorianCalendar ts = cardParams.getTimeStamp(); + if (ts != null) { + datePicker.setDate(new Date(TethysTimeFuncs.millisFromGregorianXML(ts))); + } + + + } + +} diff --git a/src/tethys/calibration/swing/CalibrationsExportWizard.java b/src/tethys/calibration/swing/CalibrationsExportWizard.java new file mode 100644 index 00000000..74bb356a --- /dev/null +++ b/src/tethys/calibration/swing/CalibrationsExportWizard.java @@ -0,0 +1,42 @@ +package tethys.calibration.swing; + +import java.awt.Window; + +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; +import nilus.Calibration; + +public class CalibrationsExportWizard extends PamWizard { + + private Calibration sampleDocument; + + private CalibrationsExportWizard(Window parentFrame, Calibration sampleDocument) { + super(parentFrame, "Calibrations Export"); + this.sampleDocument = sampleDocument; + addCard(new CalibrationProcessCard(this)); + addCard(new CalibrationsContactCard(this)); + } + + public static Calibration showWizard(Window parentFrame, Calibration sampleDocument) { + CalibrationsExportWizard wiz = new CalibrationsExportWizard(parentFrame, sampleDocument); + wiz.setParams(); + wiz.setVisible(true); + return wiz.sampleDocument; + } + + @Override + public void setCardParams(PamWizardCard wizardCard) { + wizardCard.setParams(sampleDocument); + } + + @Override + public boolean getCardParams(PamWizardCard wizardCard) { + return wizardCard.getParams(sampleDocument); + } + + @Override + public void cancelButtonPressed() { + sampleDocument = null; + } + +} diff --git a/src/tethys/calibration/swing/CalibrationsMainPanel.java b/src/tethys/calibration/swing/CalibrationsMainPanel.java new file mode 100644 index 00000000..385ca9ef --- /dev/null +++ b/src/tethys/calibration/swing/CalibrationsMainPanel.java @@ -0,0 +1,66 @@ +package tethys.calibration.swing; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + + +import PamView.panel.PamPanel; +import tethys.TethysControl; +import tethys.calibration.CalibrationHandler; +import tethys.swing.TethysGUIPanel; + +public class CalibrationsMainPanel extends TethysGUIPanel { + + private CalibrationHandler calibrationHandler; + + private CalibrationsTable calibrationsTable; + + private JPanel mainPanel; + + private JPanel ctrlPanel; + + private JButton exportButton; + + private JLabel warning; + + public CalibrationsMainPanel(TethysControl tethysControl, CalibrationHandler calibrationHandler) { + super(tethysControl); + this.calibrationHandler = calibrationHandler; + mainPanel = new PamPanel(new BorderLayout()); + mainPanel.setBorder(new TitledBorder("Instrument calibration information")); + + calibrationsTable = new CalibrationsTable(tethysControl, calibrationHandler); + mainPanel.add(BorderLayout.CENTER, calibrationsTable.getComponent()); + + ctrlPanel = new PamPanel(new BorderLayout()); + exportButton = new JButton("Export ..."); + ctrlPanel.add(BorderLayout.WEST, exportButton); + warning = new JLabel(); + ctrlPanel.add(BorderLayout.CENTER, warning); + mainPanel.add(BorderLayout.NORTH, ctrlPanel); + exportButton.setToolTipText("Export calibration data to database"); + exportButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportCalibrations(); + } + }); + } + + protected void exportCalibrations() { + calibrationHandler.exportAllCalibrations(); + } + + @Override + public JComponent getComponent() { + return mainPanel; + } + +} diff --git a/src/tethys/calibration/swing/CalibrationsPanel.java b/src/tethys/calibration/swing/CalibrationsTable.java similarity index 95% rename from src/tethys/calibration/swing/CalibrationsPanel.java rename to src/tethys/calibration/swing/CalibrationsTable.java index b084c5d6..a5943a3f 100644 --- a/src/tethys/calibration/swing/CalibrationsPanel.java +++ b/src/tethys/calibration/swing/CalibrationsTable.java @@ -33,7 +33,7 @@ import tethys.calibration.CalibrationHandler; import tethys.dbxml.TethysException; import tethys.swing.TethysGUIPanel; -public class CalibrationsPanel extends TethysGUIPanel { +public class CalibrationsTable extends TethysGUIPanel { private CalibrationHandler calibrationHandler; @@ -48,7 +48,7 @@ public class CalibrationsPanel extends TethysGUIPanel { /** * @param calibrationHandler */ - public CalibrationsPanel(TethysControl tethysControl, CalibrationHandler calibrationHandler) { + public CalibrationsTable(TethysControl tethysControl, CalibrationHandler calibrationHandler) { super(tethysControl); this.tethysControl = tethysControl; this.calibrationHandler = calibrationHandler; @@ -60,7 +60,6 @@ public class CalibrationsPanel extends TethysGUIPanel { JScrollPane scrollPane = new JScrollPane(calTable); mainPanel = new PamPanel(new BorderLayout()); - mainPanel.setBorder(new TitledBorder("Instrument calibration information")); mainPanel.add(BorderLayout.CENTER, scrollPane); calTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); @@ -177,7 +176,7 @@ public class CalibrationsPanel extends TethysGUIPanel { try { DocumentNilusObject doc = calibrationHandler.getCalibrationDataList().get(rows[i]); docName = doc.getDocumentName(); - tethysControl.getDbxmlConnect().removeDocument(Collection.Calibrations.collectionName(), docName); + tethysControl.getDbxmlConnect().removeDocument(Collection.Calibrations, docName); } catch (TethysException e) { System.out.println("Failed to delete " + docName); System.out.println(e.getMessage()); @@ -189,7 +188,7 @@ public class CalibrationsPanel extends TethysGUIPanel { } private void updateEverything() { - calibrationHandler.updateState(new TethysState(StateType.TRANSFERDATA)); + getTethysControl().sendStateUpdate(new TethysState(StateType.DELETEDATA, Collection.Calibrations)); } class CalibrationsTableModel extends AbstractTableModel { diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index baea57f7..e13b05fd 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -282,7 +282,7 @@ An error will throw an exception. } return true; } - + /** * Remove a document based on a collection name and a cdocument Id. * @param collection collection name. @@ -290,23 +290,32 @@ An error will throw an exception. * @return * @throws TethysException */ - public boolean removeDocument(String collection, String documentName) throws TethysException { + public boolean removeDocument(Collection collection, String documentName) throws TethysException { + return removeDocument(collection.collectionName(), documentName); + } + + /** + * Remove a document based on a collection name and a document namw. + * @param collectionName collection name. + * @param documentName document name (not the internal Document Id) + * @return + * @throws TethysException + */ + public boolean removeDocument(String collectionName, String documentName) throws TethysException { try { -// docId = "SoundTrap_600_HF_7129_ch00"; - Object result = jerseyClient.removeDocument(collection, documentName ); + Object result = jerseyClient.removeDocument(collectionName, documentName ); /** * Return from a sucessful delete is something like * deployment = getTethysControl().getDeploymentHandler().createDeploymentDocument(freeId++, recordPeriod); - ['ECoastNARW0'] - -An error will throw an exception. + ['ECoastNARW0'] + + An error will throw an exception. */ } catch (Exception e) { -// System.out.printf("Error deleting %s %s: %s\n", collection, docId, e.getMessage()); - String msg = String.format("Error deleting %s:%s", collection, documentName); + String msg = String.format("Error deleting %s:%s", collectionName, documentName); throw new TethysException(msg, e.getLocalizedMessage()); } return true; diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 3c0a4dfe..1d1167ca 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -54,6 +54,7 @@ import nilus.Helper; import nilus.UnknownSensor; import pamMaths.PamVector; import pamMaths.STD; +import tethys.Collection; import tethys.TethysControl; import tethys.TethysLocationFuncs; import tethys.TethysState; @@ -108,7 +109,7 @@ public class DeploymentHandler implements TethysStateObserver { case NEWPROJECTSELECTION: updateProjectDeployments(); break; - case TRANSFERDATA: + case EXPORTRDATA: updateProjectDeployments(); break; case UPDATESERVER: @@ -334,7 +335,7 @@ public class DeploymentHandler implements TethysStateObserver { catch (TethysException e) { getTethysControl().showException(e); } - getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); } /** @@ -372,7 +373,7 @@ public class DeploymentHandler implements TethysStateObserver { getTethysControl().showException(e); } } - getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); } /** diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 628fe1c9..33b5dfa9 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -196,7 +196,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } catch (TethysException e) { getTethysControl().showException(e); } - getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER)); + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); } /** diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index 56ba1b20..2e62c921 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -8,7 +8,8 @@ import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import tethys.TethysControl; -import tethys.calibration.swing.CalibrationsPanel; +import tethys.calibration.swing.CalibrationsMainPanel; +import tethys.calibration.swing.CalibrationsTable; public class TethysMainPanel extends TethysGUIPanel { @@ -26,7 +27,7 @@ public class TethysMainPanel extends TethysGUIPanel { private DetectionsExportPanel detectionsExportPanel; - private CalibrationsPanel calibrationPanel; + private CalibrationsMainPanel calibrationPanel; public TethysMainPanel(TethysControl tethysControl) { super(tethysControl); @@ -39,7 +40,7 @@ public class TethysMainPanel extends TethysGUIPanel { detectionsExportPanel = new DetectionsExportPanel(tethysControl); datablockSynchPanel.addTableObserver(detectionsExportPanel); datablockSynchPanel.addTableObserver(datablockDetectionsPanel); - calibrationPanel = new CalibrationsPanel(tethysControl, tethysControl.getCalibrationHandler()); + calibrationPanel = new CalibrationsMainPanel(tethysControl, tethysControl.getCalibrationHandler()); JSplitPane southwestSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); JPanel southEastPanel = new JPanel(new BorderLayout()); diff --git a/src/tethys/swing/export/AlgorithmCard.java b/src/tethys/swing/export/AlgorithmCard.java index ff949d2b..b9eac701 100644 --- a/src/tethys/swing/export/AlgorithmCard.java +++ b/src/tethys/swing/export/AlgorithmCard.java @@ -21,7 +21,7 @@ public class AlgorithmCard extends ExportWizardCard { private JTextField method, software, version, supportSoftware; public AlgorithmCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { - super(tethysControl, "Algorithm", dataBlock); + super(tethysControl, detectionsExportWizard, "Algorithm", dataBlock); setBorder(new TitledBorder("Algorithm details")); method = new JTextField(40); software = new JTextField(40); diff --git a/src/tethys/swing/export/DescriptionCard.java b/src/tethys/swing/export/DescriptionCard.java index c2bacaef..f6d0c1ef 100644 --- a/src/tethys/swing/export/DescriptionCard.java +++ b/src/tethys/swing/export/DescriptionCard.java @@ -11,7 +11,7 @@ public class DescriptionCard extends ExportWizardCard { private DescriptionTypePanel descriptionPanel; public DescriptionCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { - super(tethysControl, "Description", dataBlock); + super(tethysControl, detectionsExportWizard, "Description", dataBlock); this.setLayout(new BorderLayout()); descriptionPanel = new DescriptionTypePanel("Description data", true, true, true); this.add(BorderLayout.CENTER, descriptionPanel.getMainPanel()); diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index 9973d3c3..3955a69f 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -15,28 +15,27 @@ import javax.swing.JTextArea; import javax.swing.border.TitledBorder; import PamView.dialog.PamDialog; +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; import PamguardMVC.PamDataBlock; import tethys.TethysControl; import tethys.output.StreamExportParams; import tethys.pamdata.TethysDataProvider; -public class DetectionsExportWizard extends PamDialog { +public class DetectionsExportWizard extends PamWizard { private PamDataBlock dataBlock; private CardLayout cardLayout; - private JPanel cardPanel; private GranularityCard granularityCard; private DescriptionCard descriptionCard; - private JButton prevButton; private StreamExportParams streamExportParams; - private ArrayList wizardCards = new ArrayList(); private AlgorithmCard algorithmCard; private ExportWorkerCard exportWorkerCard; private TethysDataProvider tethysDataProvider; private DetectionsExportWizard(Window parentFrame, TethysControl tethysControl, PamDataBlock dataBlock) { - super(parentFrame, "Detections Export", false); + super(parentFrame, "Detections Export"); this.dataBlock = dataBlock; streamExportParams = tethysControl.getTethysExportParams().getStreamParams(dataBlock); @@ -44,31 +43,15 @@ public class DetectionsExportWizard extends PamDialog { streamExportParams = new StreamExportParams(tethysControl, dataBlock, false); } tethysDataProvider = dataBlock.getTethysDataProvider(tethysControl); + getMainPanel().add(BorderLayout.NORTH, new ExportStreamInfoPanel(dataBlock)); - cardLayout = new CardLayout(); - JPanel mainPanel = new JPanel(new BorderLayout()); - mainPanel.add(BorderLayout.NORTH, new ExportStreamInfoPanel(dataBlock)); - cardPanel = new JPanel(cardLayout); - mainPanel.add(BorderLayout.CENTER, cardPanel); - addCard(algorithmCard = new AlgorithmCard(this, tethysControl, dataBlock)); addCard(granularityCard = new GranularityCard(this, tethysControl, dataBlock)); addCard(descriptionCard = new DescriptionCard(this, tethysControl, dataBlock)); addCard(exportWorkerCard = new ExportWorkerCard(this, tethysControl, dataBlock)); - cardLayout.first(cardPanel); + moveFirst(); - setDialogComponent(mainPanel); - - getOkButton().setText("Next"); - prevButton = new JButton("Previous"); - getButtonPanel().add(prevButton, 0); - prevButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - previousButton(); - } - }); setResizable(true); @@ -77,8 +60,7 @@ public class DetectionsExportWizard extends PamDialog { private void addCard(ExportWizardCard wizPanel) { if (tethysDataProvider.wantExportDialogCard(wizPanel)) { - cardPanel.add(wizPanel, wizPanel.getTitle()); - wizardCards.add(wizPanel); + super.addCard(wizPanel); } } @@ -88,49 +70,18 @@ public class DetectionsExportWizard extends PamDialog { wiz.setVisible(true); } - private void setParams() { - for (ExportWizardCard wizCard : wizardCards) { - wizCard.setParams(streamExportParams); - } - enableControls(); -// granularityCard.setParams(streamExportParams); - } - /** - * Called when 'previous' button is clicked. - */ - protected void previousButton() { - cardLayout.previous(cardPanel); - enableControls(); - } - - public JButton getPreviousButton() { - return prevButton; + @Override + public void setCardParams(PamWizardCard wizardCard) { + wizardCard.setParams(streamExportParams); } @Override - public boolean getParams() { - int iCard = getCardIndex(); - if (iCard < wizardCards.size()-1) { - if (checkCurrentCard()) { - cardLayout.next(cardPanel); - enableControls(); - } - return false; - } - -// if (cardLayout.) -// cardLayout.next(mainPanel); -// System.out.println(mainPanel.getComponent(0).isShowing()); - /* - * there seems to be no obvious way of knowing which card is showing except - * to go through and see which one has isShowing() == true, then test for first and - * last, etc. - */ - enableControls(); - return false; + public boolean getCardParams(PamWizardCard wizardCard) { + return wizardCard.getParams(streamExportParams); } + @Override public void cancelButtonPressed() { // TODO Auto-generated method stub @@ -143,29 +94,5 @@ public class DetectionsExportWizard extends PamDialog { } - private void enableControls() { - int iCard = getCardIndex(); - prevButton.setEnabled(iCard > 0); - boolean isLast = iCard == wizardCards.size()-1; - getOkButton().setEnabled(!isLast); -// getOkButton().setText(isLast ? "Export" : "Next"); - } - private boolean checkCurrentCard() { - int iCard = getCardIndex(); - if (iCard < 0) { - return true; - } - return wizardCards.get(iCard).getParams(streamExportParams); - } - - private int getCardIndex() { - for (int i = 0; i < cardPanel.getComponentCount(); i++) { - Component component = cardPanel.getComponent(i); - if (component.isVisible()) { - return i; - } - } - return -1; - } } diff --git a/src/tethys/swing/export/ExportWizardCard.java b/src/tethys/swing/export/ExportWizardCard.java index 9b33369b..fabdfc83 100644 --- a/src/tethys/swing/export/ExportWizardCard.java +++ b/src/tethys/swing/export/ExportWizardCard.java @@ -2,6 +2,8 @@ package tethys.swing.export; import javax.swing.JPanel; +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; import PamguardMVC.PamDataBlock; import tethys.TethysControl; import tethys.output.StreamExportParams; @@ -12,7 +14,7 @@ import tethys.output.StreamExportParams; * @author dg50 * */ -abstract public class ExportWizardCard extends JPanel { +abstract public class ExportWizardCard extends PamWizardCard { private static final long serialVersionUID = 1L; @@ -20,9 +22,9 @@ abstract public class ExportWizardCard extends JPanel { private PamDataBlock dataBlock; private TethysControl tethysControl; - public ExportWizardCard(TethysControl tethysControl, String title, PamDataBlock dataBlock) { + public ExportWizardCard(TethysControl tethysControl, PamWizard pamWizard, String title, PamDataBlock dataBlock) { + super(pamWizard, title); this.tethysControl = tethysControl; - this.title = title; this.dataBlock = dataBlock; } @@ -33,13 +35,5 @@ abstract public class ExportWizardCard extends JPanel { public TethysControl getTethysControl() { return tethysControl; } - - public abstract boolean getParams(StreamExportParams streamExportParams); - - public abstract void setParams(StreamExportParams streamExportParams); - - public String getTitle() { - return title; - } } diff --git a/src/tethys/swing/export/ExportWorkerCard.java b/src/tethys/swing/export/ExportWorkerCard.java index 1cc44e79..ea07875e 100644 --- a/src/tethys/swing/export/ExportWorkerCard.java +++ b/src/tethys/swing/export/ExportWorkerCard.java @@ -39,7 +39,7 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor private DetectionsExportWizard detectionsExportWizard; public ExportWorkerCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { - super(tethysControl, "Export", dataBlock); + super(tethysControl, detectionsExportWizard, "Export", dataBlock); this.detectionsExportWizard = detectionsExportWizard; setLayout(new BorderLayout()); setBorder(new TitledBorder("Export data")); diff --git a/src/tethys/swing/export/GranularityCard.java b/src/tethys/swing/export/GranularityCard.java index c3aeaf8e..e88f9b2d 100644 --- a/src/tethys/swing/export/GranularityCard.java +++ b/src/tethys/swing/export/GranularityCard.java @@ -51,7 +51,7 @@ public class GranularityCard extends ExportWizardCard { private GranularityEnumType[] allowedGranularities; public GranularityCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { - super(tethysControl, "Granularity", dataBlock); + super(tethysControl, detectionsExportWizard, "Granularity", dataBlock); this.detectionsExportWizard = detectionsExportWizard; setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); From bafe93e7f54ceb38859b610d6aa1bc7ee13be501 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 6 Dec 2023 12:20:40 +0000 Subject: [PATCH 59/65] SQLLogging revert Revert main table definition class used by SQLLogging to PamTableDefinition, back from EmptyTableDefinition, to maintain compatibility with existing plugin modules. --- src/annotation/handler/OneStopAnnotationHandler.java | 2 +- src/generalDatabase/LogXMLSettings.java | 2 +- src/generalDatabase/SQLLogging.java | 10 +++++----- src/generalDatabase/XMLSettingsTableDefinition.java | 2 +- src/loc3d_Thode/TowedArray3DSQLLogging.java | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/annotation/handler/OneStopAnnotationHandler.java b/src/annotation/handler/OneStopAnnotationHandler.java index 50dc1983..1b2caa34 100644 --- a/src/annotation/handler/OneStopAnnotationHandler.java +++ b/src/annotation/handler/OneStopAnnotationHandler.java @@ -127,7 +127,7 @@ public abstract class OneStopAnnotationHandler extends AnnotationChoiceHandler i return; } // get, modify and set the modified tabelDef - EmptyTableDefinition tableDef = logging.getBaseTableDefinition(); + PamTableDefinition tableDef = logging.getBaseTableDefinition(); logging.setTableDefinition(tableDef); addAnnotationSqlAddons(logging); // this will add the additional columns. DBControlUnit dbc = DBControlUnit.findDatabaseControl(); diff --git a/src/generalDatabase/LogXMLSettings.java b/src/generalDatabase/LogXMLSettings.java index 76a576b5..3f33c188 100644 --- a/src/generalDatabase/LogXMLSettings.java +++ b/src/generalDatabase/LogXMLSettings.java @@ -13,7 +13,7 @@ import PamController.settings.output.xml.PamguardXMLWriter; import PamUtils.PamCalendar; import PamguardMVC.PamDataUnit; /** - * 2022 Additional say of saving settings for each module into the database in more human readable + * 2022 Additional way of saving settings for each module into the database in more human readable * XML format.

* In other ways, similar to LogSettings which saves serialised Java. This will write a line * per module. A main difference is that on pamStop it will update the end time for each line, so diff --git a/src/generalDatabase/SQLLogging.java b/src/generalDatabase/SQLLogging.java index deb195cf..74aa9066 100644 --- a/src/generalDatabase/SQLLogging.java +++ b/src/generalDatabase/SQLLogging.java @@ -92,7 +92,7 @@ public abstract class SQLLogging { * Reference to the table definition object. * This MUST be set from within the concrete logging class. */ - private EmptyTableDefinition pamTableDefinition; + private PamTableDefinition pamTableDefinition; /** * More and more data blocks are starting to use annotations, which require @@ -102,7 +102,7 @@ public abstract class SQLLogging { * table definition is set, so that it can be got and modified by the * annotation handler shortly after the main table is created. */ - private EmptyTableDefinition baseTableDefinition; + private PamTableDefinition baseTableDefinition; // private long selectT1, selectT2; private PamViewParameters currentViewParameters; @@ -234,7 +234,7 @@ public abstract class SQLLogging { * @return a Pamguard database table definition object * @see PamTableDefinition */ - public final EmptyTableDefinition getTableDefinition() { + public final PamTableDefinition getTableDefinition() { return pamTableDefinition; } @@ -243,7 +243,7 @@ public abstract class SQLLogging { * * @param pamTableDefinition PamTableDefinition to set */ - public void setTableDefinition(EmptyTableDefinition pamTableDefinition) { + public void setTableDefinition(PamTableDefinition pamTableDefinition) { this.pamTableDefinition = pamTableDefinition; if (baseTableDefinition == null && pamTableDefinition != null) { baseTableDefinition = pamTableDefinition.clone(); @@ -1827,7 +1827,7 @@ public abstract class SQLLogging { * annotation handler shortly after the main table is created. * @return the baseTableDefinition */ - public EmptyTableDefinition getBaseTableDefinition() { + public PamTableDefinition getBaseTableDefinition() { return baseTableDefinition; } diff --git a/src/generalDatabase/XMLSettingsTableDefinition.java b/src/generalDatabase/XMLSettingsTableDefinition.java index d09b8f05..8d40bc55 100644 --- a/src/generalDatabase/XMLSettingsTableDefinition.java +++ b/src/generalDatabase/XMLSettingsTableDefinition.java @@ -4,7 +4,7 @@ import java.sql.Types; import PamguardMVC.PamConstants; -public class XMLSettingsTableDefinition extends EmptyTableDefinition { +public class XMLSettingsTableDefinition extends PamTableDefinition { private PamTableItem dataStart, dataEnd, processStart, processEnd, type, name, pamGuardVersion, settingsVersion, xmlSettings; diff --git a/src/loc3d_Thode/TowedArray3DSQLLogging.java b/src/loc3d_Thode/TowedArray3DSQLLogging.java index 108c000c..95bc98e6 100644 --- a/src/loc3d_Thode/TowedArray3DSQLLogging.java +++ b/src/loc3d_Thode/TowedArray3DSQLLogging.java @@ -34,14 +34,14 @@ public class TowedArray3DSQLLogging extends PamDetectionLogging { this.towedArray3DController = towedArray3DController; // create the table definition. - EmptyTableDefinition tableDefinition = createTableDefinition(); + PamTableDefinition tableDefinition = createTableDefinition(); } - public EmptyTableDefinition createTableDefinition() { + public PamTableDefinition createTableDefinition() { // PamTableDefinition tableDef = new PamTableDefinition(towedArray3DController.getUnitName(), getUpdatePolicy()); - EmptyTableDefinition tableDef = super.getTableDefinition(); + PamTableDefinition tableDef = super.getTableDefinition(); tableDef.setUpdatePolicy(SQLLogging.UPDATE_POLICY_WRITENEW); // PamTableItem tableItem; From 7721d7f1c01ff80edaeba7bd418ea56145b30901 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 6 Dec 2023 17:35:57 +0000 Subject: [PATCH 60/65] Global metadata Reorganised how global meta data for a PAMGuard dataset is managed by wrapping a nilus deployment object so that PAMGuard settings can serialise and unpack the xml version of a nilus Deployment object. --- src/PamController/PamController.java | 2 + src/PamModel/PamModel.java | 8 +- src/metadata/MetaDataContol.java | 89 +++++-- src/metadata/PamguardMetaData.java | 79 +++++++ src/metadata/deployment/DeploymentData.java | 217 +++++++++--------- src/tethys/TethysControl.java | 44 ++-- .../calibration/CalibrationHandler.java | 22 +- .../swing/CalibrationProcessCard.java | 3 + src/tethys/deployment/DeploymentHandler.java | 37 +-- .../niluswraps/NilusSettingsWrapper.java | 196 ++++++++++++++++ src/tethys/output/TethysExporter.java | 40 ++-- src/tethys/swing/DeploymentExportPanel.java | 4 +- src/tethys/swing/NewProjectDialog.java | 29 +-- src/tethys/swing/TethysConnectionPanel.java | 22 +- 14 files changed, 570 insertions(+), 222 deletions(-) create mode 100644 src/metadata/PamguardMetaData.java create mode 100644 src/tethys/niluswraps/NilusSettingsWrapper.java diff --git a/src/PamController/PamController.java b/src/PamController/PamController.java index d63fa262..160ae7f4 100644 --- a/src/PamController/PamController.java +++ b/src/PamController/PamController.java @@ -54,6 +54,7 @@ import fftManager.FFTDataUnit; import generalDatabase.DBControlUnit; import javafx.application.Platform; import javafx.stage.Stage; +import metadata.MetaDataContol; import Array.ArrayManager; import PamController.command.MulticastController; import PamController.command.NetworkController; @@ -450,6 +451,7 @@ public class PamController implements PamControllerInterface, PamSettings { System.out.println(""); System.out.println("Note - ignore the following SLF4J warn/error messages, they are not applicable to this application"); ArrayManager.getArrayManager(); // create the array manager so that it get's it's settings + MetaDataContol.getMetaDataControl(); /** * Check for archived files and unpack automatically. diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index 003d657b..f793aa58 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -461,10 +461,10 @@ final public class PamModel implements PamModelInterface, PamSettings { mi.setMaxNumber(1); - mi = PamModuleInfo.registerControlledUnit(MetaDataContol.class.getName(), MetaDataContol.unitType); - mi.setToolTipText("Project Meta Data"); - mi.setModulesMenuGroup(utilitiesGroup); - mi.setMaxNumber(1); +// mi = PamModuleInfo.registerControlledUnit(MetaDataContol.class.getName(), MetaDataContol.unitType); +// mi.setToolTipText("Project Meta Data"); +// mi.setModulesMenuGroup(utilitiesGroup); +// mi.setMaxNumber(1); if (isViewer) { mi = PamModuleInfo.registerControlledUnit(TethysControl.class.getName(), TethysControl.defaultName); diff --git a/src/metadata/MetaDataContol.java b/src/metadata/MetaDataContol.java index 98c60eb1..c4243ef9 100644 --- a/src/metadata/MetaDataContol.java +++ b/src/metadata/MetaDataContol.java @@ -1,44 +1,99 @@ package metadata; +import java.io.Serializable; + import javax.swing.JFrame; import javax.swing.JMenuItem; import PamController.PamControlledUnit; +import PamController.PamControlledUnitSettings; +import PamController.PamController; +import PamController.PamSettingManager; +import PamController.PamSettings; import PamModel.parametermanager.ParameterSetManager; import generalDatabase.parameterstore.ParameterDatabaseStore; import metadata.deployment.DeploymentData; -public class MetaDataContol extends PamControlledUnit { +/** + * Class to handle Project MetaData. Am making this a PAMControlledUnit, but may never + * register it with the model ? Will see what advantages and disadvantages there are. + * @author dg50 + * + */ +public class MetaDataContol extends PamControlledUnit implements PamSettings { public static final String unitType = "Meta Data"; - private DeploymentData deploymentData = new DeploymentData(); + private static MetaDataContol singleInstance; - private ParameterSetManager deploymentSetManager; + private PamguardMetaData pamguardMetaData = new PamguardMetaData(); + +// private ParameterSetManager deploymentSetManager; - public MetaDataContol(String unitName) { + private MetaDataContol(String unitName) { super(unitType, unitName); - deploymentSetManager = new ParameterSetManager(deploymentData, "Deployment Data"); +// deploymentSetManager = new ParameterSetManager(deploymentData, "Deployment Data"); + PamSettingManager.getInstance().registerSettings(this); + } + + /** + * Easy getter for singleton MetaData controller. + * @return meta data controller + */ + public static MetaDataContol getMetaDataControl() { + if (singleInstance == null) { + singleInstance = new MetaDataContol(unitType); + // add this line to add it to the main modules list. Then it will get menu's, etc. + PamController.getInstance().addControlledUnit(singleInstance); + } + return singleInstance; + } + + /** + * Get PAMGuard Metadata. This contains a nilus Deployment object wrapped up + * so that it can be serialised into other PAMGuard settings. + * @return PAMGuard meta data + */ + public PamguardMetaData getMetaData() { + return pamguardMetaData; + } + + /** + * Set the meta data object. + * @param metaData + */ + public void setMetaData(PamguardMetaData metaData) { + this.pamguardMetaData = metaData; } @Override - public JMenuItem createFileMenu(JFrame parentFrame) { - return deploymentSetManager.getMenuItem(parentFrame); + public Serializable getSettingsReference() { + pamguardMetaData.checkSerialisation(); + return pamguardMetaData; } - /** - * @return the deploymentData - */ - public DeploymentData getDeploymentData() { - return deploymentData; + @Override + public long getSettingsVersion() { + return PamguardMetaData.serialVersionUID; } - /** - * @param deploymentData the deploymentData to set - */ - public void setDeploymentData(DeploymentData deploymentData) { - this.deploymentData = deploymentData; + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + Object obj = pamControlledUnitSettings.getSettings(); + if (obj instanceof PamguardMetaData) { + pamguardMetaData = (PamguardMetaData) obj; + return true; + } + return false; } +// @Override +// public JMenuItem createFileMenu(JFrame parentFrame) { +// return deploymentSetManager.getMenuItem(parentFrame); +//// return null; +// } + + + } diff --git a/src/metadata/PamguardMetaData.java b/src/metadata/PamguardMetaData.java new file mode 100644 index 00000000..9d1079b7 --- /dev/null +++ b/src/metadata/PamguardMetaData.java @@ -0,0 +1,79 @@ +package metadata; + +import java.io.Serializable; + +import PamUtils.LatLong; +import nilus.Deployment; +import nilus.Helper; +import tethys.niluswraps.NilusSettingsWrapper; + +/** + * Meta data for a PAMGuard data set. This is based around serialisable versions of + * nilus classes to be compliant with both Tethys and PAMGuard settings files. May only + * need a Deployment object, but scope for adding others / other fields if it's useful. + * @author dg50 + * + */ +public class PamguardMetaData implements Serializable { + + public static final long serialVersionUID = 1L; + + private NilusSettingsWrapper deploymentWrapper; + +// /** +// * Deployment time (used if different +// */ +// private Long deploymentMillis; +// +// private Long recoverMillis; +// +// private LatLong recoverLatLong; + + /** + * Get the deployment data + * @return nilus deployment + */ + public Deployment getDeployment() { + if (deploymentWrapper == null) { + deploymentWrapper = new NilusSettingsWrapper<>(); + } + Deployment deployment = deploymentWrapper.getNilusObject(Deployment.class); + if (deployment == null) { + deployment = new Deployment(); + try { + Helper.createRequiredElements(deployment); + } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + deploymentWrapper.setNilusObject(deployment); + } + return deploymentWrapper.getNilusObject(Deployment.class); + } + + /** + * Set the deployment data. + * @param deployment nilus deployment + */ + public void setDeployment(Deployment deployment) { + if (deploymentWrapper == null) { + deploymentWrapper = new NilusSettingsWrapper<>(); + } + deploymentWrapper.setNilusObject(deployment); + } + + /** + * @return the deploymentWrapper + */ + public NilusSettingsWrapper getDeploymentWrapper() { + return deploymentWrapper; + } + + public void checkSerialisation() { + // check that all wrappers have their xml up to date. + deploymentWrapper.reSerialise(); + } + + + +} diff --git a/src/metadata/deployment/DeploymentData.java b/src/metadata/deployment/DeploymentData.java index 4cf50403..440e6549 100644 --- a/src/metadata/deployment/DeploymentData.java +++ b/src/metadata/deployment/DeploymentData.java @@ -11,7 +11,8 @@ import PamUtils.LatLong; /** * Class to hold Deployment data in a form consistent with the ANSI PAM * Standard. This has been keep separate from the Tethys Interface to keep it - * easy to benefit from these data without using Tethys itself. + * easy to benefit from these data without using Tethys itself. Is also serilaisable + * which is important for storage into PAMGuard settings. * * @author dg50 * @@ -108,15 +109,17 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter this.recoveryMillis = recoveryMillis; } -// /** -// * Instrument type, e.g. HARP, EAR, Popup, DMON, etc. -// */ -// private String instrumentType; -// -// /** -// * Instrument identifier, e.g. serial number -// */ -// private String instrumentId; + /** + * Set data from a nilus deployment class + * @param nilusDeployment + */ + public void setData(nilus.Deployment nilusDeployment) { + + } + + public void getData(nilus.Deployment nilusDeployment) { + + } public DeploymentData() { } @@ -151,103 +154,103 @@ public class DeploymentData implements Serializable, Cloneable, ManagedParameter return ps; } - /** - * @return the id - */ - public String getId() { - return id; - } - - /** - * @param id the id to set - */ - public void setId(String id) { - this.id = id; - } - - /** - * @return the project - */ - public String getProject() { - return project; - } - - /** - * @param project the project to set - */ - public void setProject(String project) { - this.project = project; - } - - /** - * @return the deploymentId - */ - public int getDeploymentId() { - return deploymentId; - } - - /** - * @param deploymentId the deploymentId to set - */ - public void setDeploymentId(int deploymentId) { - this.deploymentId = deploymentId; - } - - /** - * @return the deplomentAlias - */ - public String getDeploymentAlias() { - return deploymentAlias; - } - - /** - * @param deplomentAlias the deplomentAlias to set - */ - public void setDeploymentAlias(String deplomentAlias) { - this.deploymentAlias = deplomentAlias; - } - - /** - * @return the site - */ - public String getSite() { - return site; - } - - /** - * @param site the site to set - */ - public void setSite(String site) { - this.site = site; - } - - /** - * @return the siteAliases - */ - public String getSiteAliases() { - return siteAliases; - } - - /** - * @param siteAliases the siteAliases to set - */ - public void setSiteAliases(String siteAliases) { - this.siteAliases = siteAliases; - } - - /** - * @return the cruise - */ - public String getCruise() { - return cruise; - } - - /** - * @param cruise the cruise to set - */ - public void setCruise(String cruise) { - this.cruise = cruise; - } +// /** +// * @return the id +// */ +// public String getId() { +// return id; +// } +// +// /** +// * @param id the id to set +// */ +// public void setId(String id) { +// this.id = id; +// } +// +// /** +// * @return the project +// */ +// public String getProject() { +// return project; +// } +// +// /** +// * @param project the project to set +// */ +// public void setProject(String project) { +// this.project = project; +// } +// +// /** +// * @return the deploymentId +// */ +// public int getDeploymentId() { +// return deploymentId; +// } +// +// /** +// * @param deploymentId the deploymentId to set +// */ +// public void setDeploymentId(int deploymentId) { +// this.deploymentId = deploymentId; +// } +// +// /** +// * @return the deplomentAlias +// */ +// public String getDeploymentAlias() { +// return deploymentAlias; +// } +// +// /** +// * @param deplomentAlias the deplomentAlias to set +// */ +// public void setDeploymentAlias(String deplomentAlias) { +// this.deploymentAlias = deplomentAlias; +// } +// +// /** +// * @return the site +// */ +// public String getSite() { +// return site; +// } +// +// /** +// * @param site the site to set +// */ +// public void setSite(String site) { +// this.site = site; +// } +// +// /** +// * @return the siteAliases +// */ +// public String getSiteAliases() { +// return siteAliases; +// } +// +// /** +// * @param siteAliases the siteAliases to set +// */ +// public void setSiteAliases(String siteAliases) { +// this.siteAliases = siteAliases; +// } +// +// /** +// * @return the cruise +// */ +// public String getCruise() { +// return cruise; +// } +// +// /** +// * @param cruise the cruise to set +// */ +// public void setCruise(String cruise) { +// this.cruise = cruise; +// } // /** // * @return the platform diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 1a5f2b7d..213cf2ff 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -36,7 +36,9 @@ import PamView.PamTabPanel; import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; +import metadata.PamguardMetaData; import metadata.deployment.DeploymentData; +import nilus.Deployment; import tethys.TethysState.StateType; import tethys.calibration.CalibrationHandler; import tethys.dbxml.DBXMLConnect; @@ -406,31 +408,23 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet * 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) { -// deployment.setProject("thisIsAProject"); -// deployment.setPlatform("Yay a platform"); -// Instrument instrument = new Instrument(); -// instrument.setType("machiney"); -// instrument.setInstrumentId("12345555"); -// deployment.setInstrument(instrument); -// return false; -// } + public Deployment getGlobalDeplopymentData() { - MetaDataContol metaControl = (MetaDataContol) aUnit; - 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"); - - return deploymentData; + MetaDataContol metaControl = MetaDataContol.getMetaDataControl(); + PamguardMetaData metaData = metaControl.getMetaData(); + return metaData.getDeployment(); +// Deployment deploymentData = metaControl != null ? metaData.getDeployment() : 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"); +// +// return deploymentData; } /** @@ -545,7 +539,7 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet if (dataBlockSynchInfos == null) { return; } - DeploymentData deplData = getGlobalDeplopymentData(); + Deployment deplData = getGlobalDeplopymentData(); String[] dataPrefixes = new String[dataBlockSynchInfos.size()]; int i = 0; ArrayList matchedDeployments = deploymentHandler.getMatchedDeployments(); diff --git a/src/tethys/calibration/CalibrationHandler.java b/src/tethys/calibration/CalibrationHandler.java index 0c283a31..e62f89e4 100644 --- a/src/tethys/calibration/CalibrationHandler.java +++ b/src/tethys/calibration/CalibrationHandler.java @@ -4,7 +4,6 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; -import java.util.Collections; import java.util.Date; import java.util.List; @@ -116,9 +115,14 @@ public class CalibrationHandler implements TethysStateObserver { * Update the list of documents associated with the selected instrument. */ private void updateDocumentsList() { + + calibrationsList.clear(); + ArrayList docsList = getArrayCalibrations(); // now immediately read the calibrations in again. - calibrationsList.clear();; + if (docsList == null) { + return; + } NilusUnpacker unpacker = new NilusUnpacker(); for (DocumentInfo aDoc : docsList) { Queries queries = tethysControl.getDbxmlConnect().getTethysQueries(); @@ -182,11 +186,7 @@ public class CalibrationHandler implements TethysStateObserver { // String docName = getHydrophoneId(i); Calibration calDoc = createCalibrationDocument(i); if (sampleCal != null) { - calDoc.setMetadataInfo(sampleCal.getMetadataInfo()); - MetadataInfo oldMeta = calDoc.getMetadataInfo(); - MetadataInfo newMeta = sampleCal.getMetadataInfo(); - - + calDoc.setMetadataInfo(sampleCal.getMetadataInfo()); calDoc.setProcess(sampleCal.getProcess()); calDoc.setQualityAssurance(sampleCal.getQualityAssurance()); calDoc.setResponsibleParty(sampleCal.getResponsibleParty()); @@ -234,7 +234,7 @@ public class CalibrationHandler implements TethysStateObserver { * Add the separate pamguard parameters to the document which are used * to make up the overall calibration. * @param calDoc - * @param i + * @param i hydrophone number */ private void addParameterDetails(Calibration calDoc, int i) { Parameters params = calDoc.getProcess().getParameters(); @@ -518,8 +518,10 @@ public class CalibrationHandler implements TethysStateObserver { */ private ArrayList getArrayCalibrations() { ArrayList allCals = tethysControl.getDbxmlQueries().getCollectionDocumentList(Collection.Calibrations); - String prefix = createCalibrationDocumentRoot(); - // find doc names that have that root. + if (allCals == null) { + return null; + } + String prefix = createCalibrationDocumentRoot(); // find doc names that have that root. ArrayList theseCals = new ArrayList<>(); for (DocumentInfo aDoc : allCals) { if (aDoc.getDocumentName().startsWith(prefix)) { diff --git a/src/tethys/calibration/swing/CalibrationProcessCard.java b/src/tethys/calibration/swing/CalibrationProcessCard.java index 7b9ef3f3..a2fcb334 100644 --- a/src/tethys/calibration/swing/CalibrationProcessCard.java +++ b/src/tethys/calibration/swing/CalibrationProcessCard.java @@ -78,9 +78,11 @@ public class CalibrationProcessCard extends CalibrationsCard { c.gridwidth = 1; processPanel.add(new JLabel("Serial number ", JLabel.RIGHT), c); c.gridx++; + c.gridwidth = 2; processPanel.add(version, c); c.gridx = 0; c.gridy++; + c.gridwidth = 1; processPanel.add(new JLabel("Quality ", JLabel.RIGHT), c); c.gridx++; processPanel.add(qaQuality, c); @@ -88,6 +90,7 @@ public class CalibrationProcessCard extends CalibrationsCard { c.gridy++; processPanel.add(new JLabel("QA Comment ", JLabel.RIGHT), c); c.gridx++; + c.gridwidth = 2; processPanel.add(qaComment, c); this.add(BorderLayout.CENTER, makeScrollablePanel(software, "Calibration method")); diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 1d1167ca..2d4e8f3a 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.ListIterator; import javax.xml.bind.JAXBException; +import javax.xml.datatype.XMLGregorianCalendar; import org.apache.commons.beanutils.converters.BigIntegerConverter; @@ -34,6 +35,8 @@ import binaryFileStorage.BinaryStore; import dataMap.OfflineDataMap; import dataMap.OfflineDataMapPoint; import generalDatabase.DBControlUnit; +import metadata.MetaDataContol; +import metadata.PamguardMetaData; import metadata.deployment.DeploymentData; import nilus.Audio; import nilus.ChannelInfo; @@ -125,7 +128,7 @@ public class DeploymentHandler implements TethysStateObserver { * @return true if OK */ public boolean updateProjectDeployments() { - DeploymentData projData = tethysControl.getGlobalDeplopymentData(); + Deployment projData = tethysControl.getGlobalDeplopymentData(); ArrayList tethysDocs = tethysControl.getDbxmlQueries().getProjectDeployments(projData.getProject(), getInstrumentId()); if (tethysDocs == null) { return false; @@ -310,7 +313,7 @@ public class DeploymentHandler implements TethysStateObserver { selectedDeployments.get(selectedDeployments.size()-1).getRecordStop()); Deployment deployment = createDeploymentDocument(freeId, onePeriod); // fill in a few things from here - DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); + Deployment globalMeta = getTethysControl().getGlobalDeplopymentData(); deployment.setCruise(globalMeta.getCruise()); deployment.setSite(globalMeta.getSite()); if (selectedDeployments.size() > 1) { @@ -344,6 +347,8 @@ public class DeploymentHandler implements TethysStateObserver { private void exportSeparateDeployments(ArrayList selectedDeployments) { int freeId = getTethysControl().getDeploymentHandler().getFirstFreeDeploymentId(); + // fill in a few things from here + Deployment globalMeta = getTethysControl().getGlobalDeplopymentData(); for (int i = 0; i < selectedDeployments.size(); i++) { RecordingPeriod recordPeriod = selectedDeployments.get(i); PDeployment exDeploymnet = recordPeriod.getMatchedTethysDeployment(); @@ -355,8 +360,6 @@ public class DeploymentHandler implements TethysStateObserver { if (deployment == null) { deployment = createDeploymentDocument(freeId++, recordPeriod); } - // fill in a few things from here - DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); deployment.setCruise(globalMeta.getCruise()); deployment.setSite(globalMeta.getSite()); // also need to sort out track data here, etc. @@ -740,7 +743,7 @@ public class DeploymentHandler implements TethysStateObserver { // TODO Auto-generated catch block e.printStackTrace(); } - DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); +// Deployment globalDeplData = tethysControl.getGlobalDeplopymentData(); TethysExportParams exportParams = tethysControl.getTethysExportParams(); String id = String.format("%s_%d", exportParams.getDatasetName(), i); deployment.setId(id); @@ -903,7 +906,8 @@ public class DeploymentHandler implements TethysStateObserver { // } // // MetaDataContol metaControl = (MetaDataContol) aUnit; - DeploymentData deploymentData = tethysControl.getGlobalDeplopymentData(); + PamguardMetaData metaData = MetaDataContol.getMetaDataControl().getMetaData(); + Deployment deploymentData = tethysControl.getGlobalDeplopymentData(); deployment.setProject(deploymentData.getProject()); deployment.setDeploymentAlias(deploymentData.getDeploymentAlias()); deployment.setSite(deploymentData.getSite()); @@ -919,18 +923,19 @@ public class DeploymentHandler implements TethysStateObserver { deployment.setInstrument(instrument); // overwrite the default deployment and recovery times if there is non null data - Long depMillis = deploymentData.getDeploymentMillis(); - if (depMillis != null) { - deployment.getDeploymentDetails().setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(depMillis)); + XMLGregorianCalendar depTime = deploymentData.getDeploymentDetails().getTimeStamp(); + if (depTime != null) { + deployment.getDeploymentDetails().setTimeStamp(depTime); } - Long recMillis = deploymentData.getRecoveryMillis(); + XMLGregorianCalendar recMillis = deploymentData.getRecoveryDetails().getTimeStamp(); if (recMillis != null) { - deployment.getRecoveryDetails().setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(recMillis)); + deployment.getRecoveryDetails().setTimeStamp(recMillis); } - LatLong recLatLong = deploymentData.getRecoverLatLong(); - if (recLatLong != null) { - deployment.getRecoveryDetails().setLatitude(recLatLong.getLatitude()); - deployment.getRecoveryDetails().setLongitude(PamUtils.constrainedAngle(recLatLong.getLongitude())); + double recLat = deploymentData.getRecoveryDetails().getLatitude(); + double recLong = deploymentData.getRecoveryDetails().getLongitude(); + if (recLat != 0 & recLong != 0.) { + deployment.getRecoveryDetails().setLatitude(recLat); + deployment.getRecoveryDetails().setLongitude(PamUtils.constrainedAngle(recLong)); } return true; @@ -951,7 +956,7 @@ public class DeploymentHandler implements TethysStateObserver { */ public String canExportDeployments() { - DeploymentData globalDeplData = tethysControl.getGlobalDeplopymentData(); + Deployment globalDeplData = tethysControl.getGlobalDeplopymentData(); if (globalDeplData.getProject() == null) { return "You must set a project name"; } diff --git a/src/tethys/niluswraps/NilusSettingsWrapper.java b/src/tethys/niluswraps/NilusSettingsWrapper.java new file mode 100644 index 00000000..8364c1d6 --- /dev/null +++ b/src/tethys/niluswraps/NilusSettingsWrapper.java @@ -0,0 +1,196 @@ +package tethys.niluswraps; + +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.StringReader; + +import javax.xml.bind.JAXBException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.w3c.dom.Document; +import org.xml.sax.InputSource; + +import nilus.MarshalXML; + +/** + * Class to wrap up a nilus object so that it can be saved using Java serialization. + * This is basically saving the xml as a String, since all nilus objects are NOT + * serializable, but should make it (relatively) straight forward to save nilus + * objects into PAMGuard settings files. + * @author dg50 + * + */ +public class NilusSettingsWrapper implements Serializable, Cloneable { + + public static final long serialVersionUID = 1L; + + private String xmlString; + + private transient T nilusObject; + + /** + * Create a nilus object. Have to pass the class type in as + * an argument, since Class isn't serializable, so can't be stored + * with the object - which contains nothing but a String, which can be safely serialised. + * @param nilusClass + * @return nilus object. + */ + public T getNilusObject(Class nilusClass) { + if (nilusObject == null) { + nilusObject = unpackNilusObject(nilusClass); + } + return nilusObject; + } + + private T unpackNilusObject(Class nilusClass) { + Document doc = getDocument(); + if (doc == null) { + return null; + } + /** + * Try to turn the string into a document. + */ + NilusUnpacker unpacker = new NilusUnpacker(); + T unpacked = (T) unpacker.unpackDocument(doc, nilusClass); + + return unpacked; + } + + /** + * Set the nilus object. This marshals the nilus object + * into xml and saves the data as an intetnal xml string which + * can be safely serialized. + * @param nilusObject nilus object. + * @return true if it was marshalled OK. + */ + public boolean setNilusObject(T nilusObject) { + // use the marshaller to create a Tethys type document, then + // get it as a string. + this.nilusObject = nilusObject; + // and convert immediately to the XML string. + return packNilusObject(nilusObject); + } + /** + * Set the nilus object. This marshals the nilus object + * into xml and saves the data as an intetnal xml string which + * can be safely serialized. + * @param nilusObject nilus object. + * @return true if it was marshalled OK. + */ + private boolean packNilusObject(T nilusObject) { + // use the marshaller to create a Tethys type document, then + // get it as a string. + // and convert immediately to XML string. + if (nilusObject == null) { + xmlString = null; + return true; + } + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + PrintWriter printWriter = new PrintWriter(bos); + MarshalXML marshaler; + try { + marshaler = new MarshalXML(); + marshaler.marshal(nilusObject, bos); + } catch (JAXBException e) { + e.printStackTrace(); + return false; + } + xmlString = bos.toString(); + return true; + } + + /** + * Repack the object. May want to do this before serializing. + * @return + */ + public boolean repackNilusObject() { + return packNilusObject(nilusObject); + } + + + /** + * Get a document from the internal xml String representation. + * @return xml document + */ + public Document getDocument() { + if(xmlString == null) { + return null; + } + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + //API to obtain DOM Document instance + DocumentBuilder builder = null; + try { + //Create DocumentBuilder with default configuration + builder = factory.newDocumentBuilder(); + //Parse the content to Document object + Document doc = builder.parse(new InputSource(new StringReader(xmlString))); + return doc; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * @return the xmlString + */ + public String getXmlString() { + return xmlString; + } + + /** + * Repack the nilus object. Call this just before settings are + * saved to ensure everything is up to date since this probably won't + * have happened if changes were made within existing nilus objects + * and setNilusObject was never called. + */ + public void reSerialise() { + packNilusObject(nilusObject); + } + + +// private Class getNilusClass() throws NoSuchMethodException, SecurityException { +// Method method = this.getClass().getMethod("getNilusObject", null); +// Class retClass = GenericTypeResolver.resolveReturnType(method, this.getClass()); +// +// return retClass; +// } + +// +// public static void main(String[] args) { +// +// Deployment deployment = new Deployment(); +// try { +// Helper.createRequiredElements(deployment); +// } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { +// e.printStackTrace(); +// } +// deployment.setCruise("Test cruise"); +// deployment.setDeploymentId(111); +// deployment.setProject("Tethys Development"); +// DescriptionType desc = deployment.getDescription(); +// if (desc == null) { +// desc = new DescriptionType(); +// deployment.setDescription(desc); +// } +// desc.setAbstract("Deployment Abstract"); +// +// NilusSettingsWrapper wrapper = new NilusSettingsWrapper<>(); +// +// wrapper.setNilusObject(deployment); +// +// System.out.println(wrapper.xmlString); +// +// Deployment newDeployment = wrapper.getNilusObject(Deployment.class); +// +// // now warp the new object again and print that. +// newDeployment.setDeploymentId(newDeployment.getDeploymentId()*2); +// wrapper.setNilusObject(newDeployment); +// System.out.println("********************************************"); +// System.out.println(wrapper.xmlString); +// +// } + +} diff --git a/src/tethys/output/TethysExporter.java b/src/tethys/output/TethysExporter.java index 7353f6ce..0afe95ff 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -210,26 +210,26 @@ public class TethysExporter { return true; } - /** - * find Deployment data. This is stored in a separate PAMGuard module, which may - * not be present. - * - * @return - */ - public DeploymentData findDeploymentData() { - /** - * What to do if this isn't present or is incomplete ? Should we be showing this - * in the main export dialog at some point ? More a Q for when we make a nicer - * UI later in the project. - */ - MetaDataContol metaControl = (MetaDataContol) PamController.getInstance() - .findControlledUnit(MetaDataContol.unitType); - if (metaControl == null) { - return null; - } else { - return metaControl.getDeploymentData(); - } - } +// /** +// * find Deployment data. This is stored in a separate PAMGuard module, which may +// * not be present. +// * +// * @return +// */ +// public DeploymentData findDeploymentData() { +// /** +// * What to do if this isn't present or is incomplete ? Should we be showing this +// * in the main export dialog at some point ? More a Q for when we make a nicer +// * UI later in the project. +// */ +// MetaDataContol metaControl = (MetaDataContol) PamController.getInstance() +// .findControlledUnit(MetaDataContol.unitType); +// if (metaControl == null) { +// return null; +// } else { +// return metaControl.getDeploymentData(); +// } +// } public SnapshotGeometry findArrayGeometrey() { /* diff --git a/src/tethys/swing/DeploymentExportPanel.java b/src/tethys/swing/DeploymentExportPanel.java index 2c2c6332..0e88ad54 100644 --- a/src/tethys/swing/DeploymentExportPanel.java +++ b/src/tethys/swing/DeploymentExportPanel.java @@ -132,7 +132,7 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT } private void copyDeploymentData(Deployment deployment) { - DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); + Deployment globalMeta = getTethysControl().getGlobalDeplopymentData(); globalMeta.setSite(deployment.getSite()); globalMeta.setCruise(deployment.getCruise()); globalMeta.setRegion(deployment.getRegion()); @@ -189,7 +189,7 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT * Set the parms from internally stored data. */ private void setInternal() { - DeploymentData globalMeta = getTethysControl().getGlobalDeplopymentData(); + Deployment globalMeta = getTethysControl().getGlobalDeplopymentData(); site.setText(globalMeta.getSite()); cruise.setText(globalMeta.getCruise()); // region.setText(globalMeta.getRegion()); diff --git a/src/tethys/swing/NewProjectDialog.java b/src/tethys/swing/NewProjectDialog.java index 03f64318..befb0489 100644 --- a/src/tethys/swing/NewProjectDialog.java +++ b/src/tethys/swing/NewProjectDialog.java @@ -10,7 +10,9 @@ import javax.swing.JTextField; import javax.swing.border.TitledBorder; import PamView.dialog.PamGridBagContraints; +import metadata.PamguardMetaData; import metadata.deployment.DeploymentData; +import nilus.Deployment; import tethys.TethysControl; public class NewProjectDialog extends PamView.dialog.PamDialog { @@ -23,7 +25,7 @@ public class NewProjectDialog extends PamView.dialog.PamDialog { private JTextField projectRegion; - private DeploymentData deploymentData; + private PamguardMetaData metaData; private NewProjectDialog(Window parentFrame, TethysControl tethysControl) { super(parentFrame, "New Project", false); @@ -45,32 +47,33 @@ public class NewProjectDialog extends PamView.dialog.PamDialog { setDialogComponent(mainPanel); } - public static DeploymentData showDialog(Window parent, TethysControl tethysControl, DeploymentData deploymentData) { + public static PamguardMetaData showDialog(Window parent, TethysControl tethysControl, PamguardMetaData metaData) { if (singleInstance == null) { singleInstance = new NewProjectDialog(parent, tethysControl); } - singleInstance.setParams(deploymentData); + singleInstance.setParams(metaData); singleInstance.setVisible(true); - return singleInstance.deploymentData; + return singleInstance.metaData; } - private void setParams(DeploymentData deploymentData) { + private void setParams(PamguardMetaData deploymentData) { if (deploymentData == null) { return; } - this.deploymentData = deploymentData; - projectName.setText(deploymentData.getProject()); - projectRegion.setText(deploymentData.getRegion()); + this.metaData = deploymentData; + projectName.setText(deploymentData.getDeployment().getProject()); + projectRegion.setText(deploymentData.getDeployment().getRegion()); } @Override public boolean getParams() { - if (deploymentData == null) { + if (metaData == null) { return false; } - deploymentData.setProject(projectName.getText()); - deploymentData.setRegion(projectRegion.getText()); - if (deploymentData.getProject() == null || deploymentData.getProject().length() == 0) { + Deployment deployment = metaData.getDeployment(); + deployment.setProject(projectName.getText()); + deployment.setRegion(projectRegion.getText()); + if (deployment.getProject() == null || deployment.getProject().length() == 0) { return showWarning("you must specify a project name"); } @@ -79,7 +82,7 @@ public class NewProjectDialog extends PamView.dialog.PamDialog { @Override public void cancelButtonPressed() { - deploymentData = null; + metaData = null; } @Override diff --git a/src/tethys/swing/TethysConnectionPanel.java b/src/tethys/swing/TethysConnectionPanel.java index 23c531dd..3f6817ed 100644 --- a/src/tethys/swing/TethysConnectionPanel.java +++ b/src/tethys/swing/TethysConnectionPanel.java @@ -22,6 +22,8 @@ import PamView.dialog.ScrollingPamLabel; import PamView.dialog.SettingsButton; import PamView.panel.PamPanel; import PamView.panel.WestAlignedPanel; +import metadata.MetaDataContol; +import metadata.PamguardMetaData; import metadata.deployment.DeploymentData; import nilus.Deployment; import pamViewFX.fxNodes.PamComboBox; @@ -185,9 +187,10 @@ public class TethysConnectionPanel extends TethysGUIPanel { * Action from new project button */ protected void createNewProject() { - DeploymentData pamDeploymentData = getTethysControl().getGlobalDeplopymentData(); + PamguardMetaData pamDeploymentData = MetaDataContol.getMetaDataControl().getMetaData(); pamDeploymentData = NewProjectDialog.showDialog(getTethysControl().getGuiFrame(), getTethysControl(), pamDeploymentData); if (pamDeploymentData != null) { + MetaDataContol.getMetaDataControl().setMetaData(pamDeploymentData); updateProjectList(); } } @@ -197,8 +200,10 @@ public class TethysConnectionPanel extends TethysGUIPanel { if (project == null) { return; } - DeploymentData globData = getTethysControl().getGlobalDeplopymentData(); - globData.setProject(project); + PamguardMetaData pamMetaData = MetaDataContol.getMetaDataControl().getMetaData(); + Deployment globDeployment = pamMetaData.getDeployment(); +// DeploymentData globData = getTethysControl().getGlobalDeplopymentData(); + globDeployment.setProject(project); getTethysControl().getDeploymentHandler().updateProjectDeployments(); /* * if there are existing deployment data, then copy the info to the @@ -207,8 +212,8 @@ public class TethysConnectionPanel extends TethysGUIPanel { 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()); + globDeployment.setProject(dep.getProject()); + globDeployment.setRegion(dep.getRegion()); getTethysControl().sendStateUpdate(new TethysState(TethysState.StateType.NEWPROJECTSELECTION)); } @@ -271,9 +276,10 @@ public class TethysConnectionPanel extends TethysGUIPanel { * list. */ String localProjName = null; - DeploymentData pamDeploymentData = getTethysControl().getGlobalDeplopymentData(); - if (pamDeploymentData != null && pamDeploymentData.getProject() != null) { - localProjName = pamDeploymentData.getProject(); + PamguardMetaData pamMetaData = MetaDataContol.getMetaDataControl().getMetaData(); + Deployment globDeployment = pamMetaData.getDeployment(); + if (globDeployment != null && globDeployment.getProject() != null) { + localProjName = globDeployment.getProject(); if (localProjName.length() == 0) { localProjName = null; } From 9c86b41027116936c683c374b20c01d2399fce1d Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 7 Dec 2023 15:10:45 +0000 Subject: [PATCH 61/65] Project info dialog More wrapping of nilus objects to make a general project info tab in PAMGuard. This is global and can share basic project informatin with the Tethys module. --- src/PamView/PamGui.java | 2 + src/metadata/MetaDataContol.java | 29 +- src/metadata/PamguardMetaData.java | 20 +- src/metadata/deployment/DeploymentData.java | 327 ------------------ src/metadata/deployment/QAData.java | 41 --- src/metadata/swing/MetaDataDialog.java | 131 +++++++ src/tethys/TethysControl.java | 10 - src/tethys/deployment/DeploymentHandler.java | 1 - src/tethys/niluswraps/NilusUnpacker.java | 18 +- ...nType.java => WrappedDescriptionType.java} | 59 ++-- src/tethys/output/StreamExportParams.java | 10 +- src/tethys/output/TethysExportParams.java | 9 +- src/tethys/output/TethysExporter.java | 1 - src/tethys/swing/DeploymentExportPanel.java | 1 - src/tethys/swing/NewProjectDialog.java | 1 - src/tethys/swing/TethysConnectionPanel.java | 1 - src/tethys/swing/export/DescriptionCard.java | 4 +- .../swing/export/DescriptionTypePanel.java | 6 +- .../swing/export/ResponsiblePartyPanel.java | 77 +++++ 19 files changed, 302 insertions(+), 446 deletions(-) delete mode 100644 src/metadata/deployment/DeploymentData.java delete mode 100644 src/metadata/deployment/QAData.java create mode 100644 src/metadata/swing/MetaDataDialog.java rename src/tethys/niluswraps/{PDescriptionType.java => WrappedDescriptionType.java} (54%) create mode 100644 src/tethys/swing/export/ResponsiblePartyPanel.java diff --git a/src/PamView/PamGui.java b/src/PamView/PamGui.java index f3e7f967..fed07f88 100644 --- a/src/PamView/PamGui.java +++ b/src/PamView/PamGui.java @@ -74,6 +74,7 @@ import javax.swing.event.MenuListener; import Acquisition.DaqSystemInterface; import annotation.tasks.AnnotationManager; +import metadata.MetaDataContol; import performanceTests.PerformanceDialog; import tipOfTheDay.TipOfTheDayManager; import Array.ArrayManager; @@ -760,6 +761,7 @@ public class PamGui extends PamView implements WindowListener, PamSettings { //for changing "hydrophones" to "microphone" and vice versa if medium changes. menu.addMenuListener(new SettingsMenuListener()); + menu.add(MetaDataContol.getMetaDataControl().createMenu(frame)); menu.addSeparator(); diff --git a/src/metadata/MetaDataContol.java b/src/metadata/MetaDataContol.java index c4243ef9..98093b08 100644 --- a/src/metadata/MetaDataContol.java +++ b/src/metadata/MetaDataContol.java @@ -1,5 +1,7 @@ package metadata; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.io.Serializable; import javax.swing.JFrame; @@ -10,9 +12,7 @@ import PamController.PamControlledUnitSettings; import PamController.PamController; import PamController.PamSettingManager; import PamController.PamSettings; -import PamModel.parametermanager.ParameterSetManager; -import generalDatabase.parameterstore.ParameterDatabaseStore; -import metadata.deployment.DeploymentData; +import metadata.swing.MetaDataDialog; /** * Class to handle Project MetaData. Am making this a PAMControlledUnit, but may never @@ -89,10 +89,25 @@ public class MetaDataContol extends PamControlledUnit implements PamSettings { } // @Override -// public JMenuItem createFileMenu(JFrame parentFrame) { -// return deploymentSetManager.getMenuItem(parentFrame); -//// return null; -// } + public JMenuItem createMenu(JFrame parentFrame) { + JMenuItem menuItem = new JMenuItem("Project information ..."); + menuItem.setToolTipText("General project objectives, region, etc."); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showDialog(parentFrame); + } + }); + return menuItem; + } + + protected void showDialog(JFrame parentFrame) { + PamguardMetaData newData = MetaDataDialog.showDialog(parentFrame, pamguardMetaData); + if (newData != null) { + this.pamguardMetaData = newData; + // send around a notification ? + } + } diff --git a/src/metadata/PamguardMetaData.java b/src/metadata/PamguardMetaData.java index 9d1079b7..45e7e051 100644 --- a/src/metadata/PamguardMetaData.java +++ b/src/metadata/PamguardMetaData.java @@ -3,8 +3,12 @@ package metadata; import java.io.Serializable; import PamUtils.LatLong; +import nilus.ContactInfo; import nilus.Deployment; +import nilus.DescriptionType; import nilus.Helper; +import nilus.MetadataInfo; +import nilus.ResponsibleParty; import tethys.niluswraps.NilusSettingsWrapper; /** @@ -43,12 +47,24 @@ public class PamguardMetaData implements Serializable { try { Helper.createRequiredElements(deployment); } catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) { - // TODO Auto-generated catch block e.printStackTrace(); } deploymentWrapper.setNilusObject(deployment); } - return deploymentWrapper.getNilusObject(Deployment.class); + // check some fields we know we'll need that the Helper may not have managed. + if (deployment.getDescription() == null) { + deployment.setDescription(new DescriptionType()); + } + if (deployment.getMetadataInfo() == null) { + deployment.setMetadataInfo(new MetadataInfo()); + } + if (deployment.getMetadataInfo().getContact() == null) { + deployment.getMetadataInfo().setContact(new ResponsibleParty()); + } + if (deployment.getMetadataInfo().getContact().getContactInfo() == null) { + deployment.getMetadataInfo().getContact().setContactInfo(new ContactInfo()); + } + return deployment; } /** diff --git a/src/metadata/deployment/DeploymentData.java b/src/metadata/deployment/DeploymentData.java deleted file mode 100644 index 440e6549..00000000 --- a/src/metadata/deployment/DeploymentData.java +++ /dev/null @@ -1,327 +0,0 @@ -package metadata.deployment; - -import java.io.Serializable; - -import PamModel.parametermanager.FieldNotFoundException; -import PamModel.parametermanager.ManagedParameters; -import PamModel.parametermanager.PamParameterSet; -import PamModel.parametermanager.PamParameterSet.ParameterSetType; -import PamUtils.LatLong; - -/** - * Class to hold Deployment data in a form consistent with the ANSI PAM - * Standard. This has been keep separate from the Tethys Interface to keep it - * easy to benefit from these data without using Tethys itself. Is also serilaisable - * which is important for storage into PAMGuard settings. - * - * @author dg50 - * - */ -public class DeploymentData implements Serializable, Cloneable, ManagedParameters { - - public static final long serialVersionUID = 1L; - - /** - * String that uniquely identifies this deployment. - */ - private String id; - - /** - * Name of project associated with this deployment. Can be related to a - * geographic region, funding source, etc - */ - private String project; - - /** - * Deployment identifier, a number related to either the Nth deployment - * operation in a series of deployments or the Nth deployment at a specific - * site. This is different from Id which is unique across all deployments - */ - private int deploymentId; - - /** - * Alternative deployment description. - */ - private String deploymentAlias; - - /** - * Name for current location. - */ - private String site; - - /** - * Alternative names for the deployment location - */ - private String siteAliases; - - /** - * Name of deployment cruise. - */ - private String cruise; - -// /** -// * On what platform is the instrument deployed? (e.g. mooring, tag) -// */ -// private String platform = "Unknown"; - - /** - * Name of geographic region. - */ - private String region; - - /** - * time of instrument deployment (different to recording start); - */ - private Long deploymentMillis; - - /** - * time of actual recovery (different to recording end); - */ - private Long recoveryMillis; - - private LatLong recoverLatLong; - - /** - * @return the deploymentMillis - */ - public Long getDeploymentMillis() { - return deploymentMillis; - } - - /** - * @param deploymentMillis the deploymentMillis to set - */ - public void setDeploymentMillis(Long deploymentMillis) { - this.deploymentMillis = deploymentMillis; - } - - /** - * @return the recoveryMillis - */ - public Long getRecoveryMillis() { - return recoveryMillis; - } - - /** - * @param recoveryMillis the recoveryMillis to set - */ - public void setRecoveryMillis(Long recoveryMillis) { - this.recoveryMillis = recoveryMillis; - } - - /** - * Set data from a nilus deployment class - * @param nilusDeployment - */ - public void setData(nilus.Deployment nilusDeployment) { - - } - - public void getData(nilus.Deployment nilusDeployment) { - - } - - public DeploymentData() { - } - - @Override - protected DeploymentData clone() { - try { - return (DeploymentData) super.clone(); - } catch (CloneNotSupportedException e) { - e.printStackTrace(); - return null; - } - } - - @Override - public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); - try { - ps.findParameterData("id").setInfo("Unique Id", null, "String that uniquely identifies this deployment", 128); -// ps.setOrder("id", 0); - ps.findParameterData("project").setInfo("Project Name", null, "Name of project associated with this deployment. Can be related to a geographic region, funding source, etc", 200); - ps.findParameterData("deploymentId").setInfo("Deployment Identifier", null, "Deployment identifier, a number related to either the Nth deployment operation in a series of deployments or the Nth deployment at a specific site. This is different from Id which is unique across all deployments"); - ps.findParameterData("deploymentAlias").setInfo("Alternative deployment description", null, "Alternative deployment description", 20); - ps.findParameterData("site").setInfo("Site name", null, "Name for current location", 40); - ps.findParameterData("siteAliases").setInfo("Alternative site name", null, "Alternative site description", 40); - ps.findParameterData("cruise").setInfo("Deployment cruise", null, "Name of deployment cruise"); - ps.findParameterData("platform").setInfo("Platform type", null, "On what platform is the instrument deployed? (e.g. mooring, tag)", 20); - ps.findParameterData("region").setInfo("Geographic Region", "", "Name of geographic region", 40); - } catch (FieldNotFoundException e) { - e.printStackTrace(); - } - return ps; - } - -// /** -// * @return the id -// */ -// public String getId() { -// return id; -// } -// -// /** -// * @param id the id to set -// */ -// public void setId(String id) { -// this.id = id; -// } -// -// /** -// * @return the project -// */ -// public String getProject() { -// return project; -// } -// -// /** -// * @param project the project to set -// */ -// public void setProject(String project) { -// this.project = project; -// } -// -// /** -// * @return the deploymentId -// */ -// public int getDeploymentId() { -// return deploymentId; -// } -// -// /** -// * @param deploymentId the deploymentId to set -// */ -// public void setDeploymentId(int deploymentId) { -// this.deploymentId = deploymentId; -// } -// -// /** -// * @return the deplomentAlias -// */ -// public String getDeploymentAlias() { -// return deploymentAlias; -// } -// -// /** -// * @param deplomentAlias the deplomentAlias to set -// */ -// public void setDeploymentAlias(String deplomentAlias) { -// this.deploymentAlias = deplomentAlias; -// } -// -// /** -// * @return the site -// */ -// public String getSite() { -// return site; -// } -// -// /** -// * @param site the site to set -// */ -// public void setSite(String site) { -// this.site = site; -// } -// -// /** -// * @return the siteAliases -// */ -// public String getSiteAliases() { -// return siteAliases; -// } -// -// /** -// * @param siteAliases the siteAliases to set -// */ -// public void setSiteAliases(String siteAliases) { -// this.siteAliases = siteAliases; -// } -// -// /** -// * @return the cruise -// */ -// public String getCruise() { -// return cruise; -// } -// -// /** -// * @param cruise the cruise to set -// */ -// public void setCruise(String cruise) { -// 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 region - */ - public String getRegion() { - return region; - } - - /** - * @param region the region to set - */ - public void setRegion(String region) { - this.region = region; - } - - /** - * Set the recovery position latlong for a static recorder. - * Deployment lat long is in the hydrophone array data. - * @param recoverLatLong - */ - public void setRecoveryLatLong(LatLong recoverLatLong) { - this.recoverLatLong = recoverLatLong; - } - - /** - * @return the recoverLatLong (may often be null) - */ - public LatLong getRecoverLatLong() { - return recoverLatLong; - } - -// /** -// * @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/metadata/deployment/QAData.java b/src/metadata/deployment/QAData.java deleted file mode 100644 index 22a7bd66..00000000 --- a/src/metadata/deployment/QAData.java +++ /dev/null @@ -1,41 +0,0 @@ -package metadata.deployment; - -import java.io.Serializable; - -import PamModel.parametermanager.ManagedParameters; -import PamModel.parametermanager.PamParameterSet; -import PamModel.parametermanager.PamParameterSet.ParameterSetType; - -/** - * Largely the content of the Tethys QualityAssurance schema - * @author dg50 - * - */ -public class QAData implements Serializable, Cloneable, ManagedParameters { - - public static final long serialVersionUID = 1L; - - private String objectives; - - private String qaAbstract; - - private String method; - - private String responsibleName; - private String responsibleOrganisation; - private String responsiblePosition; - private String responsiblePhone; - private String responsibleAddress; - private String responsibleEmail; - - public QAData() { - // TODO Auto-generated constructor stub - } - - @Override - public PamParameterSet getParameterSet() { - PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); - return ps; - } - -} diff --git a/src/metadata/swing/MetaDataDialog.java b/src/metadata/swing/MetaDataDialog.java new file mode 100644 index 00000000..73ff66bb --- /dev/null +++ b/src/metadata/swing/MetaDataDialog.java @@ -0,0 +1,131 @@ +package metadata.swing; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Window; + +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamDialog; +import PamView.dialog.PamGridBagContraints; +import PamView.panel.WestAlignedPanel; +import metadata.PamguardMetaData; +import nilus.Deployment; +import tethys.swing.export.DescriptionTypePanel; +import tethys.swing.export.ResponsiblePartyPanel; + +public class MetaDataDialog extends PamDialog { + + private static MetaDataDialog singleInstance; + + private PamguardMetaData pamguardMetaData; + + private DescriptionTypePanel descriptionPanel; + + private JTextField project, site, cruise, region; + + private ResponsiblePartyPanel responsiblePanel; + + private MetaDataDialog(Window parentFrame) { + super(parentFrame, "Project information", false); + + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + JTabbedPane tabbedPane = new JTabbedPane(); + + descriptionPanel = new DescriptionTypePanel(null, false, false, false); + descriptionPanel.getMainPanel().setPreferredSize(new Dimension(400,300)); + + JPanel projectPanel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + projectPanel.add(new JLabel("Project Name ", JLabel.RIGHT), c); + c.gridx++; + projectPanel.add(project = new JTextField(40), c); + c.gridx = 0; + c.gridy++; + projectPanel.add(new JLabel("Region ", JLabel.RIGHT), c); + c.gridx++; + projectPanel.add(region = new JTextField(20), c); + c.gridx = 0; + c.gridy++; + projectPanel.add(new JLabel("Cruise name ", JLabel.RIGHT), c); + c.gridx++; + projectPanel.add(cruise = new JTextField(20), c); + c.gridx = 0; + c.gridy++; + projectPanel.add(new JLabel("Site ", JLabel.RIGHT), c); + c.gridx++; + projectPanel.add(site = new JTextField(20), c); + c.gridx = 0; + c.gridy++; + + responsiblePanel = new ResponsiblePartyPanel(); + JPanel northPanel = new JPanel(); + WestAlignedPanel wp; + northPanel.setLayout(new BoxLayout(northPanel, BoxLayout.Y_AXIS)); + + northPanel.add(wp = new WestAlignedPanel(projectPanel)); + wp.setBorder(new TitledBorder("General project information")); + northPanel.add(wp = new WestAlignedPanel(responsiblePanel.getMainPanel())); + wp.setBorder(new TitledBorder("Contact information")); + +// mainPanel.add(BorderLayout.CENTER, descriptionPanel.getMainPanel()); +// mainPanel.add(BorderLayout.NORTH, northPanel); + mainPanel.add(tabbedPane, BorderLayout.CENTER); + tabbedPane.add(northPanel, "General"); + tabbedPane.add(descriptionPanel.getMainPanel(), "Description"); + + setResizable(true); + + setDialogComponent(mainPanel); + } + + public static PamguardMetaData showDialog(Window frame, PamguardMetaData pamguardMetaData) { + singleInstance = new MetaDataDialog(frame); + singleInstance.setParams(pamguardMetaData); + singleInstance.setVisible(true); + return singleInstance.pamguardMetaData; + } + + private void setParams(PamguardMetaData pamguardMetaData) { + this.pamguardMetaData = pamguardMetaData; + Deployment deployment = pamguardMetaData.getDeployment(); + descriptionPanel.setParams(deployment.getDescription()); + responsiblePanel.setParams(deployment.getMetadataInfo().getContact()); + cruise.setText(deployment.getCruise()); + region.setText(deployment.getRegion()); + site.setText(deployment.getSite()); + project.setText(deployment.getProject()); + } + + @Override + public boolean getParams() { + Deployment deployment = pamguardMetaData.getDeployment(); + boolean ok = descriptionPanel.getParams(deployment.getDescription()); + ok &= responsiblePanel.getParams(deployment.getMetadataInfo().getContact()); + deployment.setCruise(cruise.getText()); + deployment.setRegion(region.getText()); + deployment.setSite(site.getText()); + deployment.setProject(project.getText()); + + return ok; + } + + @Override + public void cancelButtonPressed() { + pamguardMetaData = null; + } + + @Override + public void restoreDefaultSettings() { + + } + +} diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index 213cf2ff..3352db15 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -37,7 +37,6 @@ import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; import metadata.PamguardMetaData; -import metadata.deployment.DeploymentData; import nilus.Deployment; import tethys.TethysState.StateType; import tethys.calibration.CalibrationHandler; @@ -427,15 +426,6 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet // 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 diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index 2d4e8f3a..fbdb668e 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -37,7 +37,6 @@ import dataMap.OfflineDataMapPoint; import generalDatabase.DBControlUnit; import metadata.MetaDataContol; import metadata.PamguardMetaData; -import metadata.deployment.DeploymentData; import nilus.Audio; import nilus.ChannelInfo; import nilus.ChannelInfo.DutyCycle; diff --git a/src/tethys/niluswraps/NilusUnpacker.java b/src/tethys/niluswraps/NilusUnpacker.java index 2b5b4a46..5bf8464f 100644 --- a/src/tethys/niluswraps/NilusUnpacker.java +++ b/src/tethys/niluswraps/NilusUnpacker.java @@ -39,6 +39,20 @@ import tethys.TethysTimeFuncs; */ public class NilusUnpacker { + + private boolean verbose = false; + + /** + * @param verbose + */ + public NilusUnpacker(boolean verbose) { + this.verbose = verbose; + } + + public NilusUnpacker() { + super(); + } + public Object unpackDocument(Document doc, Class nilusClass) throws SecurityException { Object nilusObject = null; @@ -155,7 +169,7 @@ public class NilusUnpacker { * it's possible that they are simply not there. */ if (child == null) { - if (required) { + if (required & verbose) { System.out.printf("Field %s in class %s is required but cannot be found\n", fieldName, nilusClass.getName()); } continue; @@ -171,7 +185,7 @@ public class NilusUnpacker { // find a setter for it. Method setter = findSetter(nilusClass, fieldName); // System.out.printf("Field %s with element %s and setter %s\n", fieldName, childName, setter); - if (setter == null) { + if (setter == null & verbose) { System.out.printf("No setter available for field %s and element %s\n", fieldName, elementName); continue; // eventually do something more intelligent here. } diff --git a/src/tethys/niluswraps/PDescriptionType.java b/src/tethys/niluswraps/WrappedDescriptionType.java similarity index 54% rename from src/tethys/niluswraps/PDescriptionType.java rename to src/tethys/niluswraps/WrappedDescriptionType.java index 889d0684..bc1a5ac4 100644 --- a/src/tethys/niluswraps/PDescriptionType.java +++ b/src/tethys/niluswraps/WrappedDescriptionType.java @@ -8,19 +8,14 @@ import nilus.DescriptionType; * Because we want to save DescriptionType objects in serialised * psfx files and because Nilus description types are not serialised * these have to be wrapped in a total bodge way with reasonably convenient - * constructors and getters for converting back and forth from the nilus object. + * constructors and getters for converting back and forth from the nilus object. + * this is now slightly more rationalised in NilusSettingsWrapper. * @author dg50 * */ -public class PDescriptionType implements Serializable { +public class WrappedDescriptionType extends NilusSettingsWrapper implements Serializable { private static final long serialVersionUID = 1L; - - protected String objectives; - - protected String _abstract; - - protected String method; /** * Constructor from a set of strings @@ -28,74 +23,70 @@ public class PDescriptionType implements Serializable { * @param _abstract * @param method */ - public PDescriptionType(String objectives, String _abstract, String method) { + public WrappedDescriptionType(String objectives, String _abstract, String method) { super(); - this.objectives = objectives; - this._abstract = _abstract; - this.method = method; + DescriptionType description = getDescription(); + description.setObjectives(objectives); + description.setAbstract(_abstract); + description.setMethod(method); + } + + public DescriptionType getDescription() { + return getNilusObject(DescriptionType.class); + } + + public void setDescription(DescriptionType description) { + setNilusObject(description); } /** * Construct from a nilus object * @param descriptionType */ - public PDescriptionType(DescriptionType descriptionType) { - this.objectives = descriptionType.getObjectives(); - this._abstract = descriptionType.getAbstract(); - this.method = descriptionType.getMethod(); + public WrappedDescriptionType(DescriptionType descriptionType) { + this.setNilusObject(descriptionType); } - public PDescriptionType() { + public WrappedDescriptionType() { } public String getObjectives() { - return objectives; + return getDescription().getObjectives(); } /** * @return the _abstract */ public String getAbstract() { - return _abstract; + return getDescription().getAbstract(); } /** * @param _abstract the _abstract to set */ public void setAbstract(String _abstract) { - this._abstract = _abstract; + getDescription().setAbstract(_abstract); } /** * @return the method */ public String getMethod() { - return method; + return getDescription().getMethod(); } /** * @param method the method to set */ public void setMethod(String method) { - this.method = method; + getDescription().setMethod(method); } /** * @param objectives the objectives to set */ public void setObjectives(String objectives) { - this.objectives = objectives; + getDescription().setObjectives(objectives);; } - /** - * convert into a nilus object for output. - * @return - */ - public DescriptionType getDescription() { - DescriptionType descriptionType = new DescriptionType(); - descriptionType.setAbstract(_abstract); - descriptionType.setObjectives(objectives); - descriptionType.setMethod(method); - return descriptionType; - } } diff --git a/src/tethys/output/StreamExportParams.java b/src/tethys/output/StreamExportParams.java index 9c9de08d..aabbff2d 100644 --- a/src/tethys/output/StreamExportParams.java +++ b/src/tethys/output/StreamExportParams.java @@ -8,7 +8,7 @@ import PamguardMVC.PamDataBlock; import nilus.DescriptionType; import nilus.GranularityEnumType; import tethys.TethysControl; -import tethys.niluswraps.PDescriptionType; +import tethys.niluswraps.WrappedDescriptionType; import tethys.pamdata.TethysDataProvider; /** @@ -65,11 +65,11 @@ public class StreamExportParams implements Serializable { /* * Can't have this here since it isn't serializable. */ - public PDescriptionType detectionDescription; + public WrappedDescriptionType detectionDescription; - public PDescriptionType getDetectionDescription() { + public WrappedDescriptionType getDetectionDescription() { if (detectionDescription == null) { - detectionDescription = new PDescriptionType(); + detectionDescription = new WrappedDescriptionType(); } return detectionDescription; } @@ -89,7 +89,7 @@ public class StreamExportParams implements Serializable { private void autoFill(TethysControl tethysControl, PamDataBlock dataBlock) { // there should always be a data provider or we'd never have got this far. TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl); - PDescriptionType desc = getDetectionDescription(); + WrappedDescriptionType desc = getDetectionDescription(); desc.setMethod(dataProvider.getDetectionsMethod()); } diff --git a/src/tethys/output/TethysExportParams.java b/src/tethys/output/TethysExportParams.java index 89d9a43f..a1e4e040 100644 --- a/src/tethys/output/TethysExportParams.java +++ b/src/tethys/output/TethysExportParams.java @@ -4,7 +4,6 @@ import java.io.Serializable; import java.util.HashMap; import PamguardMVC.PamDataBlock; import generalDatabase.DBControlUnit; -import metadata.deployment.DeploymentData; /** @@ -30,7 +29,6 @@ public class TethysExportParams implements Serializable, Cloneable{ private HashMap streamParamsMap = new HashMap(); - private DeploymentData deploymentData; /** * PAMGuard HAS to have a dataset name to link to data in Tethys, or it all gets @@ -114,11 +112,6 @@ 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 0afe95ff..7e042859 100644 --- a/src/tethys/output/TethysExporter.java +++ b/src/tethys/output/TethysExporter.java @@ -12,7 +12,6 @@ import PamController.PamController; import PamUtils.PamCalendar; import PamguardMVC.PamDataBlock; import metadata.MetaDataContol; -import metadata.deployment.DeploymentData; import nilus.Deployment; import tethys.TethysControl; import tethys.dbxml.DBXMLConnect; diff --git a/src/tethys/swing/DeploymentExportPanel.java b/src/tethys/swing/DeploymentExportPanel.java index 0e88ad54..196a4045 100644 --- a/src/tethys/swing/DeploymentExportPanel.java +++ b/src/tethys/swing/DeploymentExportPanel.java @@ -27,7 +27,6 @@ import PamView.panel.PamAlignmentPanel; import PamView.panel.WestAlignedPanel; import binaryFileStorage.BinaryStore; import generalDatabase.DBControlUnit; -import metadata.deployment.DeploymentData; import nilus.Deployment; import nilus.Deployment.Data; import tethys.TethysControl; diff --git a/src/tethys/swing/NewProjectDialog.java b/src/tethys/swing/NewProjectDialog.java index befb0489..68dd272b 100644 --- a/src/tethys/swing/NewProjectDialog.java +++ b/src/tethys/swing/NewProjectDialog.java @@ -11,7 +11,6 @@ import javax.swing.border.TitledBorder; import PamView.dialog.PamGridBagContraints; import metadata.PamguardMetaData; -import metadata.deployment.DeploymentData; import nilus.Deployment; import tethys.TethysControl; diff --git a/src/tethys/swing/TethysConnectionPanel.java b/src/tethys/swing/TethysConnectionPanel.java index 3f6817ed..f2491a13 100644 --- a/src/tethys/swing/TethysConnectionPanel.java +++ b/src/tethys/swing/TethysConnectionPanel.java @@ -24,7 +24,6 @@ import PamView.panel.PamPanel; import PamView.panel.WestAlignedPanel; import metadata.MetaDataContol; import metadata.PamguardMetaData; -import metadata.deployment.DeploymentData; import nilus.Deployment; import pamViewFX.fxNodes.PamComboBox; import tethys.TethysControl; diff --git a/src/tethys/swing/export/DescriptionCard.java b/src/tethys/swing/export/DescriptionCard.java index f6d0c1ef..cc25b2cf 100644 --- a/src/tethys/swing/export/DescriptionCard.java +++ b/src/tethys/swing/export/DescriptionCard.java @@ -19,12 +19,12 @@ public class DescriptionCard extends ExportWizardCard { @Override public boolean getParams(StreamExportParams streamExportParams) { - return descriptionPanel.getParams(streamExportParams.getDetectionDescription()); + return descriptionPanel.getParams(streamExportParams.getDetectionDescription().getDescription()); } @Override public void setParams(StreamExportParams streamExportParams) { - descriptionPanel.setParams(streamExportParams.getDetectionDescription()); + descriptionPanel.setParams(streamExportParams.getDetectionDescription().getDescription()); } } diff --git a/src/tethys/swing/export/DescriptionTypePanel.java b/src/tethys/swing/export/DescriptionTypePanel.java index c17549a2..64178883 100644 --- a/src/tethys/swing/export/DescriptionTypePanel.java +++ b/src/tethys/swing/export/DescriptionTypePanel.java @@ -18,7 +18,7 @@ import javax.swing.border.TitledBorder; import PamView.PamGui; import PamView.dialog.PamDialog; import nilus.DescriptionType; -import tethys.niluswraps.PDescriptionType; +import tethys.niluswraps.WrappedDescriptionType; /** * Panel containing the three test entry fields for nilus.DescriptionType @@ -89,7 +89,7 @@ public class DescriptionTypePanel { return mainPanel; } - public void setParams(PDescriptionType description) { + public void setParams(DescriptionType description) { if (description == null) { tObjectives.setText(null); tAbstract.setText(null); @@ -102,7 +102,7 @@ public class DescriptionTypePanel { } } - public boolean getParams(PDescriptionType description) { + public boolean getParams(DescriptionType description) { Window f = PamGui.findComponentWindow(mainPanel); if (checkField(requireObjective, tObjectives) == false) { return PamDialog.showWarning(f, "Objectives", "The objectives field must be completed"); diff --git a/src/tethys/swing/export/ResponsiblePartyPanel.java b/src/tethys/swing/export/ResponsiblePartyPanel.java new file mode 100644 index 00000000..64d49861 --- /dev/null +++ b/src/tethys/swing/export/ResponsiblePartyPanel.java @@ -0,0 +1,77 @@ +package tethys.swing.export; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamGridBagContraints; +import nilus.ResponsibleParty; + +/** + * Simple Swing panel for responsibleparty fields. + * @author dg50 + * + */ +public class ResponsiblePartyPanel { + + private JTextField name, organisation, position, email; + + private JPanel mainPanel; + + /** + * + */ + public ResponsiblePartyPanel() { + super(); + mainPanel = new JPanel(new GridBagLayout()); +// mainPanel.setBorder(new TitledBorder("Responsible party")); + GridBagConstraints c = new PamGridBagContraints(); + mainPanel.add(new JLabel("Name ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(name = new JTextField(40), c); + c.gridx = 0; + c.gridy++; + mainPanel.add(new JLabel("Organisation ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(organisation = new JTextField(30), c); + c.gridx = 0; + c.gridy++; + mainPanel.add(new JLabel("Position ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(position = new JTextField(30), c); + c.gridx = 0; + c.gridy++; + mainPanel.add(new JLabel("Email ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(email = new JTextField(30), c); + c.gridx = 0; + c.gridy++; + + } + + public JPanel getMainPanel() { + return mainPanel; + } + + public void setParams(ResponsibleParty responsibleParty) { + if (responsibleParty == null) { + return; + } + name.setText(responsibleParty.getIndividualName()); + organisation.setText(responsibleParty.getOrganizationName()); + position.setText(responsibleParty.getPositionName()); + email.setText(responsibleParty.getContactInfo().getContactInstructions()); + } + + public boolean getParams(ResponsibleParty responsibleParty) { + responsibleParty.setIndividualName(name.getText()); + responsibleParty.setOrganizationName(organisation.getText()); + responsibleParty.setPositionName(position.getText()); + responsibleParty.getContactInfo().setContactInstructions(email.getText()); + return true; + } +} From 860d1bec179cac7b57ef62adcaec67f69448a278 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 7 Dec 2023 20:08:17 +0000 Subject: [PATCH 62/65] Update Calibration export Stop repeating hydrophones and add two addresses to Calibrations docs --- .../calibration/CalibrationHandler.java | 6 + .../swing/CalibrationProcessCard.java | 7 +- .../swing/CalibrationsContactCard.java | 143 ++++++++++-------- .../niluswraps/NilusSettingsWrapper.java | 22 ++- .../swing/export/ResponsiblePartyPanel.java | 27 +++- 5 files changed, 134 insertions(+), 71 deletions(-) diff --git a/src/tethys/calibration/CalibrationHandler.java b/src/tethys/calibration/CalibrationHandler.java index e62f89e4..8b88dc36 100644 --- a/src/tethys/calibration/CalibrationHandler.java +++ b/src/tethys/calibration/CalibrationHandler.java @@ -45,6 +45,7 @@ import tethys.TethysTimeFuncs; import tethys.calibration.swing.CalibrationsExportWizard; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.TethysException; +import tethys.niluswraps.NilusSettingsWrapper; import tethys.niluswraps.NilusUnpacker; import tethys.pamdata.AutoTethysProvider; @@ -176,6 +177,9 @@ public class CalibrationHandler implements TethysStateObserver { return 0; } + NilusSettingsWrapper wrappedSample = new NilusSettingsWrapper(); + wrappedSample.setNilusObject(sampleCal); + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); int nPhone = array.getHydrophoneCount(); DBXMLConnect dbxml = tethysControl.getDbxmlConnect(); @@ -184,6 +188,8 @@ public class CalibrationHandler implements TethysStateObserver { boolean exists; for (int i = 0; i < nPhone; i++) { // String docName = getHydrophoneId(i); + NilusSettingsWrapper clonedWrap = wrappedSample.clone(); + sampleCal = clonedWrap.getNilusObject(Calibration.class); Calibration calDoc = createCalibrationDocument(i); if (sampleCal != null) { calDoc.setMetadataInfo(sampleCal.getMetadataInfo()); diff --git a/src/tethys/calibration/swing/CalibrationProcessCard.java b/src/tethys/calibration/swing/CalibrationProcessCard.java index a2fcb334..30e37ba7 100644 --- a/src/tethys/calibration/swing/CalibrationProcessCard.java +++ b/src/tethys/calibration/swing/CalibrationProcessCard.java @@ -16,6 +16,7 @@ import javax.swing.JTextField; import javax.swing.border.TitledBorder; import PamView.dialog.PamGridBagContraints; +import PamView.panel.WestAlignedPanel; import PamView.wizard.PamWizard; import nilus.AlgorithmType; import nilus.AlgorithmType.Parameters; @@ -43,8 +44,10 @@ public class CalibrationProcessCard extends CalibrationsCard { super(pamWizard, "Calibration Process"); this.setLayout(new BorderLayout()); processPanel = new JPanel(new GridBagLayout()); - processPanel.setBorder(new TitledBorder("Calibration Process")); - this.add(BorderLayout.NORTH, processPanel); + WestAlignedPanel wp; + this.add(BorderLayout.NORTH, wp = new WestAlignedPanel(processPanel)); + wp.setBorder(new TitledBorder("Calibration Process")); + GridBagConstraints c = new PamGridBagContraints(); calMethod = new JComboBox(); diff --git a/src/tethys/calibration/swing/CalibrationsContactCard.java b/src/tethys/calibration/swing/CalibrationsContactCard.java index 95d5b577..3adc9f33 100644 --- a/src/tethys/calibration/swing/CalibrationsContactCard.java +++ b/src/tethys/calibration/swing/CalibrationsContactCard.java @@ -7,6 +7,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; +import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; @@ -19,6 +20,7 @@ import org.jdesktop.swingx.JXDatePicker; import PamView.dialog.PamDialog; import PamView.dialog.PamGridBagContraints; +import PamView.panel.WestAlignedPanel; import PamView.wizard.PamWizard; import nilus.Calibration; import nilus.ContactInfo; @@ -26,20 +28,25 @@ import nilus.MetadataInfo; import nilus.ResponsibleParty; import tethys.TethysTimeFuncs; import tethys.calibration.CalibrationHandler; +import tethys.swing.export.ResponsiblePartyPanel; public class CalibrationsContactCard extends CalibrationsCard { private JXDatePicker datePicker; - private JTextField individual, organisation, position, email; + private ResponsiblePartyPanel calibrator, dataManager; private JComboBox updateInterval; + + private MetadataInfo metaData; + + private JButton copyDown, copyUp; public CalibrationsContactCard(PamWizard pamWizard) { super(pamWizard, "Contact Details"); // TODO Auto-generated constructor stub // setBorder(new TitledBorder("Contact")); - setLayout(new BorderLayout()); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); updateInterval = new JComboBox<>(); String[] vals = CalibrationHandler.updateOptions; @@ -48,8 +55,8 @@ public class CalibrationsContactCard extends CalibrationsCard { } JPanel datePanel = new JPanel(new GridBagLayout()); - datePanel.setBorder(new TitledBorder("Calibration date")); - add(BorderLayout.NORTH, datePanel); + JPanel lp = new WestAlignedPanel(datePanel); + lp.setBorder(new TitledBorder("Calibration date")); GridBagConstraints c = new PamGridBagContraints(); datePanel.add(new JLabel("Calibration date: ", JLabel.RIGHT), c); datePicker = new JXDatePicker(); @@ -61,67 +68,66 @@ public class CalibrationsContactCard extends CalibrationsCard { c.gridx++; datePanel.add(updateInterval, c); + calibrator = new ResponsiblePartyPanel("Technical Person"); + dataManager = new ResponsiblePartyPanel("Data Manager"); - JPanel contactPanel = new JPanel(new GridBagLayout()); - contactPanel.setBorder(new TitledBorder("Contact")); - this.add(BorderLayout.CENTER, contactPanel); + JPanel copyPanel = new JPanel(new GridBagLayout()); c = new PamGridBagContraints(); - contactPanel.add(new JLabel("Individual Name "), c); + copyPanel.add(copyDown = new JButton("Copy down"),c); c.gridx++; - contactPanel.add(individual = new JTextField(15), c); - c.gridx = 0; - c.gridy++; - contactPanel.add(new JLabel("Organisation "), c); - c.gridx++; - contactPanel.add(organisation = new JTextField(15), c); - c.gridx = 0; - c.gridy++; - contactPanel.add(new JLabel("Position "), c); - c.gridx++; - contactPanel.add(position = new JTextField(15), c); - c.gridx = 0; - c.gridy++; - contactPanel.add(new JLabel("Email "), c); - c.gridx++; - contactPanel.add(email = new JTextField(15), c); - c.gridx = 0; - c.gridy++; + copyPanel.add(copyUp = new JButton("Copy up"), c); + + add(lp); + add(calibrator.getMainPanel()); + add(copyPanel); + add(dataManager.getMainPanel()); + copyDown.setToolTipText("Copy technical person to data manager"); + copyUp.setToolTipText("Copy data manager to technical person"); + copyDown.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + copyRPDown(); + } + }); + copyUp.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + copyRPUp(); + } + + }); + } + + protected void copyRPDown() { + copyRPData(calibrator, dataManager); + } + private void copyRPUp() { + copyRPData(dataManager, calibrator); + } + + private void copyRPData(ResponsiblePartyPanel rFrom, ResponsiblePartyPanel rTo) { + ResponsibleParty rp = checkRPChildren(null); + rFrom.getParams(rp); + rTo.setParams(rp); } @Override public boolean getParams(Calibration cardParams) { - if (cardParams == null) { - return false; - } - MetadataInfo metaInf = cardParams.getMetadataInfo(); - if (metaInf == null) { - metaInf = new MetadataInfo(); - cardParams.setMetadataInfo(metaInf); - } - ResponsibleParty contact = metaInf.getContact(); - if (contact == null) { - contact = new ResponsibleParty(); - metaInf.setContact(contact); - } - ContactInfo contactInfo = contact.getContactInfo(); - if (contactInfo == null) { - contactInfo = new ContactInfo(); - contact.setContactInfo(contactInfo); - } + ResponsibleParty rp = checkRPChildren(cardParams.getResponsibleParty()); + cardParams.setResponsibleParty(rp); + calibrator.getParams(rp); - // so far as I'm aware, the meta info contains the time we create this record - // and the other timestamp is the data the calibration was donw. - metaInf.setDate(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); - metaInf.setUpdateFrequency((String) updateInterval.getSelectedItem()); + metaData = cardParams.getMetadataInfo(); + if (metaData == null) { + metaData = new MetadataInfo(); + cardParams.setMetadataInfo(metaData); + } + metaData.setContact(checkRPChildren(metaData.getContact())); + dataManager.getParams(metaData.getContact()); - contact.setIndividualName(individual.getText()); - contact.setOrganizationName(organisation.getText()); - contact.setPositionName(position.getText()); - contactInfo.setContactInstructions(email.getText()); - - // and set this both as the RepsonsiblePArty and in the metadata. - cardParams.setResponsibleParty(contact); + metaData.setUpdateFrequency((String) updateInterval.getSelectedItem()); + metaData.setDate(TethysTimeFuncs.xmlGregCalFromMillis(System.currentTimeMillis())); Date date = datePicker.getDate(); if (date == null) { @@ -133,6 +139,19 @@ public class CalibrationsContactCard extends CalibrationsCard { return true; } + private ResponsibleParty checkRPChildren(ResponsibleParty rp) { + if (rp == null) { + rp = new ResponsibleParty(); + } + if (rp.getContactInfo() == null) { + rp.setContactInfo(new ContactInfo()); + } + if (rp.getContactInfo().getAddress() == null) { +// rp.getContactInfo().setAddress(new Address()); + } + return rp; + } + private ResponsibleParty findResponsibleParty(Calibration cal) { if (cal == null) { return null; @@ -151,19 +170,17 @@ public class CalibrationsContactCard extends CalibrationsCard { @Override public void setParams(Calibration cardParams) { // fill in as much as possible from the existing Calibration - ResponsibleParty resp = findResponsibleParty(cardParams); + ResponsibleParty resp = cardParams.getResponsibleParty(); if (resp != null) { - individual.setText(resp.getIndividualName()); - organisation.setText(resp.getOrganizationName()); - position.setText(resp.getPositionName()); - ContactInfo cInf = resp.getContactInfo(); - if (cInf != null) { - email.setText(cInf.getContactInstructions()); - } + calibrator.setParams(resp); } MetadataInfo metaInf = cardParams.getMetadataInfo(); if (metaInf != null) { + resp = metaInf.getContact(); + if (resp != null) { + dataManager.getParams(resp); + } String uf = metaInf.getUpdateFrequency(); if (uf != null) { updateInterval.setSelectedItem(uf); diff --git a/src/tethys/niluswraps/NilusSettingsWrapper.java b/src/tethys/niluswraps/NilusSettingsWrapper.java index 8364c1d6..72616537 100644 --- a/src/tethys/niluswraps/NilusSettingsWrapper.java +++ b/src/tethys/niluswraps/NilusSettingsWrapper.java @@ -102,7 +102,8 @@ public class NilusSettingsWrapper implements Serializable, Clo } /** - * Repack the object. May want to do this before serializing. + * Repack the object.i.e. write the xml text string. + * May want to do this before serializing or cloning. * @return */ public boolean repackNilusObject() { @@ -149,6 +150,25 @@ public class NilusSettingsWrapper implements Serializable, Clo public void reSerialise() { packNilusObject(nilusObject); } + + @Override + public NilusSettingsWrapper clone() { + /** + * Clone the underlying data, then force it to re-read the string into a new object. + */ + this.repackNilusObject(); + NilusSettingsWrapper clone = null; + try { + clone = (NilusSettingsWrapper) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + if (nilusObject != null) { + clone.nilusObject = clone.unpackNilusObject(nilusObject.getClass()); + } + return clone; + } // private Class getNilusClass() throws NoSuchMethodException, SecurityException { diff --git a/src/tethys/swing/export/ResponsiblePartyPanel.java b/src/tethys/swing/export/ResponsiblePartyPanel.java index 64d49861..93b26816 100644 --- a/src/tethys/swing/export/ResponsiblePartyPanel.java +++ b/src/tethys/swing/export/ResponsiblePartyPanel.java @@ -9,6 +9,7 @@ import javax.swing.JTextField; import javax.swing.border.TitledBorder; import PamView.dialog.PamGridBagContraints; +import nilus.ContactInfo.Address; import nilus.ResponsibleParty; /** @@ -21,14 +22,18 @@ public class ResponsiblePartyPanel { private JTextField name, organisation, position, email; private JPanel mainPanel; - + + public ResponsiblePartyPanel() { + this(null); + } /** * */ - public ResponsiblePartyPanel() { + public ResponsiblePartyPanel(String borderTitle) { super(); mainPanel = new JPanel(new GridBagLayout()); -// mainPanel.setBorder(new TitledBorder("Responsible party")); + if (borderTitle != null) + mainPanel.setBorder(new TitledBorder(borderTitle)); GridBagConstraints c = new PamGridBagContraints(); mainPanel.add(new JLabel("Name ", JLabel.RIGHT), c); c.gridx++; @@ -64,14 +69,26 @@ public class ResponsiblePartyPanel { name.setText(responsibleParty.getIndividualName()); organisation.setText(responsibleParty.getOrganizationName()); position.setText(responsibleParty.getPositionName()); - email.setText(responsibleParty.getContactInfo().getContactInstructions()); + + Address addr = responsibleParty.getContactInfo().getAddress(); + if (addr != null) { + email.setText(addr.getElectronicMailAddress()); + } + } + public boolean getParams(ResponsibleParty responsibleParty) { responsibleParty.setIndividualName(name.getText()); responsibleParty.setOrganizationName(organisation.getText()); responsibleParty.setPositionName(position.getText()); - responsibleParty.getContactInfo().setContactInstructions(email.getText()); + + Address addr = responsibleParty.getContactInfo().getAddress(); + if (addr == null) { + addr = new Address(); + responsibleParty.getContactInfo().setAddress(addr); + } + addr.setElectronicMailAddress(email.getText()); return true; } } From 03483ded18b3834cfa24a54e9bca6954e4e1a61e Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Wed, 13 Dec 2023 17:31:35 +0000 Subject: [PATCH 63/65] Deployment export Improved deployment export interface and options. --- .classpath | 2 +- src/Acquisition/AcquisitionControl.java | 10 + src/Acquisition/AcquisitionDialog.java | 1 + src/GPS/GpsDataUnit.java | 11 +- src/Map/MapRectProjector.java | 45 ++++ src/PamController/OfflineDataStore.java | 7 + src/PamView/GeneralProjector.java | 9 +- src/PamguardMVC/PamDataUnit.java | 9 +- src/binaryFileStorage/BinaryStore.java | 4 + src/dataMap/filemaps/OfflineFileServer.java | 6 + src/decimator/DecimatorControl.java | 9 + src/difar/beamforming/BeamformControl.java | 10 + src/fileOfflineData/OfflineFileControl.java | 5 + src/generalDatabase/DBControlUnit.java | 5 + src/metadata/swing/MetaDataDialog.java | 61 +++-- .../calibration/CalibrationHandler.java | 8 +- .../deployment/DeploymentExportOpts.java | 40 ++++ src/tethys/deployment/DeploymentHandler.java | 213 +++++++++++++++-- src/tethys/deployment/RecordingPeriod.java | 23 ++ src/tethys/deployment/TrackInformation.java | 85 +++++++ .../deployment/swing/DeploymentDataCard.java | 103 ++++++++ .../deployment/swing/DeploymentInfoCard.java | 47 ++++ .../deployment/swing/DeploymentTrackCard.java | 183 +++++++++++++++ .../deployment/swing/DeploymentWizard.java | 100 ++++++++ .../swing/ProjectInformationPanel.java | 219 ++++++++++++++++++ .../deployment/swing/RecordingGapDialog.java | 85 +++++++ src/tethys/niluswraps/NilusUnpacker.java | 6 +- src/tethys/swing/DeploymentExportPanel.java | 1 - src/tethys/swing/DeploymentTableObserver.java | 1 + src/tethys/swing/DeploymentsPanel.java | 78 ++++++- src/tethys/swing/NewProjectDialog.java | 21 +- .../swing/PAMGuardDeploymentsTable.java | 162 ++++++++----- src/tethys/swing/SelectProjectDialog.java | 81 +++++++ src/tethys/swing/TethysConnectionPanel.java | 9 +- src/tethys/swing/TethysMainPanel.java | 11 +- src/tethys/swing/XMLStringView.java | 3 +- src/tethys/swing/export/DescriptionCard.java | 21 +- .../swing/export/DetectionsExportWizard.java | 2 +- .../swing/export/ResponsiblePartyCard.java | 31 +++ 39 files changed, 1584 insertions(+), 143 deletions(-) create mode 100644 src/tethys/deployment/DeploymentExportOpts.java create mode 100644 src/tethys/deployment/TrackInformation.java create mode 100644 src/tethys/deployment/swing/DeploymentDataCard.java create mode 100644 src/tethys/deployment/swing/DeploymentInfoCard.java create mode 100644 src/tethys/deployment/swing/DeploymentTrackCard.java create mode 100644 src/tethys/deployment/swing/DeploymentWizard.java create mode 100644 src/tethys/deployment/swing/ProjectInformationPanel.java create mode 100644 src/tethys/deployment/swing/RecordingGapDialog.java create mode 100644 src/tethys/swing/SelectProjectDialog.java create mode 100644 src/tethys/swing/export/ResponsiblePartyCard.java diff --git a/.classpath b/.classpath index 8f3f9191..e121c1d6 100644 --- a/.classpath +++ b/.classpath @@ -6,7 +6,7 @@ - + diff --git a/src/Acquisition/AcquisitionControl.java b/src/Acquisition/AcquisitionControl.java index 406796c8..354cefb5 100644 --- a/src/Acquisition/AcquisitionControl.java +++ b/src/Acquisition/AcquisitionControl.java @@ -283,6 +283,16 @@ public class AcquisitionControl extends RawInputControlledUnit implements PamSet return daqControllers; } + @Override + public String getDataLocation() { + if (offlineFileServer == null) { + return null; + } + else { + return offlineFileServer.getDataLocation(); + } + } + public AcquisitionProcess getDaqProcess() { return acquisitionProcess; } diff --git a/src/Acquisition/AcquisitionDialog.java b/src/Acquisition/AcquisitionDialog.java index 50e0d977..cb739bb2 100644 --- a/src/Acquisition/AcquisitionDialog.java +++ b/src/Acquisition/AcquisitionDialog.java @@ -151,6 +151,7 @@ public class AcquisitionDialog extends PamDialog { acquisitionParameters = oldParams.clone(); acquisitionControl = daqControl; +// singleInstance = null; if (singleInstance == null || singleInstance.getOwner() != parentFrame) { singleInstance = new AcquisitionDialog(parentFrame); diff --git a/src/GPS/GpsDataUnit.java b/src/GPS/GpsDataUnit.java index 914920b5..8d6433ca 100644 --- a/src/GPS/GpsDataUnit.java +++ b/src/GPS/GpsDataUnit.java @@ -1,5 +1,6 @@ package GPS; +import PamUtils.PamCalendar; import PamguardMVC.PamDataUnit; public class GpsDataUnit extends PamDataUnit { @@ -52,12 +53,18 @@ public class GpsDataUnit extends PamDataUnit { */ @Override public String getSummaryString() { - // TODO Auto-generated method stub - String str = super.getSummaryString(); +// String str = super.getSummaryString(); + String str = String.format("%s
UID:%d, Database: %d
%s
", + "GPS Data", getUID(), getDatabaseIndex(), PamCalendar.formatDBDateTime(getTimeMilliseconds(), true)); if (gpsData != null) { str += gpsData.summaryString(); } return str; } + @Override + public double[] getFrequency() { + return null; + } + } diff --git a/src/Map/MapRectProjector.java b/src/Map/MapRectProjector.java index dd402886..0b7e45a3 100644 --- a/src/Map/MapRectProjector.java +++ b/src/Map/MapRectProjector.java @@ -20,9 +20,15 @@ */ package Map; +import java.awt.Point; import java.awt.event.MouseMotionAdapter; import java.awt.geom.AffineTransform; +import java.util.ListIterator; +import GPS.GPSControl; +import GPS.GPSDataBlock; +import GPS.GpsDataUnit; +import PamController.PamController; import PamUtils.Coordinate3d; import PamUtils.LatLong; import PamUtils.PamCoordinate; @@ -385,6 +391,45 @@ public class MapRectProjector extends MapProjector { return xTrans; } + @Override + public String getHoverText(Point mousePoint, int ploNumberMatch) { + String text = super.getHoverText(mousePoint, ploNumberMatch); + if (text == null) { + return findGpsTrackText(mousePoint, ploNumberMatch); + } + else { + return text; + } + } + + private String findGpsTrackText(Point mousePoint, int ploNumberMatch) { + GPSControl gpsControl = GPSControl.getGpsControl(); + if (gpsControl == null) { + return null; + } + LatLong currentPos = getDataPosition(new Coordinate3d(mousePoint.x, mousePoint.y)); + GPSDataBlock gpsDataBlock = gpsControl.getGpsDataBlock(); + double dist = Double.MAX_VALUE; + GpsDataUnit closest = null; + ListIterator it = gpsDataBlock.getListIterator(0); + while (it.hasNext()) { + GpsDataUnit gpsUnit = it.next(); + double r = gpsUnit.getGpsData().distanceToMetres(currentPos); + if (r < dist) { + dist = r; + closest = gpsUnit; + } + } + if (closest == null) { + return null; + } + double rPix = dist*this.pixelsPerMetre; + if (rPix > 20) { + return null; + } + return closest.getSummaryString(); + } + } diff --git a/src/PamController/OfflineDataStore.java b/src/PamController/OfflineDataStore.java index 52e64150..20a34041 100644 --- a/src/PamController/OfflineDataStore.java +++ b/src/PamController/OfflineDataStore.java @@ -33,6 +33,13 @@ public interface OfflineDataStore { */ public String getDataSourceName(); + /** + * Get the data location. This may be a specific file, or might be a folder + * if data are in many files, a URI, etc. + * @return store locations + */ + public String getDataLocation(); + /** * Load data for a given datablock between two time limits. * @param dataBlock datablock owner of the data diff --git a/src/PamView/GeneralProjector.java b/src/PamView/GeneralProjector.java index b9aa21e4..5329d10c 100644 --- a/src/PamView/GeneralProjector.java +++ b/src/PamView/GeneralProjector.java @@ -265,6 +265,11 @@ public abstract class GeneralProjector { JComponent toolTipComponent; + /** + * Gets an adapter that can provide tooltips automatically based on plotted data units. + * @param component + * @return + */ public MouseHoverAdapter getMouseHoverAdapter(JComponent component) { ToolTipManager tt = ToolTipManager.sharedInstance(); tt.registerComponent(component); @@ -384,7 +389,9 @@ public abstract class GeneralProjector { } String hintText = dataBlock.getHoverText(this, hoveredDataUnit, hoverData.get(unitIndex).getAmbiguity()); - if (hintText == null) return null; + if (hintText == null) { + return null; + } // System.out.println(hintText); return hintText; } diff --git a/src/PamguardMVC/PamDataUnit.java b/src/PamguardMVC/PamDataUnit.java index 3295381f..8630a7d1 100644 --- a/src/PamguardMVC/PamDataUnit.java +++ b/src/PamguardMVC/PamDataUnit.java @@ -974,8 +974,13 @@ abstract public class PamDataUnit // add frequency and amplitude information - str += "Frequency: " + FrequencyFormat.formatFrequencyRange(this.getFrequency(), true) + "
"; - str += String.format("Amplitude: %3.1fdB
", getAmplitudeDB()); + double[] frequency = this.getFrequency(); + if (frequency != null) { + str += "Frequency: " + FrequencyFormat.formatFrequencyRange(this.getFrequency(), true) + "
"; + } + if (getAmplitudeDB() != 0) { + str += String.format("Amplitude: %3.1fdB
", getAmplitudeDB()); + } if (getSignalSPL() != null) { str += String.format("SPL: %3.1fdBre1uPa
",linAmplitudeToDB(getSignalSPL())); } diff --git a/src/binaryFileStorage/BinaryStore.java b/src/binaryFileStorage/BinaryStore.java index bd6d812e..97e85358 100644 --- a/src/binaryFileStorage/BinaryStore.java +++ b/src/binaryFileStorage/BinaryStore.java @@ -2553,5 +2553,9 @@ PamSettingsSource, DataOutputStore { BinaryStoreDeleter storeDeleter = new BinaryStoreDeleter(this); return storeDeleter.deleteDataFrom(timeMillis); } + @Override + public String getDataLocation() { + return binaryStoreSettings.getStoreLocation(); + } } diff --git a/src/dataMap/filemaps/OfflineFileServer.java b/src/dataMap/filemaps/OfflineFileServer.java index 2249eb4e..9a765f5c 100644 --- a/src/dataMap/filemaps/OfflineFileServer.java +++ b/src/dataMap/filemaps/OfflineFileServer.java @@ -341,6 +341,12 @@ public abstract class OfflineFileServer impl // TODO Auto-generated method stub return "Sound Files"; } + + @Override + public String getDataLocation() { + getOfflineFileParameters(); + return offlineFileParameters.folderName; + } public TmapPoint findFirstMapPoint(Iterator mapIterator, long startMillis, long endMillis) { TmapPoint mapPoint, prevMapPoint = null; diff --git a/src/decimator/DecimatorControl.java b/src/decimator/DecimatorControl.java index 7bf9f8ac..1b7d2758 100644 --- a/src/decimator/DecimatorControl.java +++ b/src/decimator/DecimatorControl.java @@ -193,6 +193,15 @@ public class DecimatorControl extends PamControlledUnit implements PamSettings, } return offlineFileServer.getDataSourceName(); } + + @Override + public String getDataLocation() { + if (offlineFileServer == null) { + return getUnitName(); + } + return offlineFileServer.getDataLocation(); + } + @Override public boolean loadData(PamDataBlock dataBlock, OfflineDataLoadInfo offlineDataLoadInfo, ViewLoadObserver loadObserver) { if (offlineFileServer == null) { diff --git a/src/difar/beamforming/BeamformControl.java b/src/difar/beamforming/BeamformControl.java index edf3507b..bf04ea05 100644 --- a/src/difar/beamforming/BeamformControl.java +++ b/src/difar/beamforming/BeamformControl.java @@ -88,6 +88,16 @@ public class BeamformControl extends PamControlledUnit implements PamSettings, O return DifarParameters.serialVersionUID; } + @Override + public String getDataLocation() { + if (offlineFileServer != null) { + return offlineFileServer.getDataLocation(); + } + else { + return null; + } + } + @Override public boolean restoreSettings( PamControlledUnitSettings pamControlledUnitSettings) { diff --git a/src/fileOfflineData/OfflineFileControl.java b/src/fileOfflineData/OfflineFileControl.java index cb408652..45998aaf 100644 --- a/src/fileOfflineData/OfflineFileControl.java +++ b/src/fileOfflineData/OfflineFileControl.java @@ -54,6 +54,11 @@ public abstract class OfflineFileControl extends PamControlledUnit implements Of } + @Override + public String getDataLocation() { + return fileParams.offlineFolder; + } + /* (non-Javadoc) * @see PamController.PamControlledUnit#notifyModelChanged(int) */ diff --git a/src/generalDatabase/DBControlUnit.java b/src/generalDatabase/DBControlUnit.java index ff310652..74073ba9 100644 --- a/src/generalDatabase/DBControlUnit.java +++ b/src/generalDatabase/DBControlUnit.java @@ -404,6 +404,11 @@ public class DBControlUnit extends DBControl implements DataOutputStore { return getUnitName(); } + @Override + public String getDataLocation() { + return getDatabaseName(); + } + @Override public boolean loadData(PamDataBlock dataBlock, OfflineDataLoadInfo offlineDataLoadInfo, ViewLoadObserver loadObserver) { SQLLogging logging = dataBlock.getLogging(); diff --git a/src/metadata/swing/MetaDataDialog.java b/src/metadata/swing/MetaDataDialog.java index 73ff66bb..cf2f2fc2 100644 --- a/src/metadata/swing/MetaDataDialog.java +++ b/src/metadata/swing/MetaDataDialog.java @@ -4,20 +4,34 @@ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; +import java.awt.Point; import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JLabel; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.border.TitledBorder; +import PamController.PamController; import PamView.dialog.PamDialog; import PamView.dialog.PamGridBagContraints; import PamView.panel.WestAlignedPanel; import metadata.PamguardMetaData; import nilus.Deployment; +import tethys.TethysControl; +import tethys.TethysState; +import tethys.TethysState.StateType; +import tethys.deployment.swing.ProjectInformationPanel; +import tethys.swing.NewProjectDialog; +import tethys.swing.SelectProjectDialog; import tethys.swing.export.DescriptionTypePanel; import tethys.swing.export.ResponsiblePartyPanel; @@ -29,9 +43,11 @@ public class MetaDataDialog extends PamDialog { private DescriptionTypePanel descriptionPanel; - private JTextField project, site, cruise, region; + private ProjectInformationPanel projectInformationPanel; private ResponsiblePartyPanel responsiblePanel; + + private TethysControl tethysControl; private MetaDataDialog(Window parentFrame) { super(parentFrame, "Project information", false); @@ -40,38 +56,21 @@ public class MetaDataDialog extends PamDialog { mainPanel.setLayout(new BorderLayout()); JTabbedPane tabbedPane = new JTabbedPane(); + tethysControl = (TethysControl) PamController.getInstance().findControlledUnit(TethysControl.unitType); + + projectInformationPanel = new ProjectInformationPanel(parentFrame, null); descriptionPanel = new DescriptionTypePanel(null, false, false, false); descriptionPanel.getMainPanel().setPreferredSize(new Dimension(400,300)); - JPanel projectPanel = new JPanel(new GridBagLayout()); - GridBagConstraints c = new PamGridBagContraints(); - projectPanel.add(new JLabel("Project Name ", JLabel.RIGHT), c); - c.gridx++; - projectPanel.add(project = new JTextField(40), c); - c.gridx = 0; - c.gridy++; - projectPanel.add(new JLabel("Region ", JLabel.RIGHT), c); - c.gridx++; - projectPanel.add(region = new JTextField(20), c); - c.gridx = 0; - c.gridy++; - projectPanel.add(new JLabel("Cruise name ", JLabel.RIGHT), c); - c.gridx++; - projectPanel.add(cruise = new JTextField(20), c); - c.gridx = 0; - c.gridy++; - projectPanel.add(new JLabel("Site ", JLabel.RIGHT), c); - c.gridx++; - projectPanel.add(site = new JTextField(20), c); - c.gridx = 0; - c.gridy++; + +// JPanel projectPanel = new JPanel(new GridBagLayout()); responsiblePanel = new ResponsiblePartyPanel(); JPanel northPanel = new JPanel(); WestAlignedPanel wp; northPanel.setLayout(new BoxLayout(northPanel, BoxLayout.Y_AXIS)); - northPanel.add(wp = new WestAlignedPanel(projectPanel)); + northPanel.add(wp = new WestAlignedPanel(projectInformationPanel.getMainPanel())); wp.setBorder(new TitledBorder("General project information")); northPanel.add(wp = new WestAlignedPanel(responsiblePanel.getMainPanel())); wp.setBorder(new TitledBorder("Contact information")); @@ -87,6 +86,9 @@ public class MetaDataDialog extends PamDialog { setDialogComponent(mainPanel); } + + + public static PamguardMetaData showDialog(Window frame, PamguardMetaData pamguardMetaData) { singleInstance = new MetaDataDialog(frame); singleInstance.setParams(pamguardMetaData); @@ -99,10 +101,6 @@ public class MetaDataDialog extends PamDialog { Deployment deployment = pamguardMetaData.getDeployment(); descriptionPanel.setParams(deployment.getDescription()); responsiblePanel.setParams(deployment.getMetadataInfo().getContact()); - cruise.setText(deployment.getCruise()); - region.setText(deployment.getRegion()); - site.setText(deployment.getSite()); - project.setText(deployment.getProject()); } @Override @@ -110,11 +108,10 @@ public class MetaDataDialog extends PamDialog { Deployment deployment = pamguardMetaData.getDeployment(); boolean ok = descriptionPanel.getParams(deployment.getDescription()); ok &= responsiblePanel.getParams(deployment.getMetadataInfo().getContact()); - deployment.setCruise(cruise.getText()); - deployment.setRegion(region.getText()); - deployment.setSite(site.getText()); - deployment.setProject(project.getText()); + if (tethysControl != null) { + tethysControl.sendStateUpdate(new TethysState(StateType.NEWPROJECTSELECTION)); + } return ok; } diff --git a/src/tethys/calibration/CalibrationHandler.java b/src/tethys/calibration/CalibrationHandler.java index 8b88dc36..15f3ece4 100644 --- a/src/tethys/calibration/CalibrationHandler.java +++ b/src/tethys/calibration/CalibrationHandler.java @@ -523,7 +523,13 @@ public class CalibrationHandler implements TethysStateObserver { * @return list of calibration documents using this instrument, based on the start of the document name. */ private ArrayList getArrayCalibrations() { - ArrayList allCals = tethysControl.getDbxmlQueries().getCollectionDocumentList(Collection.Calibrations); + ArrayList allCals = null; + try { + allCals = tethysControl.getDbxmlQueries().getCollectionDocumentList(Collection.Calibrations); + } + catch (Exception e) { + + } if (allCals == null) { return null; } diff --git a/src/tethys/deployment/DeploymentExportOpts.java b/src/tethys/deployment/DeploymentExportOpts.java new file mode 100644 index 00000000..3563fb95 --- /dev/null +++ b/src/tethys/deployment/DeploymentExportOpts.java @@ -0,0 +1,40 @@ +package tethys.deployment; + +import java.io.Serializable; + +/** + * options for Deployment export collected by the export Wizard. + * @author dg50 + * + */ +public class DeploymentExportOpts implements Serializable, Cloneable { + + public static final long serialVersionUID = 1L; + + public boolean separateDeployments; + + public double trackPointInterval; + + /** + * Max gap before recording periods are separated, potentially into + * separate Deployment documents + */ + public int maxGapSeconds = 60; + + /** + * A recording section after joining with max gap parameter is too short + * to be worth keeping. + */ + public int minLengthSeconds = 10; + + @Override + protected DeploymentExportOpts clone() { + try { + return (DeploymentExportOpts) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/src/tethys/deployment/DeploymentHandler.java b/src/tethys/deployment/DeploymentHandler.java index fbdb668e..2eb0b185 100644 --- a/src/tethys/deployment/DeploymentHandler.java +++ b/src/tethys/deployment/DeploymentHandler.java @@ -1,5 +1,7 @@ package tethys.deployment; +import java.awt.Window; +import java.io.Serializable; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; @@ -24,10 +26,16 @@ import Array.HydrophoneLocator; import Array.PamArray; import Array.Streamer; import Array.ThreadingHydrophoneLocator; +import GPS.GPSControl; +import GPS.GPSDataBlock; +import GPS.GpsData; +import GPS.GpsDataUnit; import PamController.PamSensor; +import PamController.PamSettingManager; +import PamController.PamSettings; import PamController.PamControlledUnit; +import PamController.PamControlledUnitSettings; import PamController.PamController; -import PamUtils.LatLong; import PamUtils.PamUtils; import PamguardMVC.PamDataBlock; import PamguardMVC.PamRawDataBlock; @@ -46,6 +54,10 @@ import nilus.ChannelInfo.Sampling; import nilus.ChannelInfo.Sampling.Regimen; import nilus.Deployment; import nilus.Deployment.Data; +import nilus.Deployment.Data.Tracks; +import nilus.Deployment.Data.Tracks.Track; +import nilus.Deployment.Data.Tracks.Track.Point; +import nilus.Deployment.Data.Tracks.Track.Point.BearingDegN; import nilus.Deployment.Instrument; import nilus.Deployment.SamplingDetails; import nilus.Deployment.Sensors; @@ -66,8 +78,12 @@ import tethys.calibration.CalibrationHandler; import tethys.TethysState.StateType; import tethys.dbxml.DBXMLConnect; import tethys.dbxml.TethysException; +import tethys.deployment.swing.DeploymentWizard; +import tethys.deployment.swing.RecordingGapDialog; import tethys.niluswraps.PDeployment; import tethys.output.TethysExportParams; +import tethys.pamdata.AutoTethysProvider; +import tethys.swing.DeploymentTableObserver; /** * Functions to gather data for the deployment document from all around PAMGuard. @@ -77,7 +93,7 @@ import tethys.output.TethysExportParams; * @author dg50 * */ -public class DeploymentHandler implements TethysStateObserver { +public class DeploymentHandler implements TethysStateObserver, DeploymentTableObserver { private TethysControl tethysControl; @@ -93,6 +109,8 @@ public class DeploymentHandler implements TethysStateObserver { private ArrayList projectDeployments; private Helper nilusHelper; + + private DeploymentExportOpts exportOptions = new DeploymentExportOpts(); public DeploymentHandler(TethysControl tethysControl) { super(); @@ -103,6 +121,32 @@ public class DeploymentHandler implements TethysStateObserver { } catch (JAXBException e) { e.printStackTrace(); } + PamSettingManager.getInstance().registerSettings(new SettingsHandler()); + } + + /** + * Gather up all track information both from the GPS module (if it exists) and + * the type of hydrophone array (or many!) + * @return + */ + public TrackInformation getTrackInformation() { + PamArray array = ArrayManager.getArrayManager().getCurrentArray(); + int nStreamers = array.getStreamerCount(); + HydrophoneLocator locator = null; + for (int i = 0; i < nStreamers; i++) { + Streamer aStreamer = array.getStreamer(i); + locator = aStreamer.getHydrophoneLocator(); +// locator.getLocatorSettings(). + } + // try to find a GPS datablock and see what's in it's datamap. + OfflineDataMap gpsDataMap = null; + GPSControl gpsControl = (GPSControl) PamController.getInstance().findControlledUnit(GPSControl.gpsUnitType); + if (gpsControl != null) { + GPSDataBlock gpsDataBlock = gpsControl.getGpsDataBlock(); + gpsDataMap = gpsDataBlock.getPrimaryDataMap(); + } + TrackInformation trackInformation = new TrackInformation(gpsDataMap, locator); + return trackInformation; } @Override @@ -137,6 +181,7 @@ public class DeploymentHandler implements TethysStateObserver { projectDeployments.add(new PDeployment(deployment)); } matchPamguard2Tethys(deploymentOverview, projectDeployments); + tethysControl.sendStateUpdate(new TethysState(TethysState.StateType.NEWPAMGUARDSELECTION)); return true; } @@ -240,7 +285,7 @@ public class DeploymentHandler implements TethysStateObserver { if (prevPeriod != null) { long gap = nextPeriod.getRecordStart() - prevPeriod.getRecordStop(); long prevDur = prevPeriod.getRecordStop()-prevPeriod.getRecordStart(); - if (gap < 3000 || gap < prevDur/50) { + if (gap < exportOptions.maxGapSeconds*1000) { // ignoring up to 3s gap or a sample error < 2%.Dunno if this is sensible or not. prevPeriod.setRecordStop(nextPeriod.getRecordStop()); iterator.remove(); @@ -249,6 +294,15 @@ public class DeploymentHandler implements TethysStateObserver { } prevPeriod = nextPeriod; } + // now remove ones which are too short even after merging. + iterator = tempPeriods.listIterator(); + while (iterator.hasNext()) { + RecordingPeriod nextPeriod = iterator.next(); + long duration = nextPeriod.getDuration(); + if (duration < exportOptions.minLengthSeconds*1000L) { + iterator.remove(); + } + } // i = 0; // for (RecordingPeriod aP : tempPeriods) { // System.out.printf("Post merge %d : %s to %s\n", i++, PamCalendar.formatDBDateTime(aP.getRecordStart()), @@ -288,12 +342,38 @@ public class DeploymentHandler implements TethysStateObserver { } + public void showOptions(Window parent) { + if (parent == null) { + parent = tethysControl.getGuiFrame(); + } + DeploymentExportOpts newOpts = RecordingGapDialog.showDiloag(parent, exportOptions); + if (newOpts != null) { + exportOptions = newOpts; + deploymentOverview = createPamguardOverview(); + updateProjectDeployments(); + } + } + /** - * Exprt deployments docs. Playing with a couple of different ways of doing this. + * Export button pressed on GUI. Run wizard.... + */ + public void exportDeployments() { + Deployment deployment = MetaDataContol.getMetaDataControl().getMetaData().getDeployment(); + DeploymentExportOpts exportOptions = DeploymentWizard.showWizard(getTethysControl().getGuiFrame(), tethysControl, deployment, this.exportOptions); + if (exportOptions != null) { + this.exportOptions = exportOptions; + deploymentOverview = getDeploymentOverview(); + ArrayList allPeriods = deploymentOverview.getRecordingPeriods(); + exportDeployments(allPeriods); + } + } + + /** + * Export deployments docs. Playing with a couple of different ways of doing this. * @param selectedDeployments */ public void exportDeployments(ArrayList selectedDeployments) { - if (false) { + if (exportOptions.separateDeployments) { exportSeparateDeployments(selectedDeployments); } else { @@ -765,9 +845,11 @@ public class DeploymentHandler implements TethysStateObserver { deployment.setDeploymentDetails(deploymentDetails); deployment.setRecoveryDetails(recoveryDetails); - TethysLocationFuncs.getTrackAndPositionData(deployment); - getProjectData(deployment); + + TethysLocationFuncs.getTrackAndPositionData(deployment); + + getTrackDetails(deployment); DescriptionType description = deployment.getDescription(); if (description == null ) { @@ -797,6 +879,67 @@ public class DeploymentHandler implements TethysStateObserver { return deployment; } + /** + * Add the track to the deployment, if there is one (i.e. not for + * a fixed sensor). + * @param deployment + */ + private void getTrackDetails(Deployment deployment) { + TrackInformation trackInfo = getTrackInformation(); + if (trackInfo.haveGPSTrack() == false) { + return; + } + GPSDataBlock gpsDataBlock = (GPSDataBlock) trackInfo.getGpsDataMap().getParentDataBlock(); + if (gpsDataBlock == null) { + return; + } + /* + * should have some track information. Do a load from the + * database for the whole deployment. this may be the entire GPS record, but + * we should be able to cope with that. + */ + long trackStart = TethysTimeFuncs.millisFromGregorianXML(deployment.getDeploymentDetails().getTimeStamp()); + long trackEnd = TethysTimeFuncs.millisFromGregorianXML(deployment.getRecoveryDetails().getTimeStamp()); + long dataWin =(long) (Math.max(1./trackInfo.getGPSDataRate(), exportOptions.trackPointInterval)); + + // get the tracks object. + Tracks tracks = deployment.getData().getTracks(); + if (tracks == null) { + tracks = new Tracks(); + deployment.getData().setTracks(tracks); + } + List trackList = tracks.getTrack(); // lists are usually there. + + Track aTrack = new Track(); + trackList.add(aTrack); + List points = aTrack.getPoint(); + + gpsDataBlock.loadViewerData(trackStart-dataWin, trackEnd+dataWin, null); + long lastPointTime = 0; + ListIterator it = gpsDataBlock.getListIterator(0); + while (it.hasNext()) { + GpsDataUnit gpsDataUnit = it.next(); + if (gpsDataUnit.getTimeMilliseconds()-lastPointTime < exportOptions.trackPointInterval*1000) { + continue; + } + GpsData gpsData = gpsDataUnit.getGpsData(); + Point gpsPoint = new Point(); + gpsPoint.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(gpsDataUnit.getTimeMilliseconds())); + gpsPoint.setLatitude(gpsData.getLatitude()); + gpsPoint.setLongitude(PamUtils.constrainedAngle(gpsData.getLongitude())); + BearingDegN bdn = gpsPoint.getBearingDegN(); + if (bdn == null) { + bdn = new BearingDegN(); + gpsPoint.setBearingDegN(bdn); + } + bdn.setValue(AutoTethysProvider.roundDecimalPlaces(PamUtils.constrainedAngle(gpsData.getHeading()),1)); + gpsPoint.setSpeedKn(AutoTethysProvider.roundDecimalPlaces(gpsData.getSpeed(),2)); + + points.add(gpsPoint); + lastPointTime = gpsDataUnit.getTimeMilliseconds(); + } + } + public String getBinaryDataURI() { BinaryStore binStore = BinaryStore.findBinaryStoreControl(); if (binStore != null) { @@ -926,15 +1069,17 @@ public class DeploymentHandler implements TethysStateObserver { if (depTime != null) { deployment.getDeploymentDetails().setTimeStamp(depTime); } - XMLGregorianCalendar recMillis = deploymentData.getRecoveryDetails().getTimeStamp(); - if (recMillis != null) { - deployment.getRecoveryDetails().setTimeStamp(recMillis); - } - double recLat = deploymentData.getRecoveryDetails().getLatitude(); - double recLong = deploymentData.getRecoveryDetails().getLongitude(); - if (recLat != 0 & recLong != 0.) { - deployment.getRecoveryDetails().setLatitude(recLat); - deployment.getRecoveryDetails().setLongitude(PamUtils.constrainedAngle(recLong)); + if (deploymentData.getRecoveryDetails() != null) { + XMLGregorianCalendar recMillis = deploymentData.getRecoveryDetails().getTimeStamp(); + if (recMillis != null) { + deployment.getRecoveryDetails().setTimeStamp(recMillis); + } + double recLat = deploymentData.getRecoveryDetails().getLatitude(); + double recLong = deploymentData.getRecoveryDetails().getLongitude(); + if (recLat != 0 & recLong != 0.) { + deployment.getRecoveryDetails().setLatitude(recLat); + deployment.getRecoveryDetails().setLongitude(PamUtils.constrainedAngle(recLong)); + } } return true; @@ -1164,4 +1309,40 @@ public class DeploymentHandler implements TethysStateObserver { return true; } + @Override + public void selectionChanged() { + // TODO Auto-generated method stub + + } + + private class SettingsHandler implements PamSettings { + + @Override + public String getUnitName() { + return tethysControl.getUnitName(); + } + + @Override + public String getUnitType() { + return "Tethys Deployment Handler"; + } + + @Override + public Serializable getSettingsReference() { + return exportOptions; + } + + @Override + public long getSettingsVersion() { + return DeploymentExportOpts.serialVersionUID; + } + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + exportOptions = (DeploymentExportOpts) pamControlledUnitSettings.getSettings(); + return true; + } + + } + } diff --git a/src/tethys/deployment/RecordingPeriod.java b/src/tethys/deployment/RecordingPeriod.java index b7830080..ad010827 100644 --- a/src/tethys/deployment/RecordingPeriod.java +++ b/src/tethys/deployment/RecordingPeriod.java @@ -8,6 +8,7 @@ public class RecordingPeriod { private long recordStop; + private boolean selected; // selected in the table or elsewhere for export. /** * Reference to a matched nilus Deployment document retrieved * from the database. @@ -47,6 +48,28 @@ public class RecordingPeriod { public void setMatchedTethysDeployment(PDeployment closestDeployment) { this.matchedTethysDeployment = closestDeployment; } + + /** + * @return the selected + */ + public boolean isSelected() { + return selected; + } + + /** + * @param selected the selected to set + */ + public void setSelected(boolean selected) { + this.selected = selected; + } + /** + * toggle the selected state + * @return the new state + */ + public boolean toggleSelected() { + selected = !selected; + return selected; + } } diff --git a/src/tethys/deployment/TrackInformation.java b/src/tethys/deployment/TrackInformation.java new file mode 100644 index 00000000..1acb7ce9 --- /dev/null +++ b/src/tethys/deployment/TrackInformation.java @@ -0,0 +1,85 @@ +package tethys.deployment; + +import java.util.Iterator; + +import Array.HydrophoneLocator; +import GPS.GPSDataBlock; +import PamguardMVC.PamDataBlock; +import dataMap.OfflineDataMap; +import dataMap.OfflineDataMapPoint; + +/** + * Some general information about the track: whether it exists and + * the frequency of GPS points. + * @author dg50 + * + */ +public class TrackInformation { + + private HydrophoneLocator hydrophoneLocator; + private OfflineDataMap gpsDataMap; + + public TrackInformation(OfflineDataMap gpsDataMap, HydrophoneLocator locator) { + this.gpsDataMap = gpsDataMap; + this.hydrophoneLocator = locator; + } + + public boolean haveGPSTrack() { + if (gpsDataMap == null) { + return false; + } + return (gpsDataMap.getDataCount() > 0); + } + + /** + * Get an estimate of the highest GPS data rate in points per second. This is obtained from the + * datamap, taking the highest rate for all data map points (typically an hour of + * database data). + * @return + */ + public double getGPSDataRate() { + if (gpsDataMap == null) { + return 0; + } + GPSDataBlock gpsDataBlock = (GPSDataBlock) gpsDataMap.getParentDataBlock(); + Iterator mPs = gpsDataMap.getListIterator(); + double highRate = 0; + while (mPs.hasNext()) { + OfflineDataMapPoint mP = mPs.next(); + int n = mP.getNDatas(); + double dur = (mP.getEndTime()-mP.getStartTime())/1000.; + double rate = n/dur; + highRate = Math.max(highRate, rate); + } + return highRate; + } + + /** + * @return the hydrophoneLocator + */ + public HydrophoneLocator getHydrophoneLocator() { + return hydrophoneLocator; + } + + /** + * @param hydrophoneLocator the hydrophoneLocator to set + */ + public void setHydrophoneLocator(HydrophoneLocator hydrophoneLocator) { + this.hydrophoneLocator = hydrophoneLocator; + } + + /** + * @return the gpsDataMap + */ + public OfflineDataMap getGpsDataMap() { + return gpsDataMap; + } + + /** + * @param gpsDataMap the gpsDataMap to set + */ + public void setGpsDataMap(OfflineDataMap gpsDataMap) { + this.gpsDataMap = gpsDataMap; + } + +} diff --git a/src/tethys/deployment/swing/DeploymentDataCard.java b/src/tethys/deployment/swing/DeploymentDataCard.java new file mode 100644 index 00000000..57c41b4d --- /dev/null +++ b/src/tethys/deployment/swing/DeploymentDataCard.java @@ -0,0 +1,103 @@ +package tethys.deployment.swing; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.ArrayList; + +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JRadioButton; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamController.OfflineDataStore; +import PamController.PamController; +import PamView.dialog.PamGridBagContraints; +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; +import nilus.Deployment; +import tethys.TethysControl; +import tethys.deployment.DeploymentExportOpts; +import tethys.deployment.DeploymentHandler; + +public class DeploymentDataCard extends PamWizardCard { + + private TethysControl tethysControl; + private DeploymentHandler deploymentHandler; + + private JRadioButton exportOne, exportMany; + + private JTextField[] dataStores; +// private JTextField rawURI, binaryURI, databaseURI; + private ArrayList offlineDataStores; + + public DeploymentDataCard(PamWizard pamWizard, TethysControl tethysControl) { + super(pamWizard, "Data"); + this.tethysControl = tethysControl; + deploymentHandler = tethysControl.getDeploymentHandler(); + ButtonGroup bg = new ButtonGroup(); + exportOne = new JRadioButton("Export a single detection document for all data"); + exportMany = new JRadioButton("Export separate documents for each ad-hoc recording period"); + bg.add(exportOne); + bg.add(exportMany); + + JPanel optsPanel = new JPanel(new GridBagLayout()); + optsPanel.setBorder(new TitledBorder("Number of documents")); + GridBagConstraints c = new PamGridBagContraints(); + optsPanel.add(exportOne, c); + c.gridy++; + optsPanel.add(exportMany, c); + + JPanel dataPanel = new JPanel(new GridBagLayout()); + dataPanel.setBorder(new TitledBorder("Data location")); + + // automatically generate fields for every offline data store. + offlineDataStores = PamController.getInstance().findOfflineDataStores(); + dataStores = new JTextField[offlineDataStores.size()]; + c = new PamGridBagContraints(); + for (int i = 0; i < offlineDataStores.size(); i++) { + OfflineDataStore aStore = offlineDataStores.get(i); + dataPanel.add(new JLabel(aStore.getDataSourceName() + " ", JLabel.RIGHT), c); + c.gridx++; + dataStores[i] = new JTextField(40); + dataPanel.add(dataStores[i], c); + c.gridx = 0; + c.gridy++; + } + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.add(optsPanel); + this.add(dataPanel); + } + + + @Override + public boolean getParams(Object cardParams) { + // TODO Auto-generated method stub + return true; + } + + @Override + public void setParams(Object cardParams) { + for (int i = 0; i < offlineDataStores.size(); i++) { + OfflineDataStore aStore = offlineDataStores.get(i); + dataStores[i].setText(aStore.getDataLocation()); + } + + } + + public boolean getParams(DeploymentExportOpts exportOptions, Deployment deployment) { + exportOptions.separateDeployments = exportMany.isSelected(); + return true; + } + + public void setParams(DeploymentExportOpts exportOptions, Deployment deployment) { + exportOne.setSelected(exportOptions.separateDeployments == false); + exportMany.setSelected(exportOptions.separateDeployments == true); + setParams(deployment); + } + +} diff --git a/src/tethys/deployment/swing/DeploymentInfoCard.java b/src/tethys/deployment/swing/DeploymentInfoCard.java new file mode 100644 index 00000000..f8d7938e --- /dev/null +++ b/src/tethys/deployment/swing/DeploymentInfoCard.java @@ -0,0 +1,47 @@ +package tethys.deployment.swing; + +import javax.swing.BoxLayout; + +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; +import nilus.Deployment; +import tethys.deployment.DeploymentExportOpts; +import tethys.swing.export.ResponsiblePartyPanel; + +public class DeploymentInfoCard extends PamWizardCard { + + private ResponsiblePartyPanel responsiblePartyPanel; + + private ProjectInformationPanel projectInformationPanel; + + public DeploymentInfoCard(PamWizard pamWizard, String title) { + super(pamWizard, title); + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + projectInformationPanel = new ProjectInformationPanel(pamWizard, title); + this.add(projectInformationPanel.getMainPanel()); + responsiblePartyPanel = new ResponsiblePartyPanel("Responsible Party"); + this.add(responsiblePartyPanel.getMainPanel()); + } + + @Override + public boolean getParams(Deployment cardParams) { + boolean ok = responsiblePartyPanel.getParams(cardParams.getMetadataInfo().getContact()); + ok &= projectInformationPanel.getParams(cardParams); + + return ok; + } + + @Override + public void setParams(Deployment cardParams) { + projectInformationPanel.setParams(cardParams); + responsiblePartyPanel.setParams(cardParams.getMetadataInfo().getContact()); + } + + public boolean getParams(DeploymentExportOpts exportOptions, Deployment deployment) { + boolean ok = getParams(deployment); + + return ok; + } + +} diff --git a/src/tethys/deployment/swing/DeploymentTrackCard.java b/src/tethys/deployment/swing/DeploymentTrackCard.java new file mode 100644 index 00000000..20856fca --- /dev/null +++ b/src/tethys/deployment/swing/DeploymentTrackCard.java @@ -0,0 +1,183 @@ +package tethys.deployment.swing; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +import javax.swing.BoxLayout; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import PamUtils.PamCalendar; +import PamView.dialog.PamGridBagContraints; +import PamView.panel.WestAlignedPanel; +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; +import dataMap.OfflineDataMap; +import tethys.TethysControl; +import tethys.deployment.DeploymentExportOpts; +import tethys.deployment.TrackInformation; + +public class DeploymentTrackCard extends PamWizardCard { + + private static final long serialVersionUID = 1L; + + private TethysControl tethysControl; + + private TrackInformation trackInfo; + + private JTextField totalPoints, startDate, endDate, highestRate; + + private JTextField exportInterval, exportCount; + + public DeploymentTrackCard(PamWizard pamWizard, TethysControl tethysControl, TrackInformation trackInfo) { + super(pamWizard, "Track Data"); + this.tethysControl = tethysControl; + this.trackInfo = trackInfo; + JPanel trackPanel = new JPanel(); + WestAlignedPanel wp = new WestAlignedPanel(trackPanel); + wp.setBorder(new TitledBorder("Track data summary")); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + add(wp); + trackPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + c.gridx = 1; + trackPanel.add(new JLabel("PAMGuard data content .... ", JLabel.LEFT), c); + c.gridx = 0; + c.gridy++; + trackPanel.add(new JLabel("Track Start ", JLabel.RIGHT), c); + c.gridx++; + trackPanel.add(startDate = new TrackField(20), c); + c.gridx = 0; + c.gridy++; + trackPanel.add(new JLabel("Track End ", JLabel.RIGHT), c); + c.gridx++; + trackPanel.add(endDate = new TrackField(20), c); + c.gridx = 0; + c.gridy++; + trackPanel.add(new JLabel("Total Points ", JLabel.RIGHT), c); + c.gridx++; + trackPanel.add(totalPoints = new TrackField(20), c); + c.gridx = 0; + c.gridy++; + trackPanel.add(new JLabel("Interval ", JLabel.RIGHT), c); + c.gridx++; + trackPanel.add(highestRate = new TrackField(20), c); + + c.gridx = 1; + c.gridy++; + c.gridwidth = 1; + trackPanel.add(new JLabel("Export .... ", JLabel.LEFT), c); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + trackPanel.add(new JLabel("Export interval ", JLabel.RIGHT), c); + c.gridx++; + trackPanel.add(exportInterval = new JTextField(12), c); + c.gridx = 0; + c.gridy++; + trackPanel.add(new JLabel("Estimated elements ", JLabel.RIGHT), c); + c.gridx++; + trackPanel.add(exportCount = new TrackField(12), c); + c.gridx = 0; + c.gridy++; + + IntervalListener il = new IntervalListener(); +// exportInterval.addActionListener(il); +// exportInterval.addKeyListener(il); +// exportInterval.addFocusListener(il); + exportInterval.getDocument().addDocumentListener(il); + + +// c.gridx++; +// trackPanel.add(new JLabel(" per minute ", JLabel.LEFT), c); +// c.gridy++; + + } + + private class IntervalListener implements DocumentListener { + + @Override + public void insertUpdate(DocumentEvent e) { + updateExportCount(); + } + + @Override + public void removeUpdate(DocumentEvent e) { + updateExportCount(); + } + + @Override + public void changedUpdate(DocumentEvent e) { + updateExportCount(); + } + + } + + private class TrackField extends JTextField { + + /** + * @param columns + */ + public TrackField(int columns) { + super(columns); + setEditable(false); + } + + } + + @Override + public boolean getParams(DeploymentExportOpts cardParams) { + try { + cardParams.trackPointInterval = Double.valueOf(exportInterval.getText()); + } + catch (Exception e) { + return getPamWizard().showWarning("Invalid track point interval"); + } + return true; + } + + public void updateExportCount() { + OfflineDataMap dataMap = trackInfo.getGpsDataMap(); + if (dataMap == null) { + return; + } + try { +// System.out.println(exportInterval.getText()); + double intval = Double.valueOf(exportInterval.getText()); + double highRate = trackInfo.getGPSDataRate(); + int nCount = dataMap.getDataCount(); + int newEst = (int) Math.round(Math.min(nCount/(intval*highRate), nCount)); + exportCount.setText(String.format("%d", newEst)); + } + catch (Exception e) { + exportCount.setText(null); + } + + } + + @Override + public void setParams(DeploymentExportOpts cardParams) { + OfflineDataMap dataMap = trackInfo.getGpsDataMap(); + if (dataMap == null) { + return; + } + startDate.setText(PamCalendar.formatDBDateTime(dataMap.getFirstDataTime())); + endDate.setText(PamCalendar.formatDBDateTime(dataMap.getLastDataTime())); + totalPoints.setText(String.format("%d", dataMap.getDataCount())); + double rate = trackInfo.getGPSDataRate(); + highestRate.setText(PamCalendar.formatDuration((long) (1000/rate))); + + } + +} diff --git a/src/tethys/deployment/swing/DeploymentWizard.java b/src/tethys/deployment/swing/DeploymentWizard.java new file mode 100644 index 00000000..d7ca83dc --- /dev/null +++ b/src/tethys/deployment/swing/DeploymentWizard.java @@ -0,0 +1,100 @@ +package tethys.deployment.swing; + +import java.awt.Dimension; +import java.awt.Window; + +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; +import metadata.MetaDataContol; +import nilus.Deployment; +import tethys.TethysControl; +import tethys.deployment.DeploymentExportOpts; +import tethys.deployment.DeploymentHandler; +import tethys.deployment.TrackInformation; +import tethys.swing.export.DescriptionCard; +import tethys.swing.export.ResponsiblePartyCard; + +public class DeploymentWizard extends PamWizard { + + private static final long serialVersionUID = 1L; + + private Deployment deployment; + + private DeploymentExportOpts exportOptions; + + private DescriptionCard descriptionCard; + + private DeploymentInfoCard deploymentInfoCard; + + private DeploymentDataCard deploymentDataCard; + + private DeploymentTrackCard deploymentTrackCard; +// private + + private DeploymentWizard(Window parentFrame, TethysControl tethysControl, Deployment deployment, DeploymentExportOpts exportOptions) { + super(parentFrame, "Deployment Export"); + this.deployment = deployment; + this.exportOptions = exportOptions; + DeploymentHandler deploymentHandler = tethysControl.getDeploymentHandler(); + TrackInformation trackInfo = deploymentHandler.getTrackInformation(); + + addCard(deploymentInfoCard = new DeploymentInfoCard(this, "Responsible Party")); + addCard(deploymentDataCard = new DeploymentDataCard(this, tethysControl)); + addCard(descriptionCard = new DescriptionCard(this, tethysControl)); + boolean haveGPS = trackInfo.haveGPSTrack(); + if (haveGPS) { + deploymentTrackCard = new DeploymentTrackCard(this, tethysControl, trackInfo); + addCard(deploymentTrackCard); + } + descriptionCard.setPreferredSize(new Dimension(10, 300)); + } + + public static DeploymentExportOpts showWizard(Window parentFrame, TethysControl tethysControl, Deployment deployment, DeploymentExportOpts exportOptions) { + if (deployment == null) { + deployment = MetaDataContol.getMetaDataControl().getMetaData().getDeployment(); + } + DeploymentWizard wiz = new DeploymentWizard(parentFrame, tethysControl, deployment, exportOptions); + wiz.setParams(); + wiz.setVisible(true); + return wiz.exportOptions; + } + + @Override + public void setCardParams(PamWizardCard wizardCard) { + if (wizardCard == descriptionCard) { + descriptionCard.setParams(deployment.getDescription()); + } + if (wizardCard == deploymentInfoCard) { + deploymentInfoCard.setParams(deployment); + } + if (wizardCard == deploymentDataCard) { + deploymentDataCard.setParams(exportOptions, deployment); + } + if (wizardCard == deploymentTrackCard) { + deploymentTrackCard.setParams(exportOptions); + } + } + + @Override + public boolean getCardParams(PamWizardCard wizardCard) { + if (wizardCard == descriptionCard) { + return descriptionCard.getParams(deployment.getDescription()); + } + if (wizardCard == deploymentInfoCard) { + return deploymentInfoCard.getParams(exportOptions, deployment); + } + if (wizardCard == deploymentDataCard) { + return deploymentDataCard.getParams(exportOptions, deployment); + } + if (wizardCard == deploymentTrackCard) { + return deploymentTrackCard.getParams(exportOptions); + } + return false; + } + + @Override + public void cancelButtonPressed() { + this.exportOptions = null; + } + +} diff --git a/src/tethys/deployment/swing/ProjectInformationPanel.java b/src/tethys/deployment/swing/ProjectInformationPanel.java new file mode 100644 index 00000000..dea88590 --- /dev/null +++ b/src/tethys/deployment/swing/ProjectInformationPanel.java @@ -0,0 +1,219 @@ +package tethys.deployment.swing; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Point; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JTextField; +import javax.swing.border.TitledBorder; + +import PamController.PamController; +import PamView.dialog.PamGridBagContraints; +import metadata.PamguardMetaData; +import nilus.Deployment; +import tethys.TethysControl; +import tethys.swing.NewProjectDialog; +import tethys.swing.SelectProjectDialog; + +/** + * Panel for entering project information + * @author dg50 + * + */ +public class ProjectInformationPanel { + + private JPanel projectPanel; + + private JTextField project, site, cruise, region; + + private JButton newProject, selectProject; + + private TethysControl tethysControl; + + private Deployment deployment; + + private Window owner; + + public ProjectInformationPanel(Window owner, String title) { + super(); + this.owner = owner; + + tethysControl = (TethysControl) PamController.getInstance().findControlledUnit(TethysControl.unitType); + + int txtWidth = 1; + if (tethysControl != null) { + txtWidth = 3; + } + projectPanel = new JPanel(new GridBagLayout()); + if (title != null) { + projectPanel.setBorder(new TitledBorder(title)); + } + GridBagConstraints c = new PamGridBagContraints(); + projectPanel.add(new JLabel("Project Name ", JLabel.RIGHT), c); + c.gridx++; + projectPanel.add(project = new JTextField(30), c); + if (tethysControl != null) { + c.gridx++; + projectPanel.add(selectProject = new JButton("Select")); + c.gridx++; + projectPanel.add(newProject = new JButton("New")); + } + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + projectPanel.add(new JLabel("Region ", JLabel.RIGHT), c); + c.gridx++; + c.gridwidth = txtWidth; + projectPanel.add(region = new JTextField(20), c); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + projectPanel.add(new JLabel("Cruise name ", JLabel.RIGHT), c); + c.gridx++; + c.gridwidth = txtWidth; + projectPanel.add(cruise = new JTextField(40), c); + c.gridx = 0; + c.gridy++; + c.gridwidth = 1; + projectPanel.add(new JLabel("Site ", JLabel.RIGHT), c); + c.gridx++; + c.gridwidth = txtWidth; + projectPanel.add(site = new JTextField(20), c); + c.gridx = 0; + c.gridy++; + + + if (newProject != null) { + newProject.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selNewProject(e); + } + }); + } + if (selectProject != null) { + selectProject.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selProjectPressed(e); + } + }); + } + + } + + /** + * @return the mainPanel + */ + public JPanel getMainPanel() { + return projectPanel; + } + + public void setParams(Deployment deployment) { + this.deployment = deployment; + cruise.setText(deployment.getCruise()); + region.setText(deployment.getRegion()); + site.setText(deployment.getSite()); + project.setText(deployment.getProject()); + } + + public boolean getParams(Deployment deployment) { + deployment.setCruise(cruise.getText()); + deployment.setRegion(region.getText()); + deployment.setSite(site.getText()); + deployment.setProject(project.getText()); + return true; + } + /** + * Select a new project, uses a dialog from Tethys. Only enabled + * when the tethys database is present to allow this. + * @param e + */ + protected void selNewProject(ActionEvent e) { + if (tethysControl == null) { + return; + } + getParams(deployment); + Deployment newDeployment = NewProjectDialog.showDialog(owner, tethysControl, deployment); + if (newDeployment != null) { + deployment.setProject(newDeployment.getProject()); + deployment.setRegion(newDeployment.getRegion()); + } + setParams(deployment); + } + + protected void selProjectPressed(ActionEvent e) { + if (tethysControl == null) { + return; + } + getParams(deployment); + // will this be fast enough, or do we need to get Tethys to hold this list in memory ? + ArrayList projectNames = tethysControl.getDbxmlQueries().getProjectNames(); + if (projectNames.size() < 12) { + showAsMenu(projectNames); + } + else { + showAsDialog(projectNames); + } + + } + + private void showAsDialog(ArrayList projectNames) { + Point p = selectProject.getLocationOnScreen(); + String selName = SelectProjectDialog.showDialog(owner, projectNames, project.getText(), p); + if (selName != null) { + project.setText(selName); + } + } + + private void showAsMenu(ArrayList projectNames) { + String currentName = project.getText(); + JPopupMenu popMenu = new JPopupMenu(); + JMenuItem menuItem; + if (currentName != null && currentName.length()>0) { + addProjMenuItem(popMenu, currentName); + } + for (String projName : projectNames) { + if (projName.equals(currentName)) { + continue; + } + addProjMenuItem(popMenu, projName); + } + + popMenu.show(selectProject, selectProject.getWidth()/2, selectProject.getHeight()/2); + } + + private void addProjMenuItem(JPopupMenu popMenu, String projectName) { + JMenuItem menuItem = new JMenuItem(projectName); + menuItem.addActionListener(new SelectProject(projectName)); + popMenu.add(menuItem); + } + + private class SelectProject implements ActionListener { + + private String projectName; + + /** + * @param projectName + */ + public SelectProject(String projectName) { + this.projectName = projectName; + } + + @Override + public void actionPerformed(ActionEvent e) { + project.setText(projectName); + } + + } + +} diff --git a/src/tethys/deployment/swing/RecordingGapDialog.java b/src/tethys/deployment/swing/RecordingGapDialog.java new file mode 100644 index 00000000..6012d97d --- /dev/null +++ b/src/tethys/deployment/swing/RecordingGapDialog.java @@ -0,0 +1,85 @@ +package tethys.deployment.swing; + +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.PamDialog; +import PamView.dialog.PamGridBagContraints; +import tethys.deployment.DeploymentExportOpts; + +public class RecordingGapDialog extends PamDialog { + + private JTextField maxGap, minLength; + + private DeploymentExportOpts exportOpts; + + private RecordingGapDialog(Window parentFrame) { + super(parentFrame, "Maximum Gap", true); + JPanel mainPanel = new JPanel(new GridBagLayout()); + mainPanel.setBorder(new TitledBorder("Max recording gap")); + PamGridBagContraints c = new PamGridBagContraints(); + mainPanel.add(new JLabel("Maximum gap ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(maxGap = new JTextField(3), c); + c.gridx++; + mainPanel.add(new JLabel(" seconds", JLabel.RIGHT), c); + c.gridx = 0; + c.gridy++; + mainPanel.add(new JLabel("Minimum length ", JLabel.RIGHT), c); + c.gridx++; + mainPanel.add(minLength = new JTextField(3), c); + c.gridx++; + mainPanel.add(new JLabel(" seconds", JLabel.RIGHT), c); + + maxGap.setToolTipText("Maximum gap between recording periods. Periods with a gap less than this will be counted as one"); + minLength.setToolTipText("Minimum recording length. Recording sections shorter than this will be ignored"); + + setDialogComponent(mainPanel); + } + + public static DeploymentExportOpts showDiloag(Window parent, DeploymentExportOpts exportOpts) { + RecordingGapDialog dialog = new RecordingGapDialog(parent); + dialog.setParams(exportOpts); + dialog.setVisible(true); + return dialog.exportOpts; + } + + private void setParams(DeploymentExportOpts exportOpts) { + this.exportOpts = exportOpts; + maxGap.setText(String.format("%d", exportOpts.maxGapSeconds)); + minLength.setText(String.format("%d", exportOpts.minLengthSeconds)); + } + + @Override + public boolean getParams() { + try { + exportOpts.maxGapSeconds = Integer.valueOf(maxGap.getText()); + } + catch (NumberFormatException e) { + return showWarning("Invalid inter recording interval"); + } + try { + exportOpts.minLengthSeconds = Integer.valueOf(minLength.getText()); + } + catch (NumberFormatException e) { + return showWarning("Invalid minimum recording length"); + } + return true; + } + + @Override + public void cancelButtonPressed() { + exportOpts = null; + } + + @Override + public void restoreDefaultSettings() { + DeploymentExportOpts defaults = new DeploymentExportOpts(); + } + +} diff --git a/src/tethys/niluswraps/NilusUnpacker.java b/src/tethys/niluswraps/NilusUnpacker.java index 5bf8464f..f5ad8f6b 100644 --- a/src/tethys/niluswraps/NilusUnpacker.java +++ b/src/tethys/niluswraps/NilusUnpacker.java @@ -185,8 +185,10 @@ public class NilusUnpacker { // find a setter for it. Method setter = findSetter(nilusClass, fieldName); // System.out.printf("Field %s with element %s and setter %s\n", fieldName, childName, setter); - if (setter == null & verbose) { - System.out.printf("No setter available for field %s and element %s\n", fieldName, elementName); + if (setter == null) { + if (verbose) { + System.out.printf("No setter available for field %s and element %s\n", fieldName, elementName); + } continue; // eventually do something more intelligent here. } Parameter[] params = setter.getParameters(); diff --git a/src/tethys/swing/DeploymentExportPanel.java b/src/tethys/swing/DeploymentExportPanel.java index 196a4045..21e42daf 100644 --- a/src/tethys/swing/DeploymentExportPanel.java +++ b/src/tethys/swing/DeploymentExportPanel.java @@ -231,7 +231,6 @@ public class DeploymentExportPanel extends TethysGUIPanel implements DeploymentT @Override public void selectionChanged() { - selectedDeployments = pamDeploymentsTable.getSelectedDeployments(); enableControls(); } diff --git a/src/tethys/swing/DeploymentTableObserver.java b/src/tethys/swing/DeploymentTableObserver.java index 1eda9db4..9fc70973 100644 --- a/src/tethys/swing/DeploymentTableObserver.java +++ b/src/tethys/swing/DeploymentTableObserver.java @@ -1,5 +1,6 @@ package tethys.swing; + public interface DeploymentTableObserver { public void selectionChanged(); diff --git a/src/tethys/swing/DeploymentsPanel.java b/src/tethys/swing/DeploymentsPanel.java index c41d7465..520eafdf 100644 --- a/src/tethys/swing/DeploymentsPanel.java +++ b/src/tethys/swing/DeploymentsPanel.java @@ -1,33 +1,46 @@ package tethys.swing; import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import javax.swing.BoxLayout; +import javax.swing.JButton; import javax.swing.JComponent; +import javax.swing.JLabel; import javax.swing.JPanel; -import javax.swing.JSplitPane; -import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; import PamView.panel.PamPanel; import tethys.TethysControl; +import tethys.deployment.DeploymentHandler; +import tethys.deployment.RecordingPeriod; -public class DeploymentsPanel extends TethysGUIPanel { +public class DeploymentsPanel extends TethysGUIPanel implements DeploymentTableObserver { private JPanel mainPanel; private PAMGuardDeploymentsTable pamDeploymentsTable; - DeploymentExportPanel exportPanel; + private DeploymentExportPanel exportPanel; + + private JButton exportButton, optionsButton; // private TethysDeploymentsTable tethysDeploymentsTable; + private JLabel exportWarning; public DeploymentsPanel(TethysControl tethysControl) { super(tethysControl); + DeploymentHandler deploymentHandler = tethysControl.getDeploymentHandler(); pamDeploymentsTable = new PAMGuardDeploymentsTable(tethysControl); exportPanel = new DeploymentExportPanel(tethysControl, pamDeploymentsTable); pamDeploymentsTable.addObserver(exportPanel); // tethysDeploymentsTable = new TethysDeploymentsTable(tethysControl); mainPanel = new PamPanel(new BorderLayout()); mainPanel.setBorder(new TitledBorder("Deployment information")); + pamDeploymentsTable.addObserver(this); + pamDeploymentsTable.addObserver(deploymentHandler); // JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); // splitPane.add(pamDeploymentsTable.getComponent()); // splitPane.add(tethysDeploymentsTable.getComponent()); @@ -39,8 +52,39 @@ public class DeploymentsPanel extends TethysGUIPanel { // splitPane.setDividerLocation(0.6); // } // }); + JPanel ctrlPanel = new PamPanel(new BorderLayout()); + JPanel ctrlButtons = new JPanel(); + ctrlButtons.setLayout(new BoxLayout(ctrlButtons, BoxLayout.X_AXIS)); + optionsButton = new JButton("Options ..."); + exportButton = new JButton("Export ..."); + ctrlButtons.add(optionsButton); + ctrlButtons.add(exportButton); + ctrlPanel.add(BorderLayout.WEST, ctrlButtons); + + optionsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + getTethysControl().getDeploymentHandler().showOptions(null); + } + }); + + exportButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportDeployments(); + } + }); + exportWarning = new JLabel(" "); + ctrlPanel.add(BorderLayout.CENTER, exportWarning); + mainPanel.add(BorderLayout.CENTER, pamDeploymentsTable.getComponent()); - mainPanel.add(BorderLayout.EAST, exportPanel.getComponent()); + mainPanel.add(BorderLayout.NORTH, ctrlPanel); +// mainPanel.add(BorderLayout.EAST, exportPanel.getComponent()); + exportButton.setEnabled(false); + } + + protected void exportDeployments() { + getTethysControl().getDeploymentHandler().exportDeployments(); } @Override @@ -48,6 +92,30 @@ public class DeploymentsPanel extends TethysGUIPanel { return mainPanel; } + @Override + public void selectionChanged() { + enableExportButton(); + } + + private void enableExportButton() { + ArrayList selected = pamDeploymentsTable.getSelectedPeriods(); + // and see if any warnings are needed: basically if anything selected has an output. + boolean existing = false; + for (RecordingPeriod aPeriod: selected) { + if (aPeriod.getMatchedTethysDeployment() != null) { + existing = true; + break; + } + } + String warning = null; + if (existing) { + warning = " One or more deployment documents already exist. These must be deleted prior to exporting new documents"; + exportWarning.setText(warning); + } + + exportButton.setEnabled(selected.size()>0 & existing == false); + } + } diff --git a/src/tethys/swing/NewProjectDialog.java b/src/tethys/swing/NewProjectDialog.java index 68dd272b..9c4df2ba 100644 --- a/src/tethys/swing/NewProjectDialog.java +++ b/src/tethys/swing/NewProjectDialog.java @@ -24,7 +24,7 @@ public class NewProjectDialog extends PamView.dialog.PamDialog { private JTextField projectRegion; - private PamguardMetaData metaData; + private Deployment deployment; private NewProjectDialog(Window parentFrame, TethysControl tethysControl) { super(parentFrame, "New Project", false); @@ -46,30 +46,29 @@ public class NewProjectDialog extends PamView.dialog.PamDialog { setDialogComponent(mainPanel); } - public static PamguardMetaData showDialog(Window parent, TethysControl tethysControl, PamguardMetaData metaData) { + public static Deployment showDialog(Window parent, TethysControl tethysControl, Deployment deployment) { if (singleInstance == null) { singleInstance = new NewProjectDialog(parent, tethysControl); } - singleInstance.setParams(metaData); + singleInstance.setParams(deployment); singleInstance.setVisible(true); - return singleInstance.metaData; + return singleInstance.deployment; } - private void setParams(PamguardMetaData deploymentData) { + private void setParams(Deployment deploymentData) { if (deploymentData == null) { return; } - this.metaData = deploymentData; - projectName.setText(deploymentData.getDeployment().getProject()); - projectRegion.setText(deploymentData.getDeployment().getRegion()); + this.deployment = deploymentData; + projectName.setText(deploymentData.getProject()); + projectRegion.setText(deploymentData.getRegion()); } @Override public boolean getParams() { - if (metaData == null) { + if (deployment == null) { return false; } - Deployment deployment = metaData.getDeployment(); deployment.setProject(projectName.getText()); deployment.setRegion(projectRegion.getText()); if (deployment.getProject() == null || deployment.getProject().length() == 0) { @@ -81,7 +80,7 @@ public class NewProjectDialog extends PamView.dialog.PamDialog { @Override public void cancelButtonPressed() { - metaData = null; + deployment = null; } @Override diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 33b5dfa9..7299f733 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -51,7 +51,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { private DeploymentOverview deploymentOverview; - private boolean[] selection = new boolean[0]; +// private boolean[] selection = new boolean[0]; private ArrayList observers = new ArrayList<>(); @@ -95,16 +95,15 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { public void mouseClicked(MouseEvent e) { int aRow = table.getSelectedRow(); int col = table.getSelectedColumn(); - if (aRow >= 0 && aRow < selection.length && col == TableModel.SELECTCOLUMN) { - selection[aRow] = !selection[aRow]; - for (DeploymentTableObserver obs : observers) { - obs.selectionChanged(); - } + ArrayList periods = deploymentOverview.getRecordingPeriods(); + if (aRow >= 0 && aRow < periods.size() && col == TableModel.SELECTCOLUMN) { + periods.get(aRow).toggleSelected(); + notifyObservers(); } } } - + public void showPopup(MouseEvent e) { int aRow = table.getSelectedRow(); int[] selRows = table.getSelectedRows(); @@ -132,9 +131,31 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } } } + JPopupMenu popMenu = new JPopupMenu(); + + JMenuItem menuItem = new JMenuItem("Select all"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectAll(true); + } + }); + popMenu.add(menuItem); + menuItem = new JMenuItem("Select none"); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectAll(false); + } + }); + popMenu.add(menuItem); + + if (matchedDeployments.size() > 0) { + popMenu.addSeparator(); + } + if (matchedDeployments.size() == 1) { - JPopupMenu popMenu = new JPopupMenu(); - JMenuItem menuItem = new JMenuItem("Delete deployment document " + matchedDeployments.get(0)); + menuItem = new JMenuItem("Delete deployment document " + matchedDeployments.get(0)); menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -160,19 +181,55 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { popMenu.add(menuItem); - popMenu.show(e.getComponent(), e.getX(), e.getY()); } -// 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. -// */ - + else if (matchedDeployments.size() > 1){ + menuItem = new JMenuItem(String.format("Delete %d deployment documents", matchedDeployments.size())); + menuItem.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + deleteMultipleDeployments(matchedDeployments); + } + }); + popMenu.add(menuItem); + } + + popMenu.show(e.getComponent(), e.getX(), e.getY()); + } + protected void selectAll(boolean select) { + ArrayList recordingPeriods = deploymentOverview.getRecordingPeriods(); + for (int i = 0; i < recordingPeriods.size(); i++) { + recordingPeriods.get(i).setSelected(select); + } + + tableModel.fireTableDataChanged(); + + notifyObservers(); + + } + + protected void deleteMultipleDeployments(ArrayList matchedDeployments) { + int ans = WarnOnce.showWarning(getTethysControl().getGuiFrame(), "Delete Deployment document", + "Are you sure you want to delete multiple deployment documents ", WarnOnce.OK_CANCEL_OPTION); + if (ans == WarnOnce.CANCEL_OPTION) { + return; + } + for (PDeployment depl : matchedDeployments) { + if (depl.deployment == null) { + continue; + } + try { + boolean gone = getTethysControl().getDbxmlConnect().deleteDocument(depl.deployment); + } catch (TethysException e) { + getTethysControl().showException(e); + } + } + getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); + } + protected void exportDeployment(PDeployment pDeployment) { getTethysControl().exportDocument(Collection.Deployments.collectionName(), pDeployment.deployment.getId()); } @@ -199,24 +256,6 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { getTethysControl().sendStateUpdate(new TethysState(StateType.UPDATESERVER, Collection.Deployments)); } - /** - * Get a list of selected recording periods. - * @return list of selected periods. - */ - public ArrayList getSelectedDeployments() { - if (deploymentOverview == null) { - return null; - } - ArrayList selDeps = new ArrayList<>(); - int n = Math.min(selection.length, deploymentOverview.getRecordingPeriods().size()); - for (int i = 0; i < n; i++) { - if (selection[i]) { - selDeps.add(deploymentOverview.getRecordingPeriods().get(i)); - } - } - return selDeps; - } - @Override public void updateState(TethysState tethysState) { switch(tethysState.stateType) { @@ -242,21 +281,33 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { } } + /** + * Get a list of selected periods irrespective of whether they have an existing deployment document. + * @return + */ + public ArrayList getSelectedPeriods() { + ArrayList allPeriods = deploymentOverview.getRecordingPeriods(); + ArrayList selPeriods = new ArrayList(); + int n = allPeriods.size(); + for (int i = 0; i < n; i++) { + if (allPeriods.get(i).isSelected()) { + selPeriods.add(allPeriods.get(i)); + } + } + return selPeriods; + } + private void notifyObservers() { + for (DeploymentTableObserver obs : observers) { + obs.selectionChanged(); + } + } + private void updateDeployments() { DeploymentHandler deploymentHandler = getTethysControl().getDeploymentHandler(); deploymentOverview = deploymentHandler.getDeploymentOverview(); if (deploymentOverview == null) { return; } - int n = deploymentOverview.getRecordingPeriods().size(); - if (selection.length < n) { - selection = Arrays.copyOf(selection, n); -// for (int i = 0; i < setDefaultStores.length; i++) { -// if (selectBoxes[i] == null) { -// selectBoxes[i] = new JCheckBox(); -// } -// } - } tableModel.fireTableDataChanged(); // DeploymentData deplData = getTethysControl().getGlobalDeplopymentData(); // ArrayList projectDeployments = getTethysControl().getDbxmlQueries().getProjectDeployments(deplData.getProject()); @@ -269,9 +320,9 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { private class TableModel extends AbstractTableModel { - private String[] columnNames = {"Id", "Start", "Stop", "Gap", "Duration", "Cycle", "Tethys Deployment", "Select"}; + private String[] columnNames = {"Id", "Select", "Start", "Stop", "Gap", "Duration", "Cycle", "Tethys Deployment"}; - private static final int SELECTCOLUMN = 7; + private static final int SELECTCOLUMN = 1; @Override public int getRowCount() { @@ -306,10 +357,10 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { public Object getValueAt(int rowIndex, int columnIndex) { RecordingPeriod period = deploymentOverview.getRecordingPeriods().get(rowIndex); // DeploymentRecoveryPair deplInfo = deploymentInfo.get(rowIndex); - if (columnIndex == 5) { + if (columnIndex == 6) { return deploymentOverview.getDutyCycleInfo(); } - if (columnIndex == 3 && rowIndex > 0) { + if (columnIndex == 4 && rowIndex > 0) { RecordingPeriod prevPeriod = deploymentOverview.getRecordingPeriods().get(rowIndex-1); long gap = period.getRecordStart() - prevPeriod.getRecordStop(); return PamCalendar.formatDuration(gap); @@ -321,22 +372,22 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { switch (columnIndex) { case 0: return rowIndex; - case 1: + case 2: return PamCalendar.formatDBDateTime(period.getRecordStart()); // return TethysTimeFuncs.formatGregorianTime(deplInfo.deploymentDetails.getAudioTimeStamp()); - case 2: + case 3: return PamCalendar.formatDBDateTime(period.getRecordStop()); // return TethysTimeFuncs.formatGregorianTime(deplInfo.recoveryDetails.getAudioTimeStamp()); - case 4: + case 5: // long t1 = TethysTimeFuncs.millisFromGregorianXML(deplInfo.deploymentDetails.getAudioTimeStamp()); // long t2 = TethysTimeFuncs.millisFromGregorianXML(deplInfo.recoveryDetails.getAudioTimeStamp()); return PamCalendar.formatDuration(period.getRecordStop()-period.getRecordStart()); - case 6: + case 7: PDeployment deployment = period.getMatchedTethysDeployment(); return makeDeplString(period, deployment); case SELECTCOLUMN: // return selectBoxes[rowIndex]; - return selection[rowIndex]; + return period.isSelected(); } return null; @@ -357,7 +408,8 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { long start = period.getRecordStart(); long stop = period.getRecordStop(); double percOverlap = (overlap*100.) / (stop-start); - return String.format("%s : %3.1f%% overlap", deployment.toString(), percOverlap); +// return String.format("%s : %3.1f%% overlap", deployment.toString(), percOverlap); + return deployment.toString(); } } diff --git a/src/tethys/swing/SelectProjectDialog.java b/src/tethys/swing/SelectProjectDialog.java new file mode 100644 index 00000000..508ecaa3 --- /dev/null +++ b/src/tethys/swing/SelectProjectDialog.java @@ -0,0 +1,81 @@ +package tethys.swing; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Point; +import java.awt.Window; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +import PamView.dialog.PamDialog; +import PamView.dialog.PamGridBagContraints; +import tethys.TethysControl; + +public class SelectProjectDialog extends PamDialog { + + private String project; + + private JComboBox comboBox; + + private SelectProjectDialog(Window parentFrame, List projects, String topOne) { + super(parentFrame, "Projects", topOne != null & topOne.length() > 0); + this.project = topOne; + + comboBox = new JComboBox(); + JPanel mainPanel = new JPanel(new BorderLayout()); +// GridBagConstraints c = new PamGridBagContraints(); + mainPanel.setBorder(new TitledBorder("Project names")); + mainPanel.add(comboBox, BorderLayout.CENTER); + + if (project != null) { + comboBox.addItem(topOne); + } + for (String name : projects) { + comboBox.addItem(name); + } + + setDialogComponent(mainPanel); + } + + public static String showDialog(Window parentFrame, TethysControl tethysControl, String topOne) { + ArrayList projects = tethysControl.getDbxmlQueries().getProjectNames(); + return showDialog(parentFrame, projects, topOne, null); + } + + public static String showDialog(Window parentFrame, List projects, String topOne, Point point) { + if (topOne != null & topOne.length() == 0) { + topOne = null; + } + SelectProjectDialog dialog = new SelectProjectDialog(parentFrame, projects, topOne); + if (point != null) { + dialog.setLocation(point); + } + dialog.setVisible(true); + + return dialog.project; + } + + @Override + public boolean getParams() { + project = (String) comboBox.getSelectedItem(); + return (project != null & project.length()>0); + } + + @Override + public void cancelButtonPressed() { + project = null; + } + + @Override + public void restoreDefaultSettings() { + if (project != null) { + comboBox.setSelectedItem(project); + } + } + +} diff --git a/src/tethys/swing/TethysConnectionPanel.java b/src/tethys/swing/TethysConnectionPanel.java index f2491a13..1561af87 100644 --- a/src/tethys/swing/TethysConnectionPanel.java +++ b/src/tethys/swing/TethysConnectionPanel.java @@ -187,8 +187,9 @@ public class TethysConnectionPanel extends TethysGUIPanel { */ protected void createNewProject() { PamguardMetaData pamDeploymentData = MetaDataContol.getMetaDataControl().getMetaData(); - pamDeploymentData = NewProjectDialog.showDialog(getTethysControl().getGuiFrame(), getTethysControl(), pamDeploymentData); - if (pamDeploymentData != null) { + Deployment newDep = NewProjectDialog.showDialog(getTethysControl().getGuiFrame(), getTethysControl(), pamDeploymentData.getDeployment()); + if (newDep != null) { +// oldDep MetaDataContol.getMetaDataControl().setMetaData(pamDeploymentData); updateProjectList(); } @@ -265,7 +266,9 @@ public class TethysConnectionPanel extends TethysGUIPanel { fillServerControl(); updateProjectList(); } - + if (tethysState.stateType == StateType.NEWPROJECTSELECTION) { + updateProjectList(); + } } private void updateProjectList() { diff --git a/src/tethys/swing/TethysMainPanel.java b/src/tethys/swing/TethysMainPanel.java index 2e62c921..c7f0ada0 100644 --- a/src/tethys/swing/TethysMainPanel.java +++ b/src/tethys/swing/TethysMainPanel.java @@ -53,10 +53,12 @@ public class TethysMainPanel extends TethysGUIPanel { // splitPane.set mainPanel.add(BorderLayout.CENTER, splitPane); // mainPanel.add(BorderLayout.CENTER, datablockSynchPanel.getComponent()); - JPanel splitNorth = new JPanel(new BorderLayout()); - splitNorth.add(BorderLayout.WEST, calibrationPanel.getComponent()); - splitNorth.add(deploymentsPanel.getComponent()); - splitPane.add(splitNorth); +// JPanel splitNorth = new JPanel(new BorderLayout()); + JSplitPane northSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + northSplit.add(calibrationPanel.getComponent()); + northSplit.add(deploymentsPanel.getComponent()); + + splitPane.add(northSplit); southwestSplit.add(datablockSynchPanel.getComponent()); southwestSplit.add(southEastPanel); southEastPanel.add(datablockDetectionsPanel.getComponent(), BorderLayout.CENTER); @@ -68,6 +70,7 @@ public class TethysMainPanel extends TethysGUIPanel { public void run() { splitPane.setDividerLocation(0.5); southwestSplit.setDividerLocation(0.5); + northSplit.setDividerLocation(0.27); } }); } diff --git a/src/tethys/swing/XMLStringView.java b/src/tethys/swing/XMLStringView.java index 0d871c86..be476f91 100644 --- a/src/tethys/swing/XMLStringView.java +++ b/src/tethys/swing/XMLStringView.java @@ -23,6 +23,8 @@ public class XMLStringView extends PamDialog { setDialogComponent(mainPanel); setResizable(true); textArea.setText(xmlString); + textArea.setEditable(false); + textArea.setCaretPosition(0); getCancelButton().setVisible(false); } @@ -35,7 +37,6 @@ public class XMLStringView extends PamDialog { @Override public boolean getParams() { - // TODO Auto-generated method stub return false; } diff --git a/src/tethys/swing/export/DescriptionCard.java b/src/tethys/swing/export/DescriptionCard.java index cc25b2cf..edc645d0 100644 --- a/src/tethys/swing/export/DescriptionCard.java +++ b/src/tethys/swing/export/DescriptionCard.java @@ -2,29 +2,40 @@ package tethys.swing.export; import java.awt.BorderLayout; +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; import PamguardMVC.PamDataBlock; +import nilus.DescriptionType; import tethys.TethysControl; import tethys.output.StreamExportParams; -public class DescriptionCard extends ExportWizardCard { +public class DescriptionCard extends PamWizardCard { private DescriptionTypePanel descriptionPanel; - public DescriptionCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { - super(tethysControl, detectionsExportWizard, "Description", dataBlock); + public DescriptionCard(PamWizard detectionsExportWizard, TethysControl tethysControl) { + super(detectionsExportWizard, "Description"); this.setLayout(new BorderLayout()); descriptionPanel = new DescriptionTypePanel("Description data", true, true, true); this.add(BorderLayout.CENTER, descriptionPanel.getMainPanel()); } @Override + public boolean getParams(DescriptionType description) { + return descriptionPanel.getParams(description); + } + public boolean getParams(StreamExportParams streamExportParams) { - return descriptionPanel.getParams(streamExportParams.getDetectionDescription().getDescription()); + return descriptionPanel.getParams(streamExportParams.getNilusDetectionDescription()); } @Override + public void setParams(DescriptionType description) { + descriptionPanel.setParams(description); + } + public void setParams(StreamExportParams streamExportParams) { - descriptionPanel.setParams(streamExportParams.getDetectionDescription().getDescription()); + descriptionPanel.setParams(streamExportParams.getNilusDetectionDescription()); } } diff --git a/src/tethys/swing/export/DetectionsExportWizard.java b/src/tethys/swing/export/DetectionsExportWizard.java index 3955a69f..32d70607 100644 --- a/src/tethys/swing/export/DetectionsExportWizard.java +++ b/src/tethys/swing/export/DetectionsExportWizard.java @@ -47,7 +47,7 @@ public class DetectionsExportWizard extends PamWizard { addCard(algorithmCard = new AlgorithmCard(this, tethysControl, dataBlock)); addCard(granularityCard = new GranularityCard(this, tethysControl, dataBlock)); - addCard(descriptionCard = new DescriptionCard(this, tethysControl, dataBlock)); + addCard(descriptionCard = new DescriptionCard(this, tethysControl)); addCard(exportWorkerCard = new ExportWorkerCard(this, tethysControl, dataBlock)); moveFirst(); diff --git a/src/tethys/swing/export/ResponsiblePartyCard.java b/src/tethys/swing/export/ResponsiblePartyCard.java new file mode 100644 index 00000000..c923c542 --- /dev/null +++ b/src/tethys/swing/export/ResponsiblePartyCard.java @@ -0,0 +1,31 @@ +package tethys.swing.export; + +import java.awt.BorderLayout; + +import PamView.panel.PamNorthPanel; +import PamView.wizard.PamWizard; +import PamView.wizard.PamWizardCard; +import nilus.ResponsibleParty; + +public class ResponsiblePartyCard extends PamWizardCard { + + private ResponsiblePartyPanel responsiblePartyPanel; + + public ResponsiblePartyCard(PamWizard pamWizard, String title) { + super(pamWizard, title); + responsiblePartyPanel = new ResponsiblePartyPanel("Responsible Party"); + this.setLayout(new BorderLayout()); + this.add(BorderLayout.CENTER, new PamNorthPanel(responsiblePartyPanel.getMainPanel())); + } + + @Override + public boolean getParams(ResponsibleParty cardParams) { + return responsiblePartyPanel.getParams(cardParams); + } + + @Override + public void setParams(ResponsibleParty cardParams) { + responsiblePartyPanel.setParams(cardParams); + } + +} From 02ad66db3b0dab92886df4199822b7dfca13e5f7 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:33:46 +0000 Subject: [PATCH 64/65] User input viewer bug PAMGuard was marking multiline comments as changed and then relogging them. Led to exponential increase in database size! --- src/UserInput/UserInputLogger.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/UserInput/UserInputLogger.java b/src/UserInput/UserInputLogger.java index a3b5f57c..31eb53a8 100644 --- a/src/UserInput/UserInputLogger.java +++ b/src/UserInput/UserInputLogger.java @@ -136,7 +136,9 @@ public class UserInputLogger extends SQLLogging { if (dataUnit != null && dataUnit.getDatabaseIndex() != databaseIndex) { dataUnit.setDatabaseIndex(databaseIndex); dataUnit.setUserString(dataUnit.getUserString() + " " + txt); - getPamDataBlock().updatePamData(dataUnit, timeMilliseconds); + // don't call this next line, it causes the unit to get relogged. +// getPamDataBlock().updatePamData(dataUnit, timeMilliseconds); + dataUnit.clearUpdateCount(); } else { dataUnit = new UserInputDataUnit(timeMilliseconds, txt); From 878137468563c855f05623f77664bca157613800 Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Thu, 14 Dec 2023 11:57:44 +0000 Subject: [PATCH 65/65] Small changes to deployment table --- .project | 2 +- src/PamController/PamController.java | 1 + src/tethys/swing/DeploymentsPanel.java | 2 +- src/tethys/swing/PAMGuardDeploymentsTable.java | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.project b/.project index 9ee4af40..48129c11 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - PamGuard Main DG + PamGuard Main Tethys diff --git a/src/PamController/PamController.java b/src/PamController/PamController.java index 56d4f6a9..2180b084 100644 --- a/src/PamController/PamController.java +++ b/src/PamController/PamController.java @@ -1444,6 +1444,7 @@ public class PamController implements PamControllerInterface, PamSettings { */ private void saveEndSettings(long timeNow) { // System.out.printf("Updating settings with end time %s\n", PamCalendar.formatDBDateTime(timeNow)); + ArrayList pamControlledUnits = pamConfiguration.getPamControlledUnits(); PamControlledUnit pcu; PamSettingsSource settingsSource; for (int iU = 0; iU < pamControlledUnits.size(); iU++) { diff --git a/src/tethys/swing/DeploymentsPanel.java b/src/tethys/swing/DeploymentsPanel.java index 520eafdf..37d35a38 100644 --- a/src/tethys/swing/DeploymentsPanel.java +++ b/src/tethys/swing/DeploymentsPanel.java @@ -38,7 +38,7 @@ public class DeploymentsPanel extends TethysGUIPanel implements DeploymentTableO pamDeploymentsTable.addObserver(exportPanel); // tethysDeploymentsTable = new TethysDeploymentsTable(tethysControl); mainPanel = new PamPanel(new BorderLayout()); - mainPanel.setBorder(new TitledBorder("Deployment information")); + mainPanel.setBorder(new TitledBorder("Recording periods and deployment information")); pamDeploymentsTable.addObserver(this); pamDeploymentsTable.addObserver(deploymentHandler); // JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); diff --git a/src/tethys/swing/PAMGuardDeploymentsTable.java b/src/tethys/swing/PAMGuardDeploymentsTable.java index 7299f733..2aec93d2 100644 --- a/src/tethys/swing/PAMGuardDeploymentsTable.java +++ b/src/tethys/swing/PAMGuardDeploymentsTable.java @@ -59,7 +59,7 @@ public class PAMGuardDeploymentsTable extends TethysGUIPanel { super(tethysControl); // deploymentHandler = new DeploymentHandler(getTethysControl()); mainPanel = new PamPanel(new BorderLayout()); - mainPanel.setBorder(new TitledBorder("PAMGuard recording periods")); +// mainPanel.setBorder(new TitledBorder("PAMGuard recording periods")); tableModel = new TableModel(); table = new JTable(tableModel); // table.setRowSelectionAllowed(true);