Issue #1442 Changes to update display when Simulated Time is changed. Corrections to pqact.conf.template.

Changes based on code review and added listener to GridManager and unit test.

Change-Id: Ida593a7e3cc7d4fd9bd6f44a4455145114484650

Former-commit-id: 1cd076671c [formerly 7f70c2f65453db6bf3097ae8157511c38a36b136]
Former-commit-id: 1e492b81ff
This commit is contained in:
Roger Ferrel 2013-01-15 08:09:03 -06:00 committed by Gerrit Code Review
parent e8d8dcc795
commit f10521f861
13 changed files with 442 additions and 117 deletions

View file

@ -45,6 +45,7 @@ import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Jul 3, 2007 chammack Initial Creation. * Jul 3, 2007 chammack Initial Creation.
* Jan 14, 2013 1442 rferrel Added method searchTreeUsingContraints.
* *
* </pre> * </pre>
* *
@ -279,7 +280,41 @@ public class DecisionTree<T> {
insertCriteria(searchCriteria, item, true); insertCriteria(searchCriteria, item, true);
} }
/**
* Search the tree by calling RequestConstraint.evaluate with the map values
* for each level of the tree.
*
* @param searchCriteria
* @return
*/
public List<T> searchTree(Map<String, Object> searchCriteria) { public List<T> searchTree(Map<String, Object> searchCriteria) {
return searchTree(searchCriteria, true);
}
/**
* Search the tree to find entries that were put into the tree using the
* exact same criteria as searchCriteria.
*
* @param searchCriteria
* @return
*/
public List<T> searchTreeUsingContraints(
Map<String, RequestConstraint> searchCriteria) {
return searchTree(searchCriteria, false);
}
/**
* Internal search method
*
* @param searchCriteria
* @param evaluateConstraints
* true if the map values should be passed to
* RequestConstraint.evaluate, false if they chould be passed to
* RequestConstraint.equals
* @return
*/
private List<T> searchTree(Map<String, ?> searchCriteria,
boolean evaluateConstraints) {
synchronized (this) { synchronized (this) {
List<T> lst = new ArrayList<T>(); List<T> lst = new ArrayList<T>();
if (head == null) { if (head == null) {
@ -288,13 +323,13 @@ public class DecisionTree<T> {
Node curNode = head; Node curNode = head;
searchTree(curNode, searchCriteria, lst, 0); searchTree(curNode, searchCriteria, lst, 0, evaluateConstraints);
return lst; return lst;
} }
} }
private void searchTree(Node curNode, Map<String, Object> searchCriteria, private void searchTree(Node curNode, Map<String, ?> searchCriteria,
List<T> resultList, int lvl) { List<T> resultList, int lvl, boolean evaluatedConstraint) {
if (curNode == null) { if (curNode == null) {
return; return;
@ -312,20 +347,39 @@ public class DecisionTree<T> {
Object parsedValue = searchCriteria.get(curNode.decisionAttribute); Object parsedValue = searchCriteria.get(curNode.decisionAttribute);
boolean foundSomething = false; boolean foundSomething = false;
// Evaluate through the values: First search for an exact match if (evaluatedConstraint) {
// of non-null values // Evaluate through the values: First search for an exact match
for (Node n : curNode.nodeChildren) { // of non-null values
RequestConstraint c = n.decision; for (Node n : curNode.nodeChildren) {
if (c == null RequestConstraint c = n.decision;
|| (c == RequestConstraint.WILDCARD || parsedValue == null || c if (c == null
.evaluate(parsedValue))) { || (c == RequestConstraint.WILDCARD
// for (int k = 0; k < lvl; k++) { || parsedValue == null || c
// System.out.print(" "); .evaluate(parsedValue))) {
// } // for (int k = 0; k < lvl; k++) {
foundSomething = true; // System.out.print(" ");
// System.out.println("visit: " + curNode.decisionAttribute // }
// + ":: " + c); foundSomething = true;
searchTree(n, searchCriteria, resultList, lvl + 1); // System.out.println("visit: " + curNode.decisionAttribute
// + ":: " + c);
searchTree(n, searchCriteria, resultList, lvl + 1,
evaluatedConstraint);
}
}
} else {
// Evaluate using existing constraints.
for (Node n : curNode.nodeChildren) {
RequestConstraint c = n.decision;
if (parsedValue.equals(c)) {
// for (int k = 0; k < lvl; k++) {
// System.out.print(" ");
// }
foundSomething = true;
// System.out.println("visit: " + curNode.decisionAttribute
// + ":: " + c);
searchTree(n, searchCriteria, resultList, lvl + 1,
evaluatedConstraint);
}
} }
} }

View file

@ -52,6 +52,7 @@ import com.raytheon.uf.viz.core.rsc.URICatalog.IURIRefreshCallback;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Apr 12, 2007 chammack Initial Creation. * Apr 12, 2007 chammack Initial Creation.
* Jan 14, 2013 1442 rferrel Added query method.
* *
* </pre> * </pre>
* *
@ -70,6 +71,8 @@ public class URICatalog extends DecisionTree<List<IURIRefreshCallback>> {
protected Map<Map<String, RequestConstraint>, List<IURIRefreshCallback>> queuedRCMap = new HashMap<Map<String, RequestConstraint>, List<IURIRefreshCallback>>(); protected Map<Map<String, RequestConstraint>, List<IURIRefreshCallback>> queuedRCMap = new HashMap<Map<String, RequestConstraint>, List<IURIRefreshCallback>>();
private Map<Map<String, RequestConstraint>, List<IURIRefreshCallback>> queryRCMap = new HashMap<Map<String, RequestConstraint>, List<IURIRefreshCallback>>();
/** /**
* Singleton accessor * Singleton accessor
* *
@ -193,14 +196,29 @@ public class URICatalog extends DecisionTree<List<IURIRefreshCallback>> {
do { do {
workingTime = time; workingTime = time;
Map<Map<String, RequestConstraint>, List<IURIRefreshCallback>> runRCList = null; Map<Map<String, RequestConstraint>, List<IURIRefreshCallback>> runRCList = null;
Map<Map<String, RequestConstraint>, List<IURIRefreshCallback>> runQueryList = null;
synchronized (URICatalog.this) { synchronized (URICatalog.this) {
runRCList = queuedRCMap; if (queuedRCMap.size() > 0) {
queuedRCMap = new HashMap<Map<String, RequestConstraint>, List<IURIRefreshCallback>>(); runRCList = queuedRCMap;
} queuedRCMap = new HashMap<Map<String, RequestConstraint>, List<IURIRefreshCallback>>();
}
catalogAndQueryDataURIsInternal(runRCList, monitor); if (queryRCMap.size() > 0) {
runQueryList = queryRCMap;
queryRCMap = new HashMap<Map<String, RequestConstraint>, List<IURIRefreshCallback>>();
}
}
if (runRCList != null) {
catalogAndQueryDataURIsInternal(runRCList, monitor);
}
if (runQueryList != null) {
doQuery(runQueryList, monitor);
}
} while (!monitor.isCanceled() && workingTime != time } while (!monitor.isCanceled() && workingTime != time
&& queuedRCMap.size() > 0); && (queuedRCMap.size() > 0 || queryRCMap.size() > 0));
return Status.OK_STATUS; return Status.OK_STATUS;
} }
} }
@ -245,40 +263,24 @@ public class URICatalog extends DecisionTree<List<IURIRefreshCallback>> {
try { try {
for (Map.Entry<Map<String, RequestConstraint>, List<IURIRefreshCallback>> entry : rcMap for (Map.Entry<Map<String, RequestConstraint>, List<IURIRefreshCallback>> entry : rcMap
.entrySet()) { .entrySet()) {
Map<String, RequestConstraint> map = entry.getKey();
List<IURIRefreshCallback> runnable = entry.getValue();
if (monitor.isCanceled()) { if (monitor.isCanceled()) {
// If we are cancelled add our runnables back into the queue // If we are cancelled add our runnables back into the queue
// instead of requesting times // instead of requesting times
synchronized (this) { synchronized (this) {
List<IURIRefreshCallback> runnables = queuedRCMap List<IURIRefreshCallback> runnables = queuedRCMap
.get(entry.getKey()); .get(map);
if (runnables == null) { if (runnables == null) {
queuedRCMap.put(entry.getKey(), entry.getValue()); queuedRCMap.put(map, runnable);
} else { } else {
runnables.addAll(entry.getValue()); runnables.addAll(runnable);
} }
} }
continue; continue;
} }
Map<String, RequestConstraint> map = entry.getKey(); doCallbacks(map, runnable);
DataTime[] dt = DataCubeContainer.performTimeQuery(map, true);
DataTime newDataTime = null;
if (dt != null && dt.length > 0) {
newDataTime = dt[dt.length - 1];
}
List<IURIRefreshCallback> runnable = entry.getValue();
final DataTime dataTime = newDataTime;
if (runnable != null) {
Iterator<IURIRefreshCallback> iterator = runnable
.iterator();
while (iterator.hasNext()) {
final IURIRefreshCallback r = iterator.next();
r.updateTime(dataTime);
}
}
for (Map<String, RequestConstraint> updateMap : DataCubeContainer for (Map<String, RequestConstraint> updateMap : DataCubeContainer
.getBaseUpdateConstraints(map)) { .getBaseUpdateConstraints(map)) {
insert(updateMap, rcMap.get(map)); insert(updateMap, rcMap.get(map));
@ -297,6 +299,76 @@ public class URICatalog extends DecisionTree<List<IURIRefreshCallback>> {
} }
private void doCallbacks(Map<String, RequestConstraint> map,
List<IURIRefreshCallback> runnable) throws VizException {
DataTime[] dt = DataCubeContainer.performTimeQuery(map, true);
DataTime newDataTime = null;
if (dt != null && dt.length > 0) {
newDataTime = dt[dt.length - 1];
}
final DataTime dataTime = newDataTime;
if (runnable != null) {
Iterator<IURIRefreshCallback> iterator = runnable.iterator();
while (iterator.hasNext()) {
final IURIRefreshCallback r = iterator.next();
r.updateTime(dataTime);
}
}
}
/**
* Perform query using the existing constraints in map.
*
* @param map
*/
public void query(Map<String, RequestConstraint> map) {
synchronized (this) {
List<List<IURIRefreshCallback>> runableList = searchTreeUsingContraints(map);
List<IURIRefreshCallback> runnables = queryRCMap.get(map);
if (runnables == null) {
runnables = new ArrayList<URICatalog.IURIRefreshCallback>();
queryRCMap.put(map, runnables);
}
for (List<IURIRefreshCallback> runnable : runableList) {
runnables.addAll(runnable);
}
rebuildSchedulerJob.schedule();
}
}
private void doQuery(
Map<Map<String, RequestConstraint>, List<IURIRefreshCallback>> queryMap,
IProgressMonitor monitor) {
try {
for (Map.Entry<Map<String, RequestConstraint>, List<IURIRefreshCallback>> entry : queryMap
.entrySet()) {
Map<String, RequestConstraint> map = entry.getKey();
List<IURIRefreshCallback> runnable = entry.getValue();
if (monitor.isCanceled()) {
// If we are cancelled add our runnables back into the queue
// instead of requesting times
synchronized (this) {
List<IURIRefreshCallback> runnables = queryRCMap
.get(map);
if (runnables == null) {
queryRCMap.put(map, runnable);
} else {
runnables.addAll(runnable);
}
}
continue;
}
doCallbacks(map, runnable);
}
} catch (VizException e) {
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
}
}
private void insert(Map<String, RequestConstraint> map, private void insert(Map<String, RequestConstraint> map,
List<IURIRefreshCallback> runnables) throws VizException { List<IURIRefreshCallback> runnables) throws VizException {
insertCriteria(map, runnables, false); insertCriteria(map, runnables, false);
@ -306,7 +378,5 @@ public class URICatalog extends DecisionTree<List<IURIRefreshCallback>> {
/** check and update most recent time */ /** check and update most recent time */
public abstract void updateTime(DataTime time); public abstract void updateTime(DataTime time);
} }
} }

View file

@ -46,6 +46,8 @@ import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.BinOffset; import com.raytheon.uf.common.time.BinOffset;
import com.raytheon.uf.common.time.DataTime; import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.ISimulatedTimeChangeListener;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.VariableSubstitutionUtil; import com.raytheon.uf.viz.core.VariableSubstitutionUtil;
import com.raytheon.uf.viz.core.VizApp; import com.raytheon.uf.viz.core.VizApp;
import com.raytheon.uf.viz.core.exception.VizException; import com.raytheon.uf.viz.core.exception.VizException;
@ -84,6 +86,7 @@ import com.raytheon.viz.ui.editor.AbstractEditor;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Mar 12, 2009 chammack Initial creation * Mar 12, 2009 chammack Initial creation
* Jan 14, 2013 1442 rferrel Add Simulated Time Change Listener.
* *
* </pre> * </pre>
* *
@ -127,6 +130,12 @@ public class BundleContributionItem extends ContributionItem {
protected boolean performQuery = true; protected boolean performQuery = true;
/**
* A flag to indicate simulated time has changed and item's values needs to
* be refreshed the next time it is displayed.
*/
private boolean timeChangeUpdate = false;
public BundleContributionItem(CommonBundleMenuContribution contribution, public BundleContributionItem(CommonBundleMenuContribution contribution,
VariableSubstitution[] includeSubstitutions) throws VizException { VariableSubstitution[] includeSubstitutions) throws VizException {
super(VariableSubstitutionUtil.processVariables(contribution.id, super(VariableSubstitutionUtil.processVariables(contribution.id,
@ -165,6 +174,25 @@ public class BundleContributionItem extends ContributionItem {
this.substitutions); this.substitutions);
} }
} }
// The bundle persists for the life of CAVE; no need to remove the
// listener.
ISimulatedTimeChangeListener stcl = new ISimulatedTimeChangeListener() {
@Override
public void timechanged() {
// Updating the value here will generate a flood of requests for
// all bundles.
//
// This will force the update of the item's value the next time
// it is displayed.
//
// Any open widget using the bundle will need to handle the
// update.
timeChangeUpdate = true;
}
};
SimulatedTime.getSystemTime().addSimulatedTimeChangeListener(stcl);
} }
/* /*
@ -212,6 +240,22 @@ public class BundleContributionItem extends ContributionItem {
}); });
} }
/**
* This allows a widget such as an open dialog to force the bundle to query
* for new values based on Simulated Time. Allows a widget to refresh its
* display after a user has modified Simulated Time.
*/
public void refreshText() {
lastUsedTime = null;
if (pdoMapList != null && pdoMapList.size() > 0) {
URICatalog cat = URICatalog.getInstance();
for (BundleDataItem d : pdoMapList) {
cat.query(d.metadata);
}
}
timeChangeUpdate = false;
}
protected synchronized void updateMenuText() { protected synchronized void updateMenuText() {
if (widget == null) if (widget == null)
return; return;
@ -325,6 +369,11 @@ public class BundleContributionItem extends ContributionItem {
* *
*/ */
protected void onShow() { protected void onShow() {
if (timeChangeUpdate) {
refreshText();
return;
}
if (performQuery) { if (performQuery) {
if (!shownBefore) { if (!shownBefore) {
shownBefore = true; shownBefore = true;
@ -438,7 +487,6 @@ public class BundleContributionItem extends ContributionItem {
URICatalog.getInstance().catalogAndQueryDataURI(d.metadata, URICatalog.getInstance().catalogAndQueryDataURI(d.metadata,
new BundleRefreshCallback(d.offset)); new BundleRefreshCallback(d.offset));
} }
} }
} }

View file

@ -35,7 +35,10 @@ import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.PlatformUI; import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.services.IServiceLocator; import org.eclipse.ui.services.IServiceLocator;
import com.raytheon.uf.common.time.ISimulatedTimeChangeListener;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.ContextManager; import com.raytheon.uf.viz.core.ContextManager;
import com.raytheon.uf.viz.ui.menus.widgets.BundleContributionItem;
import com.raytheon.viz.ui.VizWorkbenchManager; import com.raytheon.viz.ui.VizWorkbenchManager;
import com.raytheon.viz.ui.dialogs.CaveSWTDialog; import com.raytheon.viz.ui.dialogs.CaveSWTDialog;
@ -49,6 +52,7 @@ import com.raytheon.viz.ui.dialogs.CaveSWTDialog;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Sep 14, 2011 mnash Initial creation * Sep 14, 2011 mnash Initial creation
* Jan 09, 2013 1442 rferrel Add Simulated Time Change Listener.
* *
* </pre> * </pre>
* *
@ -64,6 +68,12 @@ public class TearOffMenuDialog extends CaveSWTDialog {
private Composite fullComp; private Composite fullComp;
/**
* Listener to force the dialog's items' display to be updated when user
* changes Simulated time.
*/
ISimulatedTimeChangeListener stcl;
/** /**
* @param parentShell * @param parentShell
*/ */
@ -170,14 +180,36 @@ public class TearOffMenuDialog extends CaveSWTDialog {
addListener(SWT.Close, deactivate); addListener(SWT.Close, deactivate);
activate.handleEvent(new Event()); activate.handleEvent(new Event());
stcl = new ISimulatedTimeChangeListener() {
@Override
public void timechanged() {
updateItems();
}
};
SimulatedTime.getSystemTime().addSimulatedTimeChangeListener(stcl);
} }
@Override @Override
protected void disposed() { protected void disposed() {
SimulatedTime.getSystemTime().removeSimulatedTimeChangeListener(stcl);
for (Control control : fullComp.getChildren()) { for (Control control : fullComp.getChildren()) {
control.dispose(); control.dispose();
} }
super.disposed(); super.disposed();
} }
/**
* Force update of item's display.
*/
private void updateItems() {
// items[0] is the tear off object and is not in the dialog's display.
for (int index = 1; index < items.length; ++index) {
MenuItem item = items[index];
if (item.getData() instanceof BundleContributionItem) {
((BundleContributionItem) item.getData()).refreshText();
}
}
}
} }

View file

@ -38,6 +38,8 @@ import com.raytheon.edex.msg.PracticeDataURINotificationMessage;
import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.viz.core.RecordFactory; import com.raytheon.uf.viz.core.RecordFactory;
import com.raytheon.uf.viz.core.alerts.AlertMessage; import com.raytheon.uf.viz.core.alerts.AlertMessage;
import com.raytheon.uf.viz.core.exception.NoPluginException; import com.raytheon.uf.viz.core.exception.NoPluginException;
@ -65,6 +67,7 @@ import com.raytheon.viz.core.mode.CAVEMode;
* 02/28/08 966 chammack Refactored to support generalized listeners * 02/28/08 966 chammack Refactored to support generalized listeners
* 05/08/08 1127 randerso Changed to implement INotificationObserver * 05/08/08 1127 randerso Changed to implement INotificationObserver
* 10/06/08 1433 chammack Updated to use VizStatus * 10/06/08 1433 chammack Updated to use VizStatus
* 01/14/2013 1442 rferrel Filter out simulated time "future" alerts.
* </pre> * </pre>
* *
* @author bphillip * @author bphillip
@ -233,6 +236,27 @@ public class ProductAlertObserver implements INotificationObserver {
// collection for future messages // collection for future messages
Collection<AlertMessage> messagesToProc = messages; Collection<AlertMessage> messagesToProc = messages;
messages = new ConcurrentLinkedQueue<AlertMessage>(); messages = new ConcurrentLinkedQueue<AlertMessage>();
SimulatedTime time = SimulatedTime.getSystemTime();
if (!time.isRealTime()) {
// Filter out any "future" alerts.
long simTime = time.getTime().getTime();
Iterator<AlertMessage> iter = messagesToProc.iterator();
while (iter.hasNext()) {
AlertMessage message = iter.next();
Map<String, Object> attribs = new HashMap<String, Object>(
message.decodedAlert);
DataTime messageTime = (DataTime) attribs
.get("dataTime");
if (messageTime != null
&& (simTime < messageTime.getRefTime()
.getTime())) {
iter.remove();
}
}
if (messagesToProc.isEmpty()) {
return Status.OK_STATUS;
}
}
try { try {
observer.alertArrived(messagesToProc); observer.alertArrived(messagesToProc);

View file

@ -46,6 +46,7 @@ import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.commands.ICommandService;
import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.progress.UIJob;
import com.raytheon.uf.common.time.ISimulatedTimeChangeListener;
import com.raytheon.uf.common.time.SimulatedTime; import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.uf.common.time.TimeRange; import com.raytheon.uf.common.time.TimeRange;
import com.raytheon.uf.viz.core.VizApp; import com.raytheon.uf.viz.core.VizApp;
@ -68,6 +69,7 @@ import com.raytheon.viz.gfe.temporaleditor.TemporalEditor;
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Apr 7, 2009 randerso Redesigned * Apr 7, 2009 randerso Redesigned
* May 18, 2009 2159 rjpeter Added temporal editor. * May 18, 2009 2159 rjpeter Added temporal editor.
* Jan 14, 2013 1442 rferrel Add SimulatedTimeChangeListener.
* </pre> * </pre>
* *
* @author randerso * @author randerso
@ -151,11 +153,13 @@ public class GridManager implements IGridManager,
/** /**
* Job to update the time display * Job to update the time display
*/ */
private static class UpdateJob extends UIJob { private static class UpdateJob extends UIJob implements
ISimulatedTimeChangeListener {
public UpdateJob() { public UpdateJob() {
super("GridManagerUpdate"); super("GridManagerUpdate");
this.setSystem(true); this.setSystem(true);
SimulatedTime.getSystemTime().addSimulatedTimeChangeListener(this);
} }
/* /*
@ -178,6 +182,18 @@ public class GridManager implements IGridManager,
return Status.OK_STATUS; return Status.OK_STATUS;
} }
/*
* (non-Javadoc)
*
* @see
* com.raytheon.uf.common.time.ISimulatedTimeChangeListener#timechanged
* ()
*/
@Override
public void timechanged() {
wakeUp();
}
} }
/** /**

View file

@ -86,6 +86,7 @@ import com.raytheon.viz.core.units.UnitRegistrar;
* Oct 02, 2012 #1236 dgilling Allow SimulatedTime to be set from * Oct 02, 2012 #1236 dgilling Allow SimulatedTime to be set from
* the command line even if practice * the command line even if practice
* mode is off. * mode is off.
* Jan 09, 2013 #1442 rferrel Changes to notify SimultedTime listeners.
* *
* </pre> * </pre>
* *
@ -319,11 +320,13 @@ public abstract class AbstractCAVEComponent implements IStandaloneComponent {
} }
SimulatedTime systemTime = SimulatedTime.getSystemTime(); SimulatedTime systemTime = SimulatedTime.getSystemTime();
systemTime.notifyListeners(false);
systemTime.setRealTime(); systemTime.setRealTime();
systemTime.setFrozen(isFrozen); systemTime.setFrozen(isFrozen);
if (timeValue != 0) { if (timeValue != 0) {
systemTime.setTime(new Date(timeValue)); systemTime.setTime(new Date(timeValue));
} }
systemTime.notifyListeners(true);
} }
/** /**

View file

@ -54,6 +54,7 @@ import com.raytheon.viz.ui.VizWorkbenchManager;
* May 21, 2008 1122 ebabin Updated to use new StatusBarDisplay. * May 21, 2008 1122 ebabin Updated to use new StatusBarDisplay.
* 09JUL2008 1234 ebabin Updates for color, and display issues. * 09JUL2008 1234 ebabin Updates for color, and display issues.
* Oct 17, 2012 1229 rferrel Made dialog non-blocking. * Oct 17, 2012 1229 rferrel Made dialog non-blocking.
* Jan 09, 2013 1442 rferrel Changes to notify Simulated Time listeners
* *
* </pre> * </pre>
* *
@ -338,8 +339,9 @@ public class SetTimeDialog extends CaveSWTDialog {
} }
private void setVizTime() { private void setVizTime() {
SimulatedTime systemTime = SimulatedTime.getSystemTime();
if (useCurrentTimeRdo.getSelection()) { if (useCurrentTimeRdo.getSelection()) {
SimulatedTime.getSystemTime().setRealTime(); systemTime.setRealTime();
} else if (setTimeRdo.getSelection()) { } else if (setTimeRdo.getSelection()) {
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
cal.clear(); cal.clear();
@ -348,9 +350,10 @@ public class SetTimeDialog extends CaveSWTDialog {
this.daySpnr.getSelection(), this.hourSpnr.getSelection(), this.daySpnr.getSelection(), this.hourSpnr.getSelection(),
this.minuteSpnr.getSelection(), this.minuteSpnr.getSelection(),
this.secondSpnr.getSelection()); this.secondSpnr.getSelection());
SimulatedTime.getSystemTime().setFrozen( systemTime.notifyListeners(false);
freezeTimeChk.getSelection()); systemTime.setFrozen(freezeTimeChk.getSelection());
SimulatedTime.getSystemTime().setTime(cal.getTime()); systemTime.setTime(cal.getTime());
systemTime.notifyListeners(true);
} }
} }

View file

@ -45,6 +45,7 @@ import org.eclipse.ui.progress.UIJob;
import com.raytheon.uf.common.status.IUFStatusHandler; import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus; import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority; import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.ISimulatedTimeChangeListener;
import com.raytheon.uf.common.time.SimulatedTime; import com.raytheon.uf.common.time.SimulatedTime;
import com.raytheon.viz.core.mode.CAVEMode; import com.raytheon.viz.core.mode.CAVEMode;
import com.raytheon.viz.ui.actions.ShowTimeDialog; import com.raytheon.viz.ui.actions.ShowTimeDialog;
@ -62,6 +63,7 @@ import com.raytheon.viz.ui.actions.ShowTimeDialog;
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Nov 30,2007 461 bphillip Initial Creation * Nov 30,2007 461 bphillip Initial Creation
* 09JUL2008 1234 ebabin Updates for color, and display issues. * 09JUL2008 1234 ebabin Updates for color, and display issues.
* Jan 09, 2013 1442 rferrel Added Simulated Time Change listener.
* *
* </pre> * </pre>
* *
@ -140,6 +142,8 @@ public class TimeDisplay extends ContributionItem {
// workaround for the random tooltip following the cursor // workaround for the random tooltip following the cursor
private boolean displayTooltip = true; private boolean displayTooltip = true;
private ISimulatedTimeChangeListener timeChangeListener;
/** /**
* Constructor * Constructor
*/ */
@ -150,6 +154,15 @@ public class TimeDisplay extends ContributionItem {
gmtFormatter.setTimeZone(GMT); gmtFormatter.setTimeZone(GMT);
localFormatter = new SimpleDateFormat(localPattern); localFormatter = new SimpleDateFormat(localPattern);
timeChangeListener = new ISimulatedTimeChangeListener() {
@Override
public void timechanged() {
update();
}
};
SimulatedTime.getSystemTime().addSimulatedTimeChangeListener(
timeChangeListener);
} }
/* /*
@ -160,6 +173,8 @@ public class TimeDisplay extends ContributionItem {
@Override @Override
public void dispose() { public void dispose() {
activeList.remove(this); activeList.remove(this);
SimulatedTime.getSystemTime().removeSimulatedTimeChangeListener(
timeChangeListener);
super.dispose(); super.dispose();
} }

View file

@ -0,0 +1,45 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.time;
/**
* Interface for hooks to send notification when simulated time is modified.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jan 09, 2013 1442 rferrel Initial creation
*
* </pre>
*
* @author rferrel
* @version 1.0
*/
public interface ISimulatedTimeChangeListener {
/**
* Method called with simulated time is modified.
*/
public void timechanged();
}

View file

@ -19,10 +19,9 @@
**/ **/
package com.raytheon.uf.common.time; package com.raytheon.uf.common.time;
import java.text.SimpleDateFormat; import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.TimeZone; import java.util.List;
/** /**
* Simulated time clock with offset and scale capabilities. * Simulated time clock with offset and scale capabilities.
@ -38,6 +37,8 @@ import java.util.TimeZone;
* Jul 16, 2008 randerso Initial creation * Jul 16, 2008 randerso Initial creation
* Aug 24, 2012 0743 djohnson Add option to use milliseconds for operations, change to singleton. * Aug 24, 2012 0743 djohnson Add option to use milliseconds for operations, change to singleton.
* Nov 02, 2012 1302 djohnson Change mistakenly public constructor to private. * Nov 02, 2012 1302 djohnson Change mistakenly public constructor to private.
* Jan 07, 2013 1442 rferrel Changes to add/remove/notify Simulated Time Change Listeners.
* Use SimulatedTimeTest for unit testing.
* *
* </pre> * </pre>
* *
@ -51,6 +52,10 @@ public final class SimulatedTime {
*/ */
private static final SimulatedTime systemTime = new SimulatedTime(); private static final SimulatedTime systemTime = new SimulatedTime();
private List<ISimulatedTimeChangeListener> listeners;
private boolean doNotify;
/** /**
* Retrieve the system global simulate time instance * Retrieve the system global simulate time instance
* *
@ -99,41 +104,19 @@ public final class SimulatedTime {
* *
*/ */
private SimulatedTime() { private SimulatedTime() {
this.listeners = new ArrayList<ISimulatedTimeChangeListener>();
this.doNotify = true;
setRealTime(); setRealTime();
} }
/** public synchronized void addSimulatedTimeChangeListener(
* Creates a simulated time starting at the specified time with ISimulatedTimeChangeListener listener) {
* acceleration/deceleration, optionally frozen. listeners.add(listener);
*
* @param date
* starting time
* @param scale
* 1.0 for normal time rate, >1.0 for accelerated time < 1.0 for
* decelerated time. If negative time will run backward.
* @param isFrozen
* true to freeze time
*/
private SimulatedTime(Date date, double scale, boolean isFrozen) {
this(date.getTime(), scale, isFrozen);
} }
/** public synchronized void removeSimulatedTimeChangeListener(
* Creates a simulated time starting at the specified time with ISimulatedTimeChangeListener listener) {
* acceleration/deceleration, optionally frozen. listeners.remove(listener);
*
* @param millis
* starting time in milliseconds
* @param scale
* 1.0 for normal time rate, >1.0 for accelerated time < 1.0 for
* decelerated time. If negative time will run backward.
* @param isFrozen
* true to freeze time
*/
private SimulatedTime(long millis, double scale, boolean isFrozen) {
setTime(millis);
this.scale = scale;
this.isFrozen = isFrozen;
} }
/** /**
@ -153,6 +136,7 @@ public final class SimulatedTime {
offset = 0; offset = 0;
scale = 1.0; scale = 1.0;
isFrozen = false; isFrozen = false;
fireTimeChangeListeners();
} }
/** /**
@ -193,6 +177,7 @@ public final class SimulatedTime {
baseTime = now(); baseTime = now();
offset = millis - baseTime; offset = millis - baseTime;
} }
fireTimeChangeListeners();
} }
/** /**
@ -213,7 +198,10 @@ public final class SimulatedTime {
* decelerated time. If negative time will run backward. * decelerated time. If negative time will run backward.
*/ */
public void setScale(double scale) { public void setScale(double scale) {
this.scale = scale; if (this.scale != scale) {
this.scale = scale;
fireTimeChangeListeners();
}
} }
/** /**
@ -243,42 +231,34 @@ public final class SimulatedTime {
offset = frozenTime - baseTime; offset = frozenTime - baseTime;
} }
this.isFrozen = isFrozen; this.isFrozen = isFrozen;
fireTimeChangeListeners();
} }
/** /**
* Test routine * Allows turning off notification of listeners when making several updates
* at the same time. Changing to false turns off notification. Changing to
* true allows notification and triggers a notification.
* *
* @param args * @param doNotify
* N/A
*/ */
public static void main(String[] args) { public void notifyListeners(boolean doNotify) {
String timeFormat = "HH:mm:ss z dd-MMM-yyyy"; if (this.doNotify != doNotify) {
TimeZone GMT = TimeZone.getTimeZone("GMT"); this.doNotify = doNotify;
fireTimeChangeListeners();
}
}
SimpleDateFormat gmtFormatter = new SimpleDateFormat(timeFormat); /**
gmtFormatter.setTimeZone(GMT); * Notify listeners of time change.
*/
SimpleDateFormat local = new SimpleDateFormat(timeFormat); private void fireTimeChangeListeners() {
if (doNotify) {
Calendar cal = Calendar.getInstance(GMT); // Copy to make thread safe.
cal.clear(); List<ISimulatedTimeChangeListener> list = new ArrayList<ISimulatedTimeChangeListener>(
cal.set(1960, 5, 5, 0, 0, 0); listeners);
for (ISimulatedTimeChangeListener listener : list) {
SimulatedTime simTime = new SimulatedTime(cal.getTime(), 2.0, false); listener.timechanged();
for (int i = 0; i < 15; i++) {
Date date = simTime.getTime();
System.out.println(gmtFormatter.format(date) + ", "
+ local.format(date)
+ (simTime.isFrozen() ? " frozen" : ""));
simTime.setFrozen(i >= 5 && i < 10);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
} }
} }
} }

View file

@ -153,8 +153,8 @@ HDS ^(O.[JMNQ].{1,3}) KWBJ (..)(..)(..)[^!]*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)
# OTLA88 KWBI 010000 /mSST !grib/ncep/SST/#235/201102010000/F000/TMP/sfc/ # OTLA88 KWBI 010000 /mSST !grib/ncep/SST/#235/201102010000/F000/TMP/sfc/
#HDS ^(O.L.{1,3}) KWBI (..)(..)(..)[^!]*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3})/([^/]*) #HDS ^(O.L.{1,3}) KWBI (..)(..)(..)[^!]*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3})/([^/]*)
# FILE -overwrite -log -close -edex /data_store/\5/(\2:yyyy)(\2:mm)\2/\3/\6/GRID\7/\9Z_\(10)_\(11)-\1_KWBI_\2\3\4_(seq).\5.%Y%m%d%H # FILE -overwrite -log -close -edex /data_store/\5/(\2:yyyy)(\2:mm)\2/\3/\6/GRID\7/\9Z_\(10)_\(11)-\1_KWBI_\2\3\4_(seq).\5.%Y%m%d%H
#!MAINT! Combined the above two patterns into one. The only difference was KWBM vs KWBI #!MAINT! Combined the above two patterns into one. The only difference was KWBM vs KWBI and the O.L or O.N in \1
HDS ^(O.N.{1,3}) (KWBM|KWBI) (..)(..)(..)[^!]*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3})/([^/]*) HDS ^(O.[LN].{1,3}) (KWBM|KWBI) (..)(..)(..)[^!]*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3})/([^/]*)
FILE -overwrite -log -close -edex /data_store/\6/(\3:yyyy)(\3:mm)\3/\4/\7/GRID\8/\(10)Z_\(11)_\(12)-\1_\2_\3\4\5_(seq).\6.%Y%m%d%H FILE -overwrite -log -close -edex /data_store/\6/(\3:yyyy)(\3:mm)\3/\4/\7/GRID\8/\(10)Z_\(11)_\(12)-\1_\2_\3\4\5_(seq).\6.%Y%m%d%H
# AWIPS1: GRID ^OEBA88.*KNWC /Grid/SBN/Raw # AWIPS1: GRID ^OEBA88.*KNWC /Grid/SBN/Raw
@ -335,6 +335,14 @@ HDS ^(ZETA98) (KTUA|PACR|KSTR|KRSA|KORN|KRHA|KKRF|KMSR|KTAR|KPTR|KTIR|KALR|KFWR)
ANY ^(ZDIA98) (....) (..)(..)(..)[^!]*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3}) ANY ^(ZDIA98) (....) (..)(..)(..)[^!]*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3})
FILE -overwrite -log -close -edex /data_store/\6/(\3:yyyy)(\3:mm)\3/\4/\7/GRID\8/\(10)Z_\(11)-\1_\2_\3\4\5_(seq).\6.%Y%m%d%H FILE -overwrite -log -close -edex /data_store/\6/(\3:yyyy)(\3:mm)\3/\4/\7/GRID\8/\(10)Z_\(11)-\1_\2_\3\4\5_(seq).\6.%Y%m%d%H
# Restore from build 12.12 with new grib storage convention.
HRS ^(YA)([WX])(A..) (KKCI) (..)(..)(..).*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3})
FILE -overwrite -log -close -edex /data_store/\8/(\5:yyyy)(\5:mm)\5/\6/\9/GRID\(10)/\(12)Z_\(13)-\1\2\3_\4_5\6\7_(seq).\8.%Y%m%d%H
HRS ^(ZV)(W)([ADGJM]..) (KKCI) (..)(..)(..).*!(grib|grib2)/[^/]*/([^/]*)/#([^/]*)/([0-9]{8})([0-9]{4})/(F[0-9]{3})
FILE -overwrite -log -close -edex /data_store/\8/(\5:yyyy)(\5:mm)\5/\6/\9/GRID\(10)/\(12)Z_\(13)-\1\2\3_\4_5\6\7_(seq).\8.%Y%m%d%H
# AWIPS1: POINT .*IUPT(0[1-4]).*|.*IUPT40.* /ispan/bufr/profiler # AWIPS1: POINT .*IUPT(0[1-4]).*|.*IUPT40.* /ispan/bufr/profiler
# IUPT01 KBOU 020300 # IUPT01 KBOU 020300
# AWIPS1: POINT ^IUAK01.* /ispan/bufr/profiler # AWIPS1: POINT ^IUAK01.* /ispan/bufr/profiler

