2022-01-07 10:41:38 +00:00
|
|
|
package pamScrollSystem;
|
|
|
|
|
|
|
|
import java.awt.event.ActionEvent;
|
|
|
|
import java.awt.event.ActionListener;
|
|
|
|
import java.io.Serializable;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import javax.swing.JCheckBoxMenuItem;
|
|
|
|
import javax.swing.JPopupMenu;
|
|
|
|
import javax.swing.SwingWorker;
|
|
|
|
|
|
|
|
import PamController.AWTScheduler;
|
|
|
|
import PamController.PamControlledUnitSettings;
|
|
|
|
import PamController.PamController;
|
|
|
|
import PamController.PamControllerInterface;
|
|
|
|
import PamController.PamGUIManager;
|
|
|
|
import PamController.PamSettingManager;
|
|
|
|
import PamController.PamSettings;
|
|
|
|
import PamguardMVC.PamDataBlock;
|
|
|
|
import PamguardMVC.dataOffline.OfflineDataLoadInfo;
|
|
|
|
import PamguardMVC.superdet.SuperDetDataBlock;
|
|
|
|
import clickDetector.offlineFuncs.OfflineEventDataBlock;
|
|
|
|
import dataMap.DataMapControl;
|
|
|
|
import dataMap.OfflineDataMap;
|
|
|
|
import detectiongrouplocaliser.DetectionGroupDataBlock;
|
|
|
|
import generalDatabase.SQLLogging;
|
|
|
|
import generalDatabase.SuperDetLogging;
|
|
|
|
import offlineProcessing.superdet.OfflineSuperDetFilter;
|
|
|
|
import pamViewFX.pamTask.PamTaskUpdate;
|
|
|
|
|
|
|
|
public class ViewerScrollerManager extends AbstractScrollManager implements PamSettings {
|
|
|
|
|
|
|
|
private boolean initialisationComplete;
|
|
|
|
|
|
|
|
private boolean intialiseLoadDone;
|
|
|
|
|
|
|
|
private StoredScrollerData oldScrollerData = new StoredScrollerData();
|
|
|
|
|
|
|
|
private boolean currentScrollInitialisation;
|
|
|
|
|
|
|
|
public ViewerScrollerManager() {
|
|
|
|
PamSettingManager.getInstance().registerSettings(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (non-Javadoc)
|
|
|
|
* @see pamScrollSystem.AbstractScrollManager#addPamScroller(pamScrollSystem.AbstractPamScroller)
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public void addPamScroller(AbstractPamScroller pamScroller) {
|
|
|
|
super.addPamScroller(pamScroller);
|
|
|
|
if (oldScrollerData != null && initialisationComplete) {
|
|
|
|
PamScrollerData oldData = oldScrollerData.findScrollerData(pamScroller);
|
|
|
|
if (oldData != null) {
|
|
|
|
pamScroller.scrollerData = oldData.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void moveInnerScroller(AbstractPamScroller scroller, long newValue) {
|
|
|
|
//Debug.out.println("Move inner scroller " + newValue);
|
|
|
|
AbstractPamScroller aScroller;
|
|
|
|
if (oldScrollerData.coupleAllScrollers) {
|
|
|
|
followCoupledScroller(scroller);
|
|
|
|
}
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
aScroller = pamScrollers.get(i);
|
|
|
|
if (aScroller == scroller) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
aScroller.anotherScrollerMovedInner(newValue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void moveOuterScroller(AbstractPamScroller scroller, long newMin,
|
|
|
|
long newMax) {
|
|
|
|
//Debug.out.println("Move outer scroller " + newMin + " " + newMax);
|
|
|
|
AbstractPamScroller aScroller;
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
aScroller = pamScrollers.get(i);
|
|
|
|
if (aScroller == scroller) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
aScroller.anotherScrollerMovedOuter(newMin, newMax);
|
|
|
|
}
|
|
|
|
if (currentScrollInitialisation == false) {
|
|
|
|
loadData(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private volatile boolean loaderRunning = false;
|
|
|
|
|
|
|
|
private DataLoader dataLoader;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void reLoad() {
|
|
|
|
loadData(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wait for the data loader to complete.
|
|
|
|
*
|
|
|
|
* @param timeOut maximum time to wait for in milliseconds. Enter 0 to wait forever.
|
|
|
|
* @return true if data loader is not running or stops, false if
|
|
|
|
* a timeout occurs.
|
|
|
|
*/
|
|
|
|
// public boolean waitForLoader(long timeOut) {
|
|
|
|
// long now = System.currentTimeMillis();
|
|
|
|
// try {
|
|
|
|
// while (loaderRunning) {
|
|
|
|
// Thread.sleep(10);
|
|
|
|
// if (timeOut > 0) {
|
|
|
|
// if (System.currentTimeMillis() > now+timeOut) {
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// } catch (InterruptedException e) {
|
|
|
|
// // TODO Auto-generated catch block
|
|
|
|
// e.printStackTrace();
|
|
|
|
// }
|
|
|
|
// return (!loaderRunning);
|
|
|
|
// }
|
|
|
|
/**
|
|
|
|
* loads data after a scroller has changed.
|
|
|
|
* @param immediateLoad
|
|
|
|
*/
|
|
|
|
public synchronized void loadData(boolean immediateLoad) {
|
|
|
|
//Debug.out.println("Viewer scroll manager: " + immediateLoad);
|
|
|
|
/**
|
|
|
|
* will need to stop this getting called multiple times
|
|
|
|
* when several scroll bars move.
|
|
|
|
*/
|
|
|
|
if (loaderRunning) {
|
|
|
|
// return;
|
|
|
|
// if (dataLoader != null) {
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
// dataLoader.cancel(true);
|
|
|
|
}
|
|
|
|
// checks what everyone wants and needs, then loads the
|
|
|
|
// appropriate data.
|
|
|
|
/*
|
|
|
|
* first loop through data blocks, then for
|
|
|
|
* every data block loop through all scrollers and
|
|
|
|
* for srollers which use that data block work out
|
|
|
|
* the min and max times for data in that block then
|
|
|
|
* load the data.
|
|
|
|
*/
|
|
|
|
ArrayList<PamDataBlock> dataBlocks = PamController.getInstance().getDataBlocks();
|
|
|
|
ArrayList<DataLoadQueData> dataLoadQueue = new ArrayList<DataLoadQueData>();
|
|
|
|
// dataLoadQueu.clear();
|
|
|
|
for (int i = 0; i < dataBlocks.size(); i++) {
|
|
|
|
checkLoadLimits(dataLoadQueue, dataBlocks.get(i));
|
|
|
|
}
|
|
|
|
if (dataLoadQueue.size() > 0) {
|
|
|
|
loaderRunning = true;
|
|
|
|
if (immediateLoad) {
|
|
|
|
loadDataQueue(dataLoadQueue);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
scheduleDataQueue(dataLoadQueue);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load the data queue immediately in the current thread.
|
|
|
|
* @param dataLoadQueue
|
|
|
|
*/
|
|
|
|
private void loadDataQueue(ArrayList<DataLoadQueData> dataLoadQueue) {
|
|
|
|
int n = dataLoadQueue.size();
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
loadDataQueueItem(dataLoadQueue.get(i), i, null);
|
|
|
|
}
|
|
|
|
loadSubformData(dataLoadQueue, null);
|
|
|
|
loadDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load the data from a single object in the queue.
|
|
|
|
* Generally called from within the worker thread.
|
|
|
|
* Note that any data units with a starting load time of <=0 are simply not loaded. This is to prevent potential issues
|
|
|
|
* later with algorithms not expecting a negative date; however, this also means that PAMGuard cannot process pre-1970 data.
|
|
|
|
* @param dataLoadQueData
|
|
|
|
* @param i
|
|
|
|
*/
|
|
|
|
private void loadDataQueueItem(DataLoadQueData dataLoadQueData, int queuePosition, ViewLoadObserver loadObserver) {
|
|
|
|
PamDataBlock dataBlock = dataLoadQueData.getPamDataBlock();
|
|
|
|
if (dataBlock instanceof OfflineEventDataBlock) {
|
|
|
|
System.out.println(dataBlock);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2019-11-12 add a check of the counts to this 'if' statement as well. If the data start time is negative
|
|
|
|
// (which can indicate that this is a 'special' data block and we should be loading all of it rather than just
|
|
|
|
// what is within the scroller time range) and we haven't loaded anything yet, go ahead and load. But if there
|
|
|
|
// are data units, it means the info has already been loaded so don't bother loading again.
|
|
|
|
// 2020-01-28 this fixed the problem with DetectionGroupLocaliser data not getting loaded, but Streamer data is still broken.
|
|
|
|
// Correct this by changing the default min time from Long.MIN_VALUE to 1 in AbstractScrollManager.getSpecialLoadTimes. This
|
|
|
|
// affects DetectionGroupLocaliser, StreamerData, AcquisitionProcess and IMUProcess (anything that uses the AbstractScrollManager.SpecialDataBlockInfo
|
|
|
|
// class without explicitly specifying time limits
|
|
|
|
if ((dataLoadQueData.getDataStart() <= 0 || dataLoadQueData.getDataStart() == Long.MAX_VALUE)
|
|
|
|
&& dataBlock.getUnitsCount() > 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dataLoadQueData.getPamDataBlock().loadViewerData(new OfflineDataLoadInfo(dataLoadQueData.getDataStart(),
|
|
|
|
dataLoadQueData.getDataEnd()), loadObserver);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Schedule the data queue to be loaded in a separate thread.
|
|
|
|
* @param dataLoadQueue
|
|
|
|
*/
|
|
|
|
private void scheduleDataQueue(ArrayList<DataLoadQueData> dataLoadQueue) {
|
|
|
|
AWTScheduler.getInstance().scheduleTask(dataLoader = new DataLoader(dataLoadQueue));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Go through the list of loaded data blocks and see if there are any that have a subform
|
|
|
|
*
|
|
|
|
* @param dataLoadQueue
|
|
|
|
* @param dataLoader
|
|
|
|
*/
|
|
|
|
private void loadSubformData(ArrayList<DataLoadQueData> dataLoadQueue, DataLoader dataLoader) {
|
|
|
|
for (int i=0; i<dataLoadQueue.size(); i++) {
|
|
|
|
PamDataBlock dataBlock = dataLoadQueue.get(i).getPamDataBlock();
|
|
|
|
if (dataBlock instanceof SuperDetDataBlock == false) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// System.out.println("Load subform data for datablock " + i + ": " + dataBlock.getLongDataName());
|
|
|
|
if (dataLoader != null) {
|
|
|
|
LoadQueueProgressData lpd = new LoadQueueProgressData("Database",
|
|
|
|
dataBlock.getDataName(),
|
|
|
|
dataLoadQueue.size(), i, LoadQueueProgressData.STATE_LINKINGSUBTABLE, 0, 0, 0, 0);
|
|
|
|
dataLoader.pubPublish(lpd);
|
|
|
|
}
|
|
|
|
((SuperDetDataBlock) dataBlock).reattachSubdetections(dataLoader);
|
|
|
|
// System.out.println("Completed subform data for datablock " + i + ": " + dataBlock.getLongDataName());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void loadDone() {
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
pamScrollers.get(i).notifyRangeChange();
|
|
|
|
}
|
|
|
|
PamController.getInstance().notifyModelChanged(PamControllerInterface.OFFLINE_DATA_LOADED);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check the required load limits for a data block which
|
|
|
|
* may be being used my multiple scrollers.
|
|
|
|
*
|
|
|
|
* @param pamDataBlock data block to check
|
|
|
|
*/
|
|
|
|
private void checkLoadLimits(ArrayList<DataLoadQueData> dataLoadQueue, PamDataBlock pamDataBlock) {
|
|
|
|
long minTime = Long.MAX_VALUE;
|
|
|
|
long maxTime = Long.MIN_VALUE;
|
|
|
|
AbstractPamScroller aScroller;
|
|
|
|
boolean used = false;
|
|
|
|
|
|
|
|
for (int i = 0; i < this.pamScrollers.size(); i++) {
|
|
|
|
aScroller = pamScrollers.get(i);
|
|
|
|
if (aScroller.isDataBlockUsed(pamDataBlock) || aScroller.isInSpecialList(pamDataBlock)) {
|
|
|
|
long[] loadTimes = aScroller.getSpecialLoadTimes(pamDataBlock, aScroller.getMinimumMillis(), aScroller.getMaximumMillis());
|
|
|
|
/*
|
|
|
|
* Needed to change the logic here, since was going wrong if one scroller had a shorter load time.
|
|
|
|
* Now a straight forward 3-way maximum.
|
|
|
|
*/
|
|
|
|
if (loadTimes != null) {
|
|
|
|
minTime = Math.min(minTime, loadTimes[0]);
|
|
|
|
maxTime = Math.max(maxTime, loadTimes[1]);
|
|
|
|
}
|
|
|
|
minTime = Math.min(minTime, aScroller.getMinimumMillis());
|
|
|
|
maxTime = Math.max(maxTime, aScroller.getMaximumMillis());
|
|
|
|
|
|
|
|
used = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pamDataBlock.getDataName().contains("Gemini Target") ) {
|
|
|
|
System.out.println("Gemini Tracks");
|
|
|
|
}
|
|
|
|
if (used) {
|
|
|
|
// dataLoadQueue.add(new DataLoadQueData(pamDataBlock, minTime, maxTime, hasSubFormData(pamDataBlock)));
|
|
|
|
addToDataQueue(dataLoadQueue, pamDataBlock, minTime, maxTime, hasSubFormData(pamDataBlock));
|
|
|
|
/*
|
|
|
|
* Also need to do this for any super detection datablock(s), which may or may not already
|
|
|
|
* be in the dataloadQueue, and may or may not be associated with this scroller. Also note that a superblock
|
|
|
|
* may already be in the load que, in which case AND the times.
|
|
|
|
*/
|
|
|
|
ArrayList<SuperDetDataBlock> superBlocks = OfflineSuperDetFilter.findPossibleSuperDetections(pamDataBlock);
|
|
|
|
for (PamDataBlock aBlock : superBlocks) {
|
|
|
|
addToDataQueue(dataLoadQueue, pamDataBlock, minTime, maxTime, true);
|
|
|
|
// DataLoadQueData exItem = findLoadQueueData(dataLoadQueue, aBlock);
|
|
|
|
// if (exItem != null) {
|
|
|
|
// // expand an existing item as necessary
|
|
|
|
// exItem.setDataStart(Math.min(exItem.getDataStart(), minTime));
|
|
|
|
// exItem.setDataEnd(Math.max(exItem.getDataEnd(), maxTime));
|
|
|
|
// }
|
|
|
|
// else {
|
|
|
|
// // or add a new item.
|
|
|
|
// dataLoadQueue.add(new DataLoadQueData(aBlock, minTime, maxTime, true));
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add to data queue, being aware that the block may already be in the queue if it was included from a sub detections super
|
|
|
|
* detection list.
|
|
|
|
* @param dataLoadQueue
|
|
|
|
* @param pamDataBlock
|
|
|
|
* @param minTime
|
|
|
|
* @param maxTime
|
|
|
|
* @param hasSubData
|
|
|
|
*/
|
|
|
|
private void addToDataQueue(ArrayList<DataLoadQueData> dataLoadQueue, PamDataBlock pamDataBlock, long minTime, long maxTime, boolean hasSubData) {
|
|
|
|
DataLoadQueData exItem = findLoadQueueData(dataLoadQueue, pamDataBlock);
|
|
|
|
if (exItem != null) {
|
|
|
|
// expand an existing item as necessary
|
|
|
|
exItem.setDataStart(Math.min(exItem.getDataStart(), minTime));
|
|
|
|
exItem.setDataEnd(Math.max(exItem.getDataEnd(), maxTime));
|
|
|
|
exItem.setHasSubTable(true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// or add a new item.
|
|
|
|
dataLoadQueue.add(new DataLoadQueData(pamDataBlock, minTime, maxTime, true));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Find an existing entry for a data block in the load queue
|
|
|
|
* @param loadQueue
|
|
|
|
* @param dataBlock
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private DataLoadQueData findLoadQueueData(ArrayList<DataLoadQueData> loadQueue, PamDataBlock dataBlock) {
|
|
|
|
for (DataLoadQueData dlqd : loadQueue) {
|
|
|
|
if (dlqd.getPamDataBlock() == dataBlock) {
|
|
|
|
return dlqd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param dataBlock
|
|
|
|
* @return true if a datablock has subform data.
|
|
|
|
*/
|
|
|
|
private boolean hasSubFormData(PamDataBlock dataBlock) {
|
|
|
|
SQLLogging logging = dataBlock.getLogging();
|
|
|
|
if (logging == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return (logging instanceof SuperDetLogging);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class DataLoader extends SwingWorker<Integer, LoadQueueProgressData> implements ViewLoadObserver{
|
|
|
|
|
|
|
|
private ArrayList<DataLoadQueData> dataLoadQueue;
|
|
|
|
|
|
|
|
private LoadingDataDialog loadingDataDialog;
|
|
|
|
|
|
|
|
private volatile boolean emergencyStop = false;
|
|
|
|
|
|
|
|
private volatile String currentDataName;
|
|
|
|
|
|
|
|
private volatile String storeName;
|
|
|
|
|
|
|
|
public DataLoader(ArrayList<DataLoadQueData> dataLoadQueue) {
|
|
|
|
super();
|
|
|
|
// System.out.printf("Makeing dataloader with %d items, inicomplete = %s\n",
|
|
|
|
// dataLoadQueue.size(),
|
|
|
|
// new Boolean(PamController.getInstance().isInitializationComplete()));
|
|
|
|
this.dataLoadQueue = dataLoadQueue;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Integer doInBackground() throws Exception {
|
|
|
|
try {
|
|
|
|
int nDone = 0;
|
|
|
|
/**
|
|
|
|
* First load all the data which has subtable detections
|
|
|
|
*/
|
|
|
|
for (int i = 0; i < dataLoadQueue.size(); i++) {
|
|
|
|
DataLoadQueData queueItem = dataLoadQueue.get(i);
|
|
|
|
if (queueItem.isHasSubTable() == false) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
OfflineDataMap priMap = dataLoadQueue.get(i).getPamDataBlock().getPrimaryDataMap();
|
|
|
|
if (priMap != null) {
|
|
|
|
storeName = priMap.getDataMapName();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
storeName = null;
|
|
|
|
}
|
|
|
|
currentDataName = dataLoadQueue.get(i).getPamDataBlock().getDataName();
|
|
|
|
LoadQueueProgressData lpd = new LoadQueueProgressData(storeName,
|
|
|
|
currentDataName,
|
|
|
|
dataLoadQueue.size(), nDone++, 0, 0, 0, 0, 0);
|
|
|
|
publish(lpd);
|
|
|
|
loadDataQueueItem(queueItem, i, this);
|
|
|
|
/**
|
|
|
|
* Now go through every item which was loaded so far and
|
|
|
|
* then check to see that any required sub detections are
|
|
|
|
* about to be loaded.
|
|
|
|
*/
|
|
|
|
// ArrayList subTableData = queueItem.getPamDataBlock().getSubtableData();
|
|
|
|
/*
|
|
|
|
* Need to
|
|
|
|
* 1. Get a list of datablock,
|
|
|
|
* 2. Find if they are in the rest of the queueItem list
|
|
|
|
* 3. If so, then check load limits are the same
|
|
|
|
* 4. If not, then add to the list before proceeding below.
|
|
|
|
* All very complicated !!!!!!!!
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Then load all the data that doesn't have sub table detections.
|
|
|
|
*/
|
|
|
|
for (int i = 0; i < dataLoadQueue.size(); i++) {
|
|
|
|
DataLoadQueData queueItem = dataLoadQueue.get(i);
|
|
|
|
if (queueItem.isHasSubTable() == true) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
LoadQueueProgressData lpd = new LoadQueueProgressData(storeName,
|
|
|
|
dataLoadQueue.get(i).getPamDataBlock().getDataName(),
|
|
|
|
dataLoadQueue.size(), nDone++, 0, 0, 0, 0, 0);
|
|
|
|
publish(lpd);
|
|
|
|
loadDataQueueItem(queueItem, i, this);
|
|
|
|
}
|
|
|
|
loadSubformData(dataLoadQueue, this);
|
|
|
|
// need to set this here so that waitForLoader() can execute in the AWT thread.
|
|
|
|
loaderRunning = false;
|
|
|
|
}
|
|
|
|
catch (Exception e){
|
|
|
|
System.out.println("Error in Viewer Scroller data loader " + e.getMessage());
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
LoadQueueProgressData lpd = new LoadQueueProgressData(storeName,
|
|
|
|
"Data Load Complete, updating displays",
|
|
|
|
dataLoadQueue.size(), dataLoadQueue.size(), 0, 0, 0, 0, 0);
|
|
|
|
publish(lpd);
|
|
|
|
PamController.getInstance().notifyModelChanged(PamControllerInterface.DATA_LOAD_COMPLETE);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void pubPublish(LoadQueueProgressData loadQueueProgressData) {
|
|
|
|
publish(loadQueueProgressData);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void done() {
|
|
|
|
super.done();
|
|
|
|
loaderRunning = false;
|
|
|
|
if (PamGUIManager.isSwing()) {
|
|
|
|
if (loadingDataDialog != null) {
|
|
|
|
loadingDataDialog.closeLater();
|
|
|
|
// loadingDataDialog.setVisible(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
loadDone();
|
|
|
|
PamController.getInstance().notifyTaskProgress(new LoadQueueProgressData(PamTaskUpdate.STATUS_DONE));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void process(List<LoadQueueProgressData> chunks) {
|
|
|
|
if (PamGUIManager.isSwing()) {
|
|
|
|
if (loadingDataDialog == null) {
|
|
|
|
loadingDataDialog = LoadingDataDialog.showDialog(PamController.getMainFrame());
|
|
|
|
}
|
|
|
|
if (loadingDataDialog != null) {
|
|
|
|
for (LoadQueueProgressData lpd:chunks) {
|
|
|
|
loadingDataDialog.setData(lpd);
|
|
|
|
}
|
|
|
|
emergencyStop = loadingDataDialog.shouldStop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (LoadQueueProgressData lpd:chunks) {
|
|
|
|
PamController.getInstance().notifyTaskProgress(lpd);
|
|
|
|
}
|
|
|
|
//TODO- how to return an emergency stop form seperate GUI?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void sayProgress(int state, long loadStart, long loadEnd,
|
|
|
|
long lastTime, int nLoaded) {
|
|
|
|
LoadQueueProgressData lpd = new LoadQueueProgressData(storeName,
|
|
|
|
currentDataName, 0, 0, state, loadStart, loadEnd, lastTime, nLoaded);
|
|
|
|
publish(lpd);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (non-Javadoc)
|
|
|
|
* @see pamScrollSystem.ViewLoadObserver#cancelLoad()
|
|
|
|
*/
|
|
|
|
@Override
|
|
|
|
public boolean cancelLoad() {
|
|
|
|
return emergencyStop ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
private DataMapControl findDataMapControl() {
|
|
|
|
return DataMapControl.getDataMapControl();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long checkMaximumTime(long requestedTime) {
|
|
|
|
DataMapControl dmc = findDataMapControl();
|
|
|
|
if (dmc == null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return Math.min(dmc.getLastTime(), requestedTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long checkMinimumTime(long requestedTime) {
|
|
|
|
DataMapControl dmc = findDataMapControl();
|
|
|
|
if (dmc == null) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return Math.max(dmc.getFirstTime(), requestedTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void notifyModelChanged(int changeType) {
|
|
|
|
switch (changeType) {
|
|
|
|
case PamControllerInterface.INITIALIZATION_COMPLETE:
|
|
|
|
initialisationComplete = true;
|
|
|
|
break;
|
|
|
|
case PamControllerInterface.INITIALIZE_LOADDATA:
|
|
|
|
intialiseLoadDone = true;
|
|
|
|
case PamControllerInterface.CHANGED_OFFLINE_DATASTORE:
|
|
|
|
case PamControllerInterface.ADD_CONTROLLEDUNIT:
|
|
|
|
case PamControllerInterface.REMOVE_CONTROLLEDUNIT:
|
|
|
|
if (initialisationComplete && intialiseLoadDone) {
|
|
|
|
initialiseScrollers();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called once at the start, and possibly after
|
|
|
|
* any changes to the database or binary store.
|
|
|
|
* Initialises scroll bars and calls for a data load.
|
|
|
|
*/
|
|
|
|
private void initialiseScrollers() {
|
|
|
|
DataMapControl dmc = findDataMapControl();
|
|
|
|
if (dmc == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
currentScrollInitialisation = true;
|
|
|
|
AbstractPamScroller aScroller;
|
|
|
|
PamScrollerData oldData;
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
aScroller = pamScrollers.get(i);
|
|
|
|
if (oldScrollerData != null) {
|
|
|
|
oldData = oldScrollerData.findScrollerData(aScroller);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
oldData = null;
|
|
|
|
}
|
|
|
|
if (oldData == null) {
|
|
|
|
aScroller.setRangeMillis(dmc.getFirstTime(),
|
|
|
|
dmc.getFirstTime() + aScroller.getDefaultLoadtime(), true);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
aScroller.scrollerData = oldData.clone();
|
|
|
|
aScroller.rangesChanged(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
currentScrollInitialisation = false;
|
|
|
|
loadData(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Serializable getSettingsReference() {
|
|
|
|
StoredScrollerData sd = new StoredScrollerData();
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
sd.addScrollerData(pamScrollers.get(i));
|
|
|
|
}
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long getSettingsVersion() {
|
|
|
|
return StoredScrollerData.serialVersionUID;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getUnitName() {
|
|
|
|
return "Viewer Scroll Manager";
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getUnitType() {
|
|
|
|
return "Viewer Scroll Manager";
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean restoreSettings(
|
|
|
|
PamControlledUnitSettings pamControlledUnitSettings) {
|
|
|
|
oldScrollerData = (StoredScrollerData) pamControlledUnitSettings.getSettings();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void centreDataAt(PamDataBlock dataBlock, long menuMouseTime) {
|
|
|
|
// centre all scroll bars as close to the above as is possible.
|
|
|
|
AbstractPamScroller aScroller;
|
|
|
|
long scrollRange, newMax, newMin;
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
aScroller = pamScrollers.get(i);
|
|
|
|
scrollRange = aScroller.getMaximumMillis() - aScroller.getMinimumMillis();
|
|
|
|
newMin = checkMinimumTime(menuMouseTime - scrollRange / 2);
|
|
|
|
newMax = checkMaximumTime(newMin + scrollRange);
|
|
|
|
newMin = newMax-scrollRange;
|
|
|
|
newMin = checkGapPos(dataBlock, newMin, newMax);
|
|
|
|
newMax = newMin + scrollRange;
|
|
|
|
aScroller.setRangeMillis(newMin, newMax, false);
|
|
|
|
// aScroller.setValueMillis(menuMouseTime - scrollRange/2);
|
|
|
|
}
|
|
|
|
loadData(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void startDataAt(PamDataBlock dataBlock, long menuMouseTime, boolean immediateLoad) {
|
|
|
|
AbstractPamScroller aScroller;
|
|
|
|
long scrollRange, newMax, newMin;
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
aScroller = pamScrollers.get(i);
|
|
|
|
scrollRange = aScroller.getMaximumMillis() - aScroller.getMinimumMillis();
|
|
|
|
newMin = checkMinimumTime(menuMouseTime);
|
|
|
|
newMax = checkMaximumTime(newMin + scrollRange);
|
|
|
|
newMin = newMax-scrollRange;
|
|
|
|
newMin = checkGapPos(dataBlock, newMin, newMax);
|
|
|
|
newMax = newMin + scrollRange;
|
|
|
|
aScroller.setRangeMillis(newMin, newMax, false);
|
|
|
|
}
|
|
|
|
loadData(immediateLoad);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public JPopupMenu getStandardOptionsMenu(AbstractPamScroller pamScroller) {
|
|
|
|
JPopupMenu menu = new JPopupMenu();
|
|
|
|
JCheckBoxMenuItem cbItem = new JCheckBoxMenuItem("Couple all scrollers");
|
|
|
|
cbItem.setSelected(oldScrollerData.coupleAllScrollers);
|
|
|
|
cbItem.addActionListener(new CoupleScrollersMenuItem(pamScroller, oldScrollerData.coupleAllScrollers));
|
|
|
|
menu.add(cbItem);
|
|
|
|
return menu;
|
|
|
|
}
|
|
|
|
|
|
|
|
class CoupleScrollersMenuItem implements ActionListener {
|
|
|
|
private boolean currentState;
|
|
|
|
private AbstractPamScroller pamScroller;
|
|
|
|
|
|
|
|
public CoupleScrollersMenuItem(AbstractPamScroller pamScroller, boolean currentState) {
|
|
|
|
super();
|
|
|
|
this.currentState = currentState;
|
|
|
|
this.pamScroller = pamScroller;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void actionPerformed(ActionEvent arg0) {
|
|
|
|
setCoupleAllScrollers(pamScroller, !currentState);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setCoupleAllScrollers(AbstractPamScroller pamScroller, boolean newState) {
|
|
|
|
oldScrollerData.coupleAllScrollers = newState;
|
|
|
|
if (newState) {
|
|
|
|
followCoupledScroller(pamScroller);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void followCoupledScroller(AbstractPamScroller pamScroller) {
|
|
|
|
AbstractPamScroller aScroller;
|
|
|
|
long value = pamScroller.getValueMillis();
|
|
|
|
for (int i = 0; i < pamScrollers.size(); i++) {
|
|
|
|
aScroller = pamScrollers.get(i);
|
|
|
|
if (aScroller == pamScroller) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-01-19 17:53:59 +00:00
|
|
|
/*
|
|
|
|
* Can end up with some horrible feedback here if two
|
|
|
|
* coupled scrollers bring in rounding errors and start
|
|
|
|
* to oscillate each other.
|
|
|
|
*/
|
|
|
|
long currentVal = aScroller.getValueMillis();
|
|
|
|
long change = Math.abs(value-currentVal);
|
|
|
|
long range = aScroller.getMaximumMillis() - aScroller.getMinimumMillis();
|
|
|
|
if (range == 0) {
|
|
|
|
aScroller.setValueMillis(aScroller.getMinimumMillis());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
double fracChange = (double) change / (double) range;
|
|
|
|
if (fracChange <= .0001) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-01-07 10:41:38 +00:00
|
|
|
aScroller.setValueMillis(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public long checkGapPos(AbstractPamScroller scroller,
|
|
|
|
long oldMin, long oldMax, long newMin, long newMax, int direction) {
|
|
|
|
int nDataBlocks = scroller.getNumUsedDataBlocks();
|
|
|
|
if (nDataBlocks == 0) {
|
|
|
|
return newMin;
|
|
|
|
}
|
|
|
|
int oldMinGap = 0, oldMaxGap = 0, newMinGap = 0, newMaxGap = 0;
|
|
|
|
PamDataBlock dataBlock;
|
|
|
|
long range = oldMax-oldMin;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get a series of flags for all four positions indicating
|
|
|
|
* exactly where the data are / want to be relative to
|
|
|
|
* gaps in the datamap.
|
|
|
|
* These are defined in OfflineDataMap
|
|
|
|
* <p>NO_DATA
|
|
|
|
* <p>BEFORE_FIRST
|
|
|
|
* <p>AFTER_LAST
|
|
|
|
* <p>POINT_START
|
|
|
|
* <p>POINT_END
|
|
|
|
* <P>IN_GAP
|
|
|
|
*/
|
|
|
|
for (int i = 0; i < nDataBlocks; i++) {
|
|
|
|
dataBlock = scroller.getUsedDataBlock(i);
|
|
|
|
oldMinGap |= isInGap(dataBlock, oldMin);
|
|
|
|
oldMaxGap |= isInGap(dataBlock, oldMax);
|
|
|
|
newMinGap |= isInGap(dataBlock, newMin);
|
|
|
|
newMaxGap |= isInGap(dataBlock, newMax);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Can now think about what to do based on gap status
|
|
|
|
* of the four times.
|
|
|
|
*/
|
|
|
|
if ((newMinGap & OfflineDataMap.IN_DATA) > 0 && (newMaxGap & OfflineDataMap.IN_DATA) > 0) {
|
|
|
|
return newMin;
|
|
|
|
}
|
|
|
|
long newStart;
|
|
|
|
if (direction > 0) { // going forward.
|
|
|
|
/*
|
|
|
|
* if the old start was in a gap, move so the new start is at the edge of
|
|
|
|
* the next data.
|
|
|
|
*/
|
|
|
|
if ((oldMinGap == OfflineDataMap.IN_GAP)) {
|
|
|
|
newStart = getNextDataStart(scroller, oldMin);
|
|
|
|
if (newStart != Long.MAX_VALUE) {
|
|
|
|
return newStart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If the old end was on the end of a point, then jump so that the new start
|
|
|
|
* is at the start of the next map point AFTER the old end.
|
|
|
|
*/
|
|
|
|
if ((oldMaxGap & OfflineDataMap.POINT_END) != 0) {
|
|
|
|
newStart = getNextDataStart(scroller, oldMax);
|
|
|
|
if (newStart != Long.MAX_VALUE) {
|
|
|
|
return newStart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the old end wasn't in gap and the new end is in a gap, then align the
|
|
|
|
* data so that the end is on the end of the data.
|
|
|
|
*/
|
|
|
|
if ((oldMaxGap & OfflineDataMap.IN_DATA) != 0 &&
|
|
|
|
(newMaxGap == OfflineDataMap.IN_GAP)) {
|
|
|
|
newStart = getPrevDataEnd(scroller, newMax);
|
|
|
|
if (newStart != Long.MAX_VALUE) {
|
|
|
|
return newStart - range;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (direction < 0) { // going backwards.
|
|
|
|
/*
|
|
|
|
* if the old end was in a gap, move so the new end is at the edge of
|
|
|
|
* the previous data.
|
|
|
|
*/
|
|
|
|
if ((oldMaxGap == OfflineDataMap.IN_GAP)) {
|
|
|
|
newStart = getPrevDataEnd(scroller, oldMax);
|
|
|
|
if (newStart != Long.MIN_VALUE) {
|
|
|
|
return newStart-range;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If the old start was on the start of a point, then jump so that the new end
|
|
|
|
* is at the end of the previous map point BEFORE the old start.
|
|
|
|
*/
|
|
|
|
if ((oldMinGap & OfflineDataMap.POINT_START) != 0) {
|
|
|
|
newStart = getPrevDataEnd(scroller, oldMin);
|
|
|
|
if (newStart != Long.MIN_VALUE) {
|
|
|
|
return newStart-range;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the old start wasn't in gap and the new start is in a gap, then align the
|
|
|
|
* data so that the start is on the start of the data.
|
|
|
|
*/
|
|
|
|
if ((oldMinGap & OfflineDataMap.IN_DATA) != 0 &&
|
|
|
|
(newMinGap == OfflineDataMap.IN_GAP)) {
|
|
|
|
newStart = getNextDataStart(scroller, newMin);
|
|
|
|
if (newStart != Long.MAX_VALUE) {
|
|
|
|
return newStart;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return newMin;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Very similar to the function checking gap pos for a whole scroller,
|
|
|
|
* but only does it for one datablock.
|
|
|
|
* @param dataBlock data block
|
|
|
|
* @param newMin new min time
|
|
|
|
* @param newMax new max time
|
|
|
|
* @return adjusted new min time
|
|
|
|
*/
|
|
|
|
private long checkGapPos(PamDataBlock dataBlock, long newMin, long newMax) {
|
|
|
|
int newMinGap = 0, newMaxGap = 0;
|
|
|
|
newMinGap |= isInGap(dataBlock, newMin);
|
|
|
|
newMaxGap |= isInGap(dataBlock, newMax);
|
|
|
|
long newT;
|
|
|
|
if ((newMinGap & OfflineDataMap.IN_DATA) > 0 && (newMaxGap & OfflineDataMap.IN_DATA) > 0) {
|
|
|
|
return newMin;
|
|
|
|
}
|
|
|
|
else if (newMinGap == OfflineDataMap.IN_GAP) {
|
|
|
|
/*
|
|
|
|
* Move forward
|
|
|
|
*/
|
|
|
|
newT = dataBlock.getNextDataStart(newMin);
|
|
|
|
if (newT > 0xFF) {
|
|
|
|
return newT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (newMinGap == OfflineDataMap.IN_DATA && newMaxGap == OfflineDataMap.IN_GAP) {
|
|
|
|
/**
|
|
|
|
* Move back a bit, so end of data aligns.
|
|
|
|
*/
|
|
|
|
newT = dataBlock.getPrevDataEnd(newMax);
|
|
|
|
if (newT > 0xFF) {
|
|
|
|
return newT - (newMax-newMin);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return newMin;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getNextDataStart(AbstractPamScroller scroller, long timeMillis) {
|
|
|
|
long time = Long.MAX_VALUE;
|
|
|
|
long t;
|
|
|
|
for (int i = 0; i < scroller.getNumUsedDataBlocks(); i++) {
|
|
|
|
t = scroller.getUsedDataBlock(i).getNextDataStart(timeMillis);
|
|
|
|
if (t > 0xFF) {
|
|
|
|
time = Math.min(time, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
|
|
|
public long getPrevDataEnd(AbstractPamScroller scroller, long timeMillis) {
|
|
|
|
long time = Long.MIN_VALUE;
|
|
|
|
long t;
|
|
|
|
for (int i = 0; i < scroller.getNumUsedDataBlocks(); i++) {
|
|
|
|
t = scroller.getUsedDataBlock(i).getPrevDataEnd(timeMillis);
|
|
|
|
if (t > 0xFF) {
|
|
|
|
time = Math.max(time, t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|