diff --git a/.classpath b/.classpath index 49faf461..053df83d 100644 --- a/.classpath +++ b/.classpath @@ -5,7 +5,6 @@ - diff --git a/pom.xml b/pom.xml index dd3572a4..d09a31fd 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.pamguard Pamguard - 2.02.11d + 2.02.11e Pamguard Pamguard using Maven to control dependencies www.pamguard.org @@ -579,7 +579,7 @@ com.fazecast jSerialComm - 2.5.3 + 2.11.0 diff --git a/src/Acquisition/FileInputSystem.java b/src/Acquisition/FileInputSystem.java index 33779534..b4678913 100644 --- a/src/Acquisition/FileInputSystem.java +++ b/src/Acquisition/FileInputSystem.java @@ -15,6 +15,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.Serializable; import java.text.DateFormat; +import java.util.Arrays; import java.util.Calendar; import java.util.List; @@ -1017,9 +1018,11 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe newDataUnit = new RawDataUnit(ms, 1 << ichan, totalSamples, newSamples); newDataUnit.setRawData(doubleData[ichan]); - if (1000*(readFileSamples/sampleRate)>=fileInputParameters.skipStartFileTime) { - newDataUnits.addNewData(newDataUnit); + if (1000*(readFileSamples/sampleRate) bs = findControlledUnits(BinaryStore.class); + for (PamControlledUnit aBS : bs) { + BinaryStore binStore = (BinaryStore) aBS; + binStore.getBinaryStoreProcess().checkFileTime(timeInMillis); + } + } + } diff --git a/src/PamController/PamguardVersionInfo.java b/src/PamController/PamguardVersionInfo.java index b40c4afa..d1cee5f8 100644 --- a/src/PamController/PamguardVersionInfo.java +++ b/src/PamController/PamguardVersionInfo.java @@ -31,12 +31,12 @@ public class PamguardVersionInfo { * Version number, major version.minorversion.sub-release. * Note: can't go higher than sub-release 'f' */ - static public final String version = "2.02.11d"; + static public final String version = "2.02.11e"; /** * Release date */ - static public final String date = "30 May 2024"; + static public final String date = "19 June 2024"; // /** // * Release type - Beta or Core diff --git a/src/PamController/fileprocessing/ReprocessChoiceDialog.java b/src/PamController/fileprocessing/ReprocessChoiceDialog.java index f2b9eee5..985fbb99 100644 --- a/src/PamController/fileprocessing/ReprocessChoiceDialog.java +++ b/src/PamController/fileprocessing/ReprocessChoiceDialog.java @@ -4,6 +4,8 @@ import java.awt.BorderLayout; 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.List; import javax.swing.ButtonGroup; @@ -64,15 +66,28 @@ public class ReprocessChoiceDialog extends PamDialog { List userChoices = choiceSummary.getChoices(); choiceButtons = new JRadioButton[userChoices.size()]; ButtonGroup bg = new ButtonGroup(); + SelAction selAction = new SelAction(); for (int i = 0; i < userChoices.size(); i++) { ReprocessStoreChoice aChoice = userChoices.get(i); choiceButtons[i] = new JRadioButton(aChoice.toString()); choiceButtons[i].setToolTipText(aChoice.getToolTip()); + bg.add(choiceButtons[i]); + choiceButtons[i].addActionListener(selAction); choicePanel.add(choiceButtons[i], c); c.gridy++; } setDialogComponent(mainPanel); getCancelButton().setVisible(false); + getOkButton().setEnabled(false); + } + + private class SelAction implements ActionListener { + + @Override + public void actionPerformed(ActionEvent e) { + getOkButton().setEnabled(true); + } + } public static ReprocessStoreChoice showDialog(Window parentFrame, StoreChoiceSummary choices) { diff --git a/src/PamController/fileprocessing/ReprocessManager.java b/src/PamController/fileprocessing/ReprocessManager.java index 4f05913a..b4e8c395 100644 --- a/src/PamController/fileprocessing/ReprocessManager.java +++ b/src/PamController/fileprocessing/ReprocessManager.java @@ -62,7 +62,7 @@ public class ReprocessManager { */ boolean setupOK = setupInputStream(choiceSummary, choice); - if (choice == ReprocessStoreChoice.DONTSSTART) { + if (choice == null || choice == ReprocessStoreChoice.DONTSSTART) { return false; } @@ -172,6 +172,8 @@ public class ReprocessManager { choiceSummary.addChoice(ReprocessStoreChoice.STARTNORMAL); return choiceSummary; } + + choiceSummary.addChoice(ReprocessStoreChoice.STARTNORMAL); ArrayList outputStores = PamController.getInstance().findControlledUnits(DataOutputStore.class, true); boolean partStores = false; diff --git a/src/PamController/fileprocessing/StoreChoiceSummary.java b/src/PamController/fileprocessing/StoreChoiceSummary.java index f0f13c3a..571f7aa7 100644 --- a/src/PamController/fileprocessing/StoreChoiceSummary.java +++ b/src/PamController/fileprocessing/StoreChoiceSummary.java @@ -65,6 +65,9 @@ public class StoreChoiceSummary { * @param choice */ public void addChoice(ReprocessStoreChoice choice) { + if (choices.contains(choice)) { + return; + } choices.add(choice); } diff --git a/src/PamUtils/PamCalendar.java b/src/PamUtils/PamCalendar.java index 2e1388f9..428130d0 100644 --- a/src/PamUtils/PamCalendar.java +++ b/src/PamUtils/PamCalendar.java @@ -25,6 +25,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Locale; @@ -32,6 +33,7 @@ import java.util.TimeZone; import PamController.PamController; import PamUtils.time.CalendarControl; +import binaryFileStorage.BinaryStore; /** @@ -85,7 +87,7 @@ public class PamCalendar { * viewPositions which is the number of milliseconds from the sessionsStartTime. */ private static long viewPosition; - + /** * If files are being analysed, return the time based on the file @@ -880,14 +882,18 @@ public class PamCalendar { */ public static void setSessionStartTime(long sessionStartTime) { PamCalendar.sessionStartTime = sessionStartTime; + PamController.getInstance().updateMasterClock(getTimeInMillis()); } /** * - * @param soundFileTimeMillis The start time of a sound file + * Relative time within a sound file. This is always just added to sessionStartTime + * to give an absolute time. + * @param soundFileTimeMillis The relative time of a sound file. */ public static void setSoundFileTimeInMillis(long soundFileTimeMillis) { PamCalendar.soundFileTimeInMillis = soundFileTimeMillis; + PamController.getInstance().updateMasterClock(getTimeInMillis()); } /** diff --git a/src/PamguardMVC/PamRawDataBlock.java b/src/PamguardMVC/PamRawDataBlock.java index f1246242..6b06ead5 100644 --- a/src/PamguardMVC/PamRawDataBlock.java +++ b/src/PamguardMVC/PamRawDataBlock.java @@ -257,12 +257,13 @@ public class PamRawDataBlock extends AcousticDataBlock { synchronized public RawDataUnit[] getAvailableSamples(long startMillis, long durationMillis, int channelMap) throws RawDataUnavailableException { RawDataUnit firstUnit = getFirstUnit(); if (firstUnit == null) { - throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, startMillis, (int) durationMillis); + throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, 0,0, startMillis, (int) durationMillis); } long firstMillis = firstUnit.getTimeMilliseconds(); long firstSamples = firstUnit.getStartSample(); RawDataUnit lastUnit = getLastUnit(); long lastMillis = lastUnit.getEndTimeInMilliseconds(); + long lastSample = lastUnit.getStartSample()+lastUnit.getSampleDuration(); long firstAvailableMillis = Math.max(firstMillis, startMillis); @@ -272,7 +273,8 @@ public class PamRawDataBlock extends AcousticDataBlock { double[][] data = getSamplesForMillis(firstAvailableMillis, lastAvailableMillis-firstAvailableMillis, channelMap); if (data == null) { // this shouldn't happen. If an exception wasn't thrown from getSamples... then data should no tb enull - throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, startMillis, (int) durationMillis); + throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, + firstSamples, lastSample, startMillis, (int) durationMillis); } RawDataUnit[] dataUnits = new RawDataUnit[data.length]; for (int i = 0; i < data.length; i++) { @@ -298,7 +300,7 @@ public class PamRawDataBlock extends AcousticDataBlock { synchronized public double[][] getSamplesForMillis(long startMillis, long durationMillis, int channelMap) throws RawDataUnavailableException { RawDataUnit firstUnit = getFirstUnit(); if (firstUnit == null) { - throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, startMillis, (int) durationMillis); + throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, 0, 0, startMillis, (int) durationMillis); } long firstMillis = firstUnit.getTimeMilliseconds(); long firstSamples = firstUnit.getStartSample(); @@ -317,23 +319,28 @@ public class PamRawDataBlock extends AcousticDataBlock { // run a few tests ... int chanOverlap = channelMap & getChannelMap(); if (chanOverlap != channelMap) { - throw new RawDataUnavailableException(this, RawDataUnavailableException.INVALID_CHANNEL_LIST, startSample, duration); + throw new RawDataUnavailableException(this, RawDataUnavailableException.INVALID_CHANNEL_LIST, 0,0,startSample, duration); } if (duration < 0) { - throw new RawDataUnavailableException(this, RawDataUnavailableException.NEGATIVE_DURATION, startSample, duration); + throw new RawDataUnavailableException(this, RawDataUnavailableException.NEGATIVE_DURATION,0,0, startSample, duration); } RawDataUnit dataUnit = getFirstUnit(); if (dataUnit == null) { return null; } - if (dataUnit.getStartSample() > startSample) { + RawDataUnit lastUnit = getLastUnit(); + long firstSample = dataUnit.getStartSample(); + long lastSample = lastUnit.getStartSample()+lastUnit.getSampleDuration(); + if (firstSample > startSample) { // System.out.println("Earliest start sample : " + dataUnit.getStartSample()); - throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_ALREADY_DISCARDED, startSample, duration); + throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_ALREADY_DISCARDED, + firstSample, lastSample, startSample, duration); } dataUnit = getLastUnit(); if (hasLastSample(dataUnit, startSample+duration, channelMap) == false) { - throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, startSample, duration); + throw new RawDataUnavailableException(this, RawDataUnavailableException.DATA_NOT_ARRIVED, + firstSample, lastSample, startSample, duration); } int nChan = PamUtils.getNumChannels(channelMap); diff --git a/src/PamguardMVC/RawDataUnavailableException.java b/src/PamguardMVC/RawDataUnavailableException.java index 37e4275a..524f8072 100644 --- a/src/PamguardMVC/RawDataUnavailableException.java +++ b/src/PamguardMVC/RawDataUnavailableException.java @@ -21,6 +21,10 @@ public class RawDataUnavailableException extends Exception { private long startSample; private int duration; + + private long availableStart; + + private long availableEnd; /** * @return the dataCause */ @@ -34,10 +38,12 @@ public class RawDataUnavailableException extends Exception { * @param startSample * @param cause */ - public RawDataUnavailableException(PamRawDataBlock rawDataBlock, int dataCause, long startSample, int duration) { + public RawDataUnavailableException(PamRawDataBlock rawDataBlock, int dataCause, long availStart, long availEnd, long startSample, int duration) { super(); this.rawDataBlock = rawDataBlock; this.dataCause = dataCause; + this.availableStart = availStart; + this.availableEnd = availEnd; this.startSample = startSample; this.duration = duration; } @@ -55,8 +61,8 @@ public class RawDataUnavailableException extends Exception { public String getMessage() { switch (dataCause) { case DATA_ALREADY_DISCARDED: - return String.format("Samples %d length %d requested from %s have already been discarded", startSample, duration, - rawDataBlock.getDataName()); + return String.format("Samples %d length %d requested from %s have already been discarded. %d to %d available", startSample, duration, + rawDataBlock.getDataName(), availableStart, availableEnd); case DATA_NOT_ARRIVED: return String.format("Samples %d length %d requested from %s have not yet arrived", startSample, duration, rawDataBlock.getDataName()); @@ -70,6 +76,20 @@ public class RawDataUnavailableException extends Exception { } return super.getMessage(); } + + /** + * @return the availableStart + */ + public long getAvailableStart() { + return availableStart; + } + + /** + * @return the availableEnd + */ + public long getAvailableEnd() { + return availableEnd; + } } diff --git a/src/PamguardMVC/ThreadedObserver.java b/src/PamguardMVC/ThreadedObserver.java index 83713af0..1e73c30a 100644 --- a/src/PamguardMVC/ThreadedObserver.java +++ b/src/PamguardMVC/ThreadedObserver.java @@ -8,6 +8,7 @@ import Acquisition.AcquisitionControl; import Acquisition.AcquisitionProcess; import Acquisition.DaqSystem; import PamController.PamController; +import PamModel.PamModel; import PamUtils.PamCalendar; import PamguardMVC.debug.Debug; @@ -130,6 +131,7 @@ public class ThreadedObserver implements PamObserver { } } } + h += PamModel.getPamModel().getPamModelSettings().getThreadingJitterMillis()*2; return h; } diff --git a/src/binaryFileStorage/BinaryStore.java b/src/binaryFileStorage/BinaryStore.java index 1785eb6f..67151c22 100644 --- a/src/binaryFileStorage/BinaryStore.java +++ b/src/binaryFileStorage/BinaryStore.java @@ -228,6 +228,7 @@ PamSettingsSource, DataOutputStore { super.pamToStart(); prepareStores(); openStores(); + binaryStoreProcess.checkFileTimer(); } @Override @@ -245,9 +246,9 @@ PamSettingsSource, DataOutputStore { * Called from the process to close and reopen each datastream in * a new file. Probably gets called about once an hour on the hour. */ - protected void reOpenStores(int endReason) { + protected synchronized void reOpenStores(int endReason, long newFileTime) { - long dataTime = PamCalendar.getTimeInMillis(); + long dataTime = newFileTime;//PamCalendar.getTimeInMillis(); long analTime = System.currentTimeMillis(); BinaryOutputStream dataStream; for (int i = 0; i < storageStreams.size(); i++) { @@ -536,7 +537,7 @@ PamSettingsSource, DataOutputStore { */ if (immediateChanges) { if (storesOpen) { - reOpenStores(BinaryFooter.END_UNKNOWN); + reOpenStores(BinaryFooter.END_UNKNOWN, PamCalendar.getTimeInMillis()); } } @@ -2601,5 +2602,11 @@ PamSettingsSource, DataOutputStore { public DataIntegrityChecker getInegrityChecker() { return new BinaryIntegrityChecker(this); } + /** + * @return the binaryStoreProcess + */ + public BinaryStoreProcess getBinaryStoreProcess() { + return binaryStoreProcess; + } } diff --git a/src/binaryFileStorage/BinaryStoreProcess.java b/src/binaryFileStorage/BinaryStoreProcess.java index c3a728b4..d95be814 100644 --- a/src/binaryFileStorage/BinaryStoreProcess.java +++ b/src/binaryFileStorage/BinaryStoreProcess.java @@ -7,37 +7,43 @@ import PamUtils.PamCalendar; import PamguardMVC.PamProcess; public class BinaryStoreProcess extends PamProcess { - + private BinaryStore binaryStore; - + private long startTime; - + private long nextFileTime; - + private Timer timer; + private Object timerSynch = new Object(); + public BinaryStoreProcess(BinaryStore binaryStore) { super(binaryStore, null); this.binaryStore = binaryStore; } - + @Override public String getProcessName() { return "Binary store file control"; } - - public synchronized void checkFileTime() { + + public synchronized void checkFileTime(long masterClockTime) { + // if (binaryStore.binaryStoreSettings.autoNewFiles && + // PamCalendar.getTimeInMillis() >= nextFileTime) { + // startNewFiles(); + // } if (binaryStore.binaryStoreSettings.autoNewFiles && - PamCalendar.getTimeInMillis() >= nextFileTime) { - startNewFiles(); + masterClockTime >= nextFileTime) { + startNewFiles(masterClockTime); } - + } - private synchronized void startNewFiles() { + private synchronized void startNewFiles(long masterClockTime) { nextFileTime += binaryStore.binaryStoreSettings.fileSeconds * 1000; - binaryStore.reOpenStores(BinaryFooter.END_FILETOOLONG); + binaryStore.reOpenStores(BinaryFooter.END_FILETOOLONG, masterClockTime); } @@ -46,24 +52,55 @@ public class BinaryStoreProcess extends PamProcess { startTime = PamCalendar.getTimeInMillis(); long round = binaryStore.binaryStoreSettings.fileSeconds * 1000; nextFileTime = (startTime/round) * round + round; -// System.out.println("Next file start at " + PamCalendar.formatDateTime(nextFileTime)); - timer = new Timer(); - timer.schedule(new FileTimerTask(), 1000, 1000); - + // System.out.println("Next file start at " + PamCalendar.formatDateTime(nextFileTime)); + } + + public void checkFileTimer() { + boolean needTimer = PamCalendar.isSoundFile() == false; + if (needTimer) { + startTimer(); + } + else { + stopTimer(); + } } + private void startTimer() { + synchronized (timerSynch) { + if (timer == null) { + timer = new Timer(); + timer.schedule(new FileTimerTask(), 1000, 1000); + } + } + } + + private void stopTimer() { + synchronized (timerSynch) { + if (timer != null) { + timer.cancel(); + timer = null; + } + } + } + + + + // @Override + // public void masterClockUpdate(long timeMilliseconds, long sampleNumber) { + // super.masterClockUpdate(timeMilliseconds, sampleNumber); + // checkFileTime(timeMilliseconds); + // } + class FileTimerTask extends TimerTask { @Override public void run() { - checkFileTime(); + checkFileTime(PamCalendar.getTimeInMillis()); } } @Override public void pamStop() { - if (timer != null) { - timer.cancel(); - } + stopTimer(); } } diff --git a/src/clickDetector/ClickDetector.java b/src/clickDetector/ClickDetector.java index 56b9481e..2ee4ece2 100644 --- a/src/clickDetector/ClickDetector.java +++ b/src/clickDetector/ClickDetector.java @@ -329,7 +329,7 @@ public class ClickDetector extends PamProcess { offlineEventLogging.setSubLogging(getClickDataBlock().getOfflineClickLogging()); triggerBackgroundHandler = new TriggerBackgroundHandler(this); - + clickBackgroundManager = new ClickBackgroundManager(this); setProcessCheck(new BaseProcessCheck(this, RawDataUnit.class, 1, 0.0000001)); @@ -1378,7 +1378,7 @@ public class ClickDetector extends PamProcess { private boolean initialiseFilters; private long clickStartSample, clickEndSample; - + private double maxSignalExcess; private int clickTriggers; diff --git a/src/clipgenerator/ClipProcess.java b/src/clipgenerator/ClipProcess.java index b61c0a43..112281e3 100644 --- a/src/clipgenerator/ClipProcess.java +++ b/src/clipgenerator/ClipProcess.java @@ -140,10 +140,13 @@ public class ClipProcess extends SpectrogramMarkProcess { clipErr = clipRequest.clipBlockProcess.processClipRequest(clipRequest); switch (clipErr) { case 0: // no error - clip should have been created. + li.remove(); + break; case RawDataUnavailableException.DATA_ALREADY_DISCARDED: case RawDataUnavailableException.INVALID_CHANNEL_LIST: - // System.out.println("Clip error : " + clipErr); +// System.out.println("Clip error : " + clipErr); li.remove(); + break; case RawDataUnavailableException.DATA_NOT_ARRIVED: continue; // hopefully, will get this next time ! } @@ -230,6 +233,17 @@ public class ClipProcess extends SpectrogramMarkProcess { } minH = Math.max(minH, clipBlockProcesses[i].getRequiredDataHistory(o, arg)); } + + ClipRequest firstClip = null; + synchronized(clipRequestSynch) { + if (clipRequestQueue.size() > 0) { + firstClip = clipRequestQueue.get(0); + } + } + if (firstClip != null) { + minH += firstClip.dataUnit.getDurationInMilliseconds(); + } + minH += Math.max(3000, 192000/(long)getSampleRate()); if (specMouseDown) { minH = Math.max(minH, masterClockTime-specMouseDowntime); @@ -453,8 +467,7 @@ public class ClipProcess extends SpectrogramMarkProcess { this.dataBlock = dataBlock; this.clipGenSetting = clipGenSetting; clipBudgetMaker = new StandardClipBudgetMaker(this); - dataBlock.addObserver(this, true); - + dataBlock.addObserver(this, false); if (rawDataBlock != null) { int chanMap = decideChannelMap(rawDataBlock.getChannelMap()); @@ -499,6 +512,7 @@ public class ClipProcess extends SpectrogramMarkProcess { rawData = rawDataBlock.getSamples(rawStart, (int) (rawEnd-rawStart), channelMap); } catch (RawDataUnavailableException e) { + System.out.println(e.getMessage()); return e.getDataCause(); } if (rawData==null) { @@ -583,9 +597,15 @@ public class ClipProcess extends SpectrogramMarkProcess { public PamObserver getObserverObject() { return clipProcess.getObserverObject(); } + @Override public long getRequiredDataHistory(PamObservable o, Object arg) { - return (long) ((clipGenSetting.preSeconds+clipGenSetting.postSeconds) * 1000.); + long h = (long) ((clipGenSetting.preSeconds+clipGenSetting.postSeconds) * 1000.); +// if (dataBlock != null) { + // can't do this since dataBlock is observing this, so will wrap. +// h += dataBlock.getRequiredHistory(); +// } + return h; } diff --git a/src/tethys/dbxml/DBXMLConnect.java b/src/tethys/dbxml/DBXMLConnect.java index b534703b..67ae165d 100644 --- a/src/tethys/dbxml/DBXMLConnect.java +++ b/src/tethys/dbxml/DBXMLConnect.java @@ -150,8 +150,7 @@ public class DBXMLConnect { */ public boolean postAndLog(Object nilusObject, String documentName) throws TethysException { - boolean ok = NilusChecker.warnEmptyFields(tethysControl.getGuiFrame(), nilusObject); - +// boolean ok = NilusChecker.warnEmptyFields(tethysControl.getGuiFrame(), nilusObject); TethysException e = null; boolean success = false;