View file

@ -25,6 +25,7 @@ import java.util.Date;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import com.raytheon.uf.common.util.TestUtil; import com.raytheon.uf.common.util.TestUtil;
@ -38,6 +39,7 @@ import com.raytheon.uf.common.util.TestUtil;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Aug 24, 2012 djohnson Initial creation * Aug 24, 2012 djohnson Initial creation
* Jan 15, 2013 1442 rferrel Added tests for notify Time changes.
* *
* </pre> * </pre>
* *
@ -73,4 +75,29 @@ public class SimulatedTimeTest {
TestUtil.assertNotEquals(start, end); TestUtil.assertNotEquals(start, end);
} }
@Test
public void testListenerNotifiedOnTimeChanges() throws Exception {
SimulatedTime simulatedTime = SimulatedTime.getSystemTime();
ISimulatedTimeChangeListener listener = Mockito
.mock(ISimulatedTimeChangeListener.class);
simulatedTime.addSimulatedTimeChangeListener(listener);
try {
simulatedTime.setFrozen(true);
Mockito.verify(listener).timechanged();
} finally {
simulatedTime.removeSimulatedTimeChangeListener(listener);
}
}
@Test
public void testRemovedListenerNotNotifiedOnTimeChanges() throws Exception {
SimulatedTime simulatedTime = SimulatedTime.getSystemTime();
ISimulatedTimeChangeListener listener = Mockito
.mock(ISimulatedTimeChangeListener.class);
simulatedTime.addSimulatedTimeChangeListener(listener);
simulatedTime.removeSimulatedTimeChangeListener(listener);
simulatedTime.setFrozen(true);
Mockito.verify(listener, Mockito.never()).timechanged();
}
} }