Issue #2868 plot delegate python now uses PythonCoordinatorJob API,

Refactored plot data request, image creation, and sample
              threading

Change-Id: I3e1a3dc70b2346aa826ec4509ee7a3e59c61a918

Former-commit-id: 85b411bca3 [formerly 2418c9bdfe] [formerly fe33896e7e] [formerly 85b411bca3 [formerly 2418c9bdfe] [formerly fe33896e7e] [formerly c4beb293b9 [formerly fe33896e7e [formerly e421731415d798487c96dd1e56b6f02347d9aa26]]]]
Former-commit-id: c4beb293b9
Former-commit-id: df01318647 [formerly 0acb4621f9] [formerly 3d803664963a310bbf45fe46e8f20c9255a3b529 [formerly 774091461c]]
Former-commit-id: c0e78be8fc6eca4dfac050feb6b22af3fded1f5c [formerly 96339798b5]
Former-commit-id: 5b6683bd56
This commit is contained in:
Nate Jensen 2014-03-21 19:33:48 -05:00
parent 531dafbe7d
commit 6897e58b4e
16 changed files with 1055 additions and 597 deletions

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Pointdata Plug-in
Bundle-SymbolicName: com.raytheon.viz.pointdata;singleton:=true
Bundle-Version: 1.12.1174.qualifier
Bundle-Version: 1.14.0.qualifier
Bundle-Activator: com.raytheon.viz.pointdata.Activator
Bundle-Vendor: Raytheon
Eclipse-RegisterBuddy: com.raytheon.viz.core, com.raytheon.uf.viz.core
@ -20,7 +20,8 @@ Require-Bundle: org.apache.batik,
com.raytheon.uf.common.pointdata;bundle-version="1.12.1174",
com.raytheon.uf.common.dataplugin.level;bundle-version="1.12.1174",
com.raytheon.uf.common.colormap;bundle-version="1.12.1174",
com.raytheon.uf.common.wxmath
com.raytheon.uf.common.wxmath,
com.raytheon.uf.common.python.concurrent
Bundle-ActivationPolicy: lazy
Export-Package: com.raytheon.viz.pointdata,
com.raytheon.viz.pointdata.drawables,

View file

@ -1,196 +0,0 @@
/**
* 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.viz.pointdata;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.graphics.RGB;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.MapDescriptor;
import com.raytheon.viz.pointdata.thread.GetDataTask;
/**
* Thread pool for requesting data for plots. Each data request job is tied to a
* plot generator job.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jun 30, 2011 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class PlotDataThreadPool {
private static final int THREADS = 2;
protected ConcurrentLinkedQueue<GetDataTask> stationQueue = new ConcurrentLinkedQueue<GetDataTask>();
protected List<PlotModelDataRequestJob> jobList = new ArrayList<PlotModelDataRequestJob>();
public PlotDataThreadPool(IGraphicsTarget aTarget,
MapDescriptor mapDescriptor, String plotModelFile, String levelKey,
String plugin, HashMap<String, RequestConstraint> constraintMap,
IPlotModelGeneratorCaller caller) throws VizException {
for (int i = 0; i < THREADS; i++) {
jobList.add(new PlotModelDataRequestJob(aTarget, mapDescriptor,
plotModelFile, levelKey, plugin, constraintMap, caller,
this));
}
}
/**
* Adds a batch of stations to the queue
*
* @param station
*
*/
public void queueStation(GetDataTask task) {
List<PlotInfo[]> station = task.getStations();
Iterator<PlotInfo[]> itr = station.iterator();
while (itr.hasNext()) {
PlotInfo[] infos = itr.next();
boolean allQueued = true;
for (PlotInfo info : infos) {
switch (task.getRequestType()) {
case PLOT_ONLY:
if (!info.plotQueued) {
allQueued = false;
info.plotQueued = true;
}
break;
case SAMPLE_ONLY:
if (!info.sampleQueued) {
allQueued = false;
info.sampleQueued = true;
}
break;
case PLOT_AND_SAMPLE:
if (!info.sampleQueued) {
allQueued = false;
info.sampleQueued = true;
}
if (!info.plotQueued) {
allQueued = false;
info.plotQueued = true;
}
}
}
if (allQueued) {
itr.remove();
}
}
if (station.size() > 0) {
task.setStations(station);
stationQueue.add(task);
for (PlotModelDataRequestJob job : jobList) {
if (job.getState() != Job.RUNNING) {
job.schedule();
}
}
}
}
public void setPlotModelColor(RGB color) {
for (PlotModelDataRequestJob job : jobList) {
job.setPlotModelColor(color);
}
}
public void setPlotModelLineStyle(LineStyle lineStyle) {
for (PlotModelDataRequestJob job : jobList) {
job.setPlotModelLineStyle(lineStyle);
}
}
public void setPlotModelLineWidth(int outlineWidth) {
for (PlotModelDataRequestJob job : jobList) {
job.setPlotModelLineWidth(outlineWidth);
}
}
public void setPlotMissingData(boolean plotMissingData) {
for (PlotModelDataRequestJob job : jobList) {
job.setPlotMissingData(plotMissingData);
}
}
public void setLowerLimit(double lowerLimit) {
for (PlotModelDataRequestJob job : jobList) {
job.setLowerLimit(lowerLimit);
}
}
public void setUpperLimit(double upperLimit) {
for (PlotModelDataRequestJob job : jobList) {
job.setUpperLimit(upperLimit);
}
}
public double getPlotModelWidth() {
return jobList.get(0).getPlotModelWidth();
}
public void setPlotModelSize(long round) {
for (PlotModelDataRequestJob job : jobList) {
job.setPlotModelSize(round);
}
}
public boolean isDone() {
for (PlotModelDataRequestJob job : jobList) {
if (!job.isDone()) {
return false;
}
}
return true;
}
public void shutdown() {
List<PlotModelDataRequestJob> jobListCopy = new ArrayList<PlotModelDataRequestJob>(
jobList);
jobList.clear();
stationQueue.clear();
for (PlotModelDataRequestJob job : jobListCopy) {
job.shutdown();
}
}
}

View file

@ -29,6 +29,7 @@ import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
@ -40,8 +41,6 @@ import javax.measure.converter.UnitConverter;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import jep.JepException;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgentAdapter;
@ -57,10 +56,8 @@ import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import com.raytheon.uf.common.localization.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.PathManagerFactory;
import com.raytheon.uf.common.python.PythonScript;
import com.raytheon.uf.common.python.concurrent.PythonJobCoordinator;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
@ -69,12 +66,16 @@ import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.IMapDescriptor;
import com.raytheon.viz.pointdata.lookup.IAbstractLookupTable;
import com.raytheon.viz.pointdata.lookup.LookupUtils;
import com.raytheon.viz.pointdata.python.CheckPlotValidityExecutor;
import com.raytheon.viz.pointdata.python.PlotPythonScript;
import com.raytheon.viz.pointdata.python.PlotPythonScriptFactory;
import com.raytheon.viz.pointdata.python.SampleTextExecutor;
import com.raytheon.viz.pointdata.rsc.PlotResource2;
import com.raytheon.viz.pointdata.rsc.PlotResourceData;
/**
* A singleton that will create a plot model texture based on a passed in
* MetarRecord object.
* A factory for generating plot images and sample messages by parsing the
* associated plotModel SVG file.
*
* <pre>
*
@ -89,6 +90,7 @@ import com.raytheon.viz.pointdata.rsc.PlotResourceData;
* Sep 05, 2013 2316 bsteffen Unify pirep and ncpirep.
* Sep 05, 2013 2307 dgilling Use better PythonScript constructor.
* Nov 20, 2013 2033 njensen Fix detecting plotModels dirs from multiple plugins
* Mar 21, 2014 2868 njensen Refactored python usage to PythonJobCoordinator
*
* </pre>
*
@ -99,7 +101,7 @@ public class PlotModelFactory2 {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotModelFactory2.class);
private static final String PLOT_MODEL_DIR = "plotModels";
public static final String PLOT_MODEL_DIR = "plotModels";
private static final String DM_ATTRIBUTE = "plotMode";
@ -121,8 +123,6 @@ public class PlotModelFactory2 {
private static final String REQUIRED = "required";
private static String cachedIncludePath;
private final SimpleDateFormat SAMPLE_DATE = new SimpleDateFormat("HHmm");
// Need to include attribute and code to allow for String2String lookups and
@ -137,8 +137,6 @@ public class PlotModelFactory2 {
private Document document;
private GraphicsNode theGraphicsNode;
private final GVTBuilder builder;
private final BridgeContext bridgeContext;
@ -171,9 +169,9 @@ public class PlotModelFactory2 {
private Map<String, BufferedImage> imageCache = null;
private ScriptInfo scriptInfo;
protected final String plotModelFile;
private ScriptInfo sampleScriptInfo;
protected PythonJobCoordinator<PlotPythonScript> python;
public static enum DisplayMode {
TEXT, BARB, TABLE, AVAIL, RANGE, NULL, SAMPLE, ARROW
@ -211,6 +209,10 @@ public class PlotModelFactory2 {
public Node getPlotNode() {
return plotNode;
}
public String getParameter() {
return parameter;
}
}
public class PlotWindElement {
@ -238,6 +240,7 @@ public class PlotModelFactory2 {
byte[] blue = { 0, full };
byte[] green = { 0, zero };
regenerateStyle();
this.plotModelFile = plotModelFile;
this.plotFields = new ArrayList<PlotModelElement>();
this.sampleFields = new ArrayList<PlotModelElement>();
@ -452,12 +455,16 @@ public class PlotModelFactory2 {
imageCache = new HashMap<String, BufferedImage>();
}
NodeList scriptNodes = document.getElementsByTagName("script");
// Only one script node supported
if (scriptNodes.getLength() > 0) {
int nScriptNodes = scriptNodes.getLength();
if (nScriptNodes > 1) {
throw new UnsupportedOperationException(
"Only one script node allowed in plotModel SVG file. Please check and fix "
+ plotModelFile);
} else if (nScriptNodes == 1) {
Element scriptNode = (Element) scriptNodes.item(0);
scriptInfo = new ScriptInfo();
scriptInfo.plotDelegateName = scriptNode
.getAttribute("plotDelegate");
String plotDelegateName = scriptNode.getAttribute("plotDelegate");
NodeList childNodes = scriptNode.getChildNodes();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < childNodes.getLength(); i++) {
@ -466,14 +473,13 @@ public class PlotModelFactory2 {
sb.append(((Text) child).getData());
}
}
if (sb.length() > 0) {
scriptInfo.scriptText = sb.toString();
String scriptText = sb.toString().trim();
if (scriptText.length() > 0) {
PlotPythonScriptFactory pythonFactory = new PlotPythonScriptFactory(
plotModelFile, scriptText, plotDelegateName);
python = PythonJobCoordinator.newInstance(pythonFactory);
}
sampleScriptInfo = new ScriptInfo();
sampleScriptInfo.plotDelegateName = scriptInfo.plotDelegateName;
sampleScriptInfo.scriptText = scriptInfo.scriptText;
// remove the scriptNode in memory so time isn't wasted
// later attempting to render it
scriptNode.getParentNode().removeChild(scriptNode);
@ -491,8 +497,8 @@ public class PlotModelFactory2 {
byte fullr = (byte) color.red;
byte fullg = (byte) color.green;
byte fullb = (byte) color.blue;
String style = "stroke: rgb(" + color.red + "," + color.green + ","
+ color.blue + ");";
// String style = "stroke: rgb(" + color.red + "," + color.green + ","
// + color.blue + ");";
// this.svgRoot.setAttribute("style", style);
// System.out.println(style);
byte[] red = { 0, fullr };
@ -567,8 +573,7 @@ public class PlotModelFactory2 {
}
/**
* Takes the station name and its MetarRecord object and produces a buffered
* image.
* Takes the station data object and produces a buffered image.
*
* @param station
* The station name
@ -591,27 +596,28 @@ public class PlotModelFactory2 {
this.gc.setDestinationGeographicPoint(newWorldLocation[0],
newWorldLocation[1]);
}
StringBuffer imageId = new StringBuffer();
PlotPythonScript script = null;
try {
boolean discard = false;
if (scriptInfo != null) {
script = scriptInfo.getScript();
if (python != null) {
Boolean result = false;
CheckPlotValidityExecutor task = new CheckPlotValidityExecutor(
stationData);
try {
Object result = script.executePlotDelegateMethod("isValid",
"rec", stationData);
if (result instanceof Boolean
&& !((Boolean) result).booleanValue()) {
result = python.submitSyncJob(task);
} catch (Exception e) {
statusHandler.handle(Priority.PROBLEM,
"Error checking if plot is valid for plot model "
+ getPlotModelFilename(), e);
} finally {
if (result.booleanValue() == false) {
return null;
}
} catch (JepException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
}
}
StringBuilder imageId = new StringBuilder();
for (PlotModelElement element : this.plotFields) {
boolean valid = true;
boolean required = element.required;
@ -680,13 +686,13 @@ public class PlotModelFactory2 {
this.plotModelWidth, this.plotModelHeight,
BufferedImage.TYPE_BYTE_INDEXED, tm);
long t0 = System.currentTimeMillis();
this.theGraphicsNode = builder.build(this.bridgeContext,
// long t0 = System.currentTimeMillis();
GraphicsNode graphicsNode = builder.build(this.bridgeContext,
this.document);
Graphics2D g2d = null;
try {
g2d = bufferedImage.createGraphics();
this.theGraphicsNode.primitivePaint(g2d);
graphicsNode.primitivePaint(g2d);
} finally {
if (g2d != null) {
g2d.dispose();
@ -702,9 +708,6 @@ public class PlotModelFactory2 {
} catch (Exception e) {
statusHandler.handle(Priority.PROBLEM,
"Error:" + e.getLocalizedMessage(), e);
} catch (JepException e) {
statusHandler.handle(Priority.PROBLEM,
"Error:" + e.getLocalizedMessage(), e);
}
return null;
@ -712,20 +715,20 @@ public class PlotModelFactory2 {
public synchronized String getStationMessage(PlotData stationData,
String dataURI) {
PlotPythonScript script = null;
StringBuilder sampleMessage = new StringBuilder();
try {
if (sampleScriptInfo != null) {
script = sampleScriptInfo.getScript();
Object result = script.executePlotDelegateMethod("isValid",
"rec", stationData);
if (result instanceof Boolean
&& ((Boolean) result).booleanValue()) {
result = script.executePlotDelegateMethod("getSampleText",
"rec", stationData);
if (result instanceof String) {
sampleMessage.append((String) result);
if (python != null) {
String result = null;
SampleTextExecutor task = new SampleTextExecutor(stationData);
try {
result = python.submitSyncJob(task);
} catch (Exception e) {
statusHandler.handle(Priority.PROBLEM,
"Error getting sample text for plot model "
+ getPlotModelFilename(), e);
} finally {
if (result != null) {
sampleMessage.append(result);
}
}
} else {
@ -736,11 +739,8 @@ public class PlotModelFactory2 {
}
} catch (Exception e) {
// TODO
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
} catch (JepException e) {
// TODO Auto-generated catch block. Please revise as appropriate.
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
statusHandler.handle(Priority.PROBLEM,
"Error generating sample text with " + plotModelFile, e);
}
String message = sampleMessage.toString();
@ -752,7 +752,7 @@ public class PlotModelFactory2 {
}
private void addToImageId(PlotData stationData, String parameters,
StringBuffer imageId) {
StringBuilder imageId) {
for (String parameter : parameters.split(",")) {
switch (stationData.getType(parameter)) {
case STRING:
@ -809,6 +809,7 @@ public class PlotModelFactory2 {
Formatter testing = new Formatter(sb);
testing.format(element.format, displayValue);
sValue = sb.toString();
testing.close();
} else {
sValue = Double.toString(displayValue);
}
@ -1073,6 +1074,7 @@ public class PlotModelFactory2 {
Formatter testing = new Formatter(sb);
testing.format(element.format, displayValue);
sValue = sb.toString();
testing.close();
}
} else {
sValue = Double.toString(displayValue);
@ -1138,6 +1140,7 @@ public class PlotModelFactory2 {
Formatter testing = new Formatter(sb);
testing.format(element.format, displayValue);
sValue = sb.toString();
testing.close();
} else {
sValue = Double.toString(displayValue);
}
@ -1197,8 +1200,8 @@ public class PlotModelFactory2 {
return major * 5;
}
public synchronized List<PlotModelElement> getPlotFields() {
return this.plotFields;
public List<PlotModelElement> getPlotFields() {
return Collections.unmodifiableList(this.plotFields);
}
/**
@ -1244,140 +1247,6 @@ public class PlotModelFactory2 {
this.plotMissingData = b;
}
public void disposeScript() {
if (scriptInfo != null) {
try {
scriptInfo.disposeScript();
} catch (JepException e) {
statusHandler.handle(Priority.ERROR,
"Error disposing plot model script", e);
}
}
}
public void disposeSampleScript() {
if (sampleScriptInfo != null) {
try {
sampleScriptInfo.disposeScript();
} catch (JepException e) {
statusHandler.handle(Priority.ERROR,
"Error disposing plot model sample script", e);
}
}
}
private class ScriptInfo {
public String plotDelegateName;
public String scriptText;
private PlotPythonScript script;
private Thread scriptThread;
public PlotPythonScript getScript() throws JepException {
if (script != null) {
if (Thread.currentThread() == scriptThread) {
return script;
} else {
statusHandler.handle(Priority.ERROR,
"Plot model scripting was not properly disposed.");
script = null;
scriptThread = null;
}
}
script = createScript();
scriptThread = Thread.currentThread();
return script;
}
public void disposeScript() throws JepException {
if (script != null) {
try {
if (Thread.currentThread() == scriptThread) {
script.dispose();
}
} finally {
script = null;
scriptThread = null;
}
}
}
private PlotPythonScript createScript() throws JepException {
synchronized (PlotModelFactory2.class) {
if (cachedIncludePath == null) {
IPathManager pm = PathManagerFactory.getPathManager();
LocalizationFile[] files = pm
.listFiles(
pm.getLocalSearchHierarchy(LocalizationType.CAVE_STATIC),
PLOT_MODEL_DIR, null, false, false);
StringBuilder includePath = new StringBuilder();
for (LocalizationFile lf : files) {
if (lf.exists() && lf.isDirectory()) {
if (includePath.length() > 0) {
includePath.append(File.pathSeparator);
}
includePath.append(lf.getFile().getAbsolutePath());
}
}
cachedIncludePath = includePath.toString();
}
}
File baseFile = PathManagerFactory.getPathManager().getStaticFile(
PLOT_MODEL_DIR + IPathManager.SEPARATOR
+ "PlotModelInterface.py");
PlotPythonScript localScript = new PlotPythonScript(
baseFile.getAbsolutePath(), cachedIncludePath,
plotDelegateName);
if (scriptText != null) {
localScript.evaluate(scriptText);
}
localScript.executePlotDelegateMethod("init", "plotModelFactory",
PlotModelFactory2.this);
return localScript;
}
}
private static class PlotPythonScript extends PythonScript {
private static String CHEAT_RUN = "_cheat_run";
private String plotDelegateName;
public PlotPythonScript(String filePath, String anIncludePath,
String plotDelegateName) throws JepException {
super(filePath, anIncludePath, PlotPythonScript.class
.getClassLoader());
jep.eval("def "
+ CHEAT_RUN
+ "(text):\n return eval(compile(text,'string','exec'),globals(),globals())");
this.plotDelegateName = plotDelegateName;
}
public Object evaluate(String script) throws JepException {
Object result = jep.invoke(CHEAT_RUN, script);
return result;
}
public Object executePlotDelegateMethod(String methodName,
String argName, Object argValue) throws JepException {
if (plotDelegateName != null) {
HashMap<String, Object> map = null;
if (argName != null) {
map = new HashMap<String, Object>();
map.put(argName, argValue);
}
return execute(methodName, plotDelegateName, map);
} else {
return null;
}
}
}
private File getTableFile(String fileName) {
File rval = PathManagerFactory.getPathManager().getStaticFile(
PLOT_MODEL_DIR + IPathManager.SEPARATOR + fileName);
@ -1385,10 +1254,24 @@ public class PlotModelFactory2 {
}
public List<PlotModelElement> getSampleFields() {
return this.sampleFields;
return Collections.unmodifiableList(this.sampleFields);
}
public boolean isCachingImages() {
return imageCache != null;
}
public String getPlotModelFilename() {
return this.plotModelFile;
}
/**
* Disposes of the plot model
*/
public void dispose() {
if (python != null) {
python.shutdown();
}
}
}

View file

@ -0,0 +1,50 @@
/**
* 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.viz.pointdata.python;
import com.raytheon.viz.pointdata.PlotData;
/**
* Abstract class for python plot delegates where the subclasses will implement
* IPythonExecutor<PlotPythonScript, R>
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 18, 2014 2868 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public abstract class AbstractPlotDelegateExecutor {
protected PlotData plotData;
public AbstractPlotDelegateExecutor(PlotData data) {
this.plotData = data;
}
}

View file

@ -0,0 +1,62 @@
/**
* 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.viz.pointdata.python;
import java.util.HashMap;
import java.util.Map;
import jep.JepException;
import com.raytheon.uf.common.python.concurrent.IPythonExecutor;
import com.raytheon.viz.pointdata.PlotData;
/**
* Task to check if the plot data is valid based on the python method isValid()
* extracted from the SVG file.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 18, 2014 2868 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class CheckPlotValidityExecutor extends AbstractPlotDelegateExecutor
implements IPythonExecutor<PlotPythonScript, Boolean> {
public CheckPlotValidityExecutor(PlotData data) {
super(data);
}
@Override
public Boolean execute(PlotPythonScript script) throws JepException {
Map<String, Object> args = new HashMap<String, Object>();
args.put("rec", plotData);
return (Boolean) script.execute("isValid", args);
}
}

View file

@ -0,0 +1,90 @@
/**
* 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.viz.pointdata.python;
import java.util.Map;
import jep.JepException;
import com.raytheon.uf.common.python.PythonScript;
/**
* A python interpreter that is expecting to receive and retain a multi-line
* script of plot delegate python extracted from a plot model SVG file.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 14, 2014 2868 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class PlotPythonScript extends PythonScript {
protected String plotDelegateName;
/**
* Constructor
*
* @param filePath
* the path to the plot model interface python
* @param anIncludePath
* the include path for relevant python modules
* @param scriptText
* the text of the python code extracted from the plot model svg
* @param plotDelegateName
* the instance name of the plot delegate
* @param svgFilename
* the name of the SVG file the python code came from, this is
* used in exception stacktraces
*
* @throws JepException
*/
public PlotPythonScript(String filePath, String anIncludePath,
String scriptText, String plotDelegateName, String svgFilename)
throws JepException {
super(filePath, anIncludePath, PlotPythonScript.class.getClassLoader());
/*
* jep.eval() won't evaluate more than a single line of python code,
* unless that first line is a python class or method definition, so we
* work around that by compiling it, then eval-ing it
*
* TODO contemplate building the workaround into PythonInterpreter,
* PythonScript, or PythonEval
*/
jep.set("scriptToRun", scriptText);
jep.eval("eval(compile(scriptToRun, '" + svgFilename + "', 'exec'))");
this.plotDelegateName = plotDelegateName;
}
@Override
public Object execute(String methodName, Map<String, Object> args)
throws JepException {
return execute(methodName, plotDelegateName, args);
}
}

View file

@ -0,0 +1,116 @@
/**
* 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.viz.pointdata.python;
import java.io.File;
import jep.JepException;
import com.raytheon.uf.common.localization.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.PathManagerFactory;
import com.raytheon.uf.common.python.concurrent.AbstractPythonScriptFactory;
import com.raytheon.viz.pointdata.PlotModelFactory2;
/**
* Builds a plot delegate python script based on the script text extracted from
* a plot model SVG file. The name should be the name of the plot model SVG file
* to optimize reusing the interpreters across different resources that display
* the same plot model.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 14, 2014 2868 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class PlotPythonScriptFactory extends
AbstractPythonScriptFactory<PlotPythonScript> {
private static String includePath;
private static String baseFilePath;
protected String scriptText;
protected String plotDelegateName;
/**
* Constructor
*
* @param plotSvgName
* the name of the svg file that the python code was extracted
* from
* @param scriptText
* the python code that was extracted from the svg file
* @param plotDelegateName
* the name of the instance of the plot delegate inside the
* python code
*/
public PlotPythonScriptFactory(String plotSvgName, String scriptText,
String plotDelegateName) {
super(plotSvgName, 1);
this.scriptText = scriptText;
this.plotDelegateName = plotDelegateName;
}
@Override
public PlotPythonScript createPythonScript() throws JepException {
synchronized (PlotPythonScriptFactory.class) {
if (includePath == null) {
IPathManager pm = PathManagerFactory.getPathManager();
LocalizationFile[] files = pm.listFiles(pm
.getLocalSearchHierarchy(LocalizationType.CAVE_STATIC),
PlotModelFactory2.PLOT_MODEL_DIR, null, false, false);
StringBuilder includeBuilder = new StringBuilder();
for (LocalizationFile lf : files) {
if (lf.exists() && lf.isDirectory()) {
if (includeBuilder.length() > 0) {
includeBuilder.append(File.pathSeparator);
}
includeBuilder.append(lf.getFile().getAbsolutePath());
}
}
includePath = includeBuilder.toString();
}
if (baseFilePath == null) {
File baseFile = PathManagerFactory.getPathManager()
.getStaticFile(
PlotModelFactory2.PLOT_MODEL_DIR
+ IPathManager.SEPARATOR
+ "PlotModelInterface.py");
baseFilePath = baseFile.getAbsolutePath();
}
}
return new PlotPythonScript(baseFilePath, includePath, scriptText,
plotDelegateName, this.getName());
}
}

View file

@ -0,0 +1,69 @@
/**
* 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.viz.pointdata.python;
import java.util.HashMap;
import java.util.Map;
import jep.JepException;
import com.raytheon.uf.common.python.concurrent.IPythonExecutor;
import com.raytheon.viz.pointdata.PlotData;
/**
* Task to get the sample data text if the plot data is valid. Uses the python
* code of the plot delegate's isValid() and getSampleText() methods to get the
* sample text.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 18, 2014 2868 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class SampleTextExecutor extends AbstractPlotDelegateExecutor implements
IPythonExecutor<PlotPythonScript, String> {
public SampleTextExecutor(PlotData data) {
super(data);
}
@Override
public String execute(PlotPythonScript script) throws JepException {
String result = null;
Map<String, Object> args = new HashMap<String, Object>();
args.put("rec", plotData);
Boolean isValid = (Boolean) script.execute("isValid", args);
if (isValid.booleanValue()) {
result = (String) script.execute("getSampleText", args);
}
return result;
}
}

View file

@ -60,7 +60,6 @@ import com.raytheon.uf.viz.core.rsc.capabilities.MagnificationCapability;
import com.raytheon.uf.viz.core.rsc.capabilities.OutlineCapability;
import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller;
import com.raytheon.viz.pointdata.PlotAlertParser;
import com.raytheon.viz.pointdata.PlotDataThreadPool;
import com.raytheon.viz.pointdata.PlotInfo;
import com.raytheon.viz.pointdata.drawables.IPointImageExtension;
import com.raytheon.viz.pointdata.drawables.IPointImageExtension.PointImage;
@ -70,6 +69,7 @@ import com.raytheon.viz.pointdata.rsc.progdisc.DynamicProgDisclosure;
import com.raytheon.viz.pointdata.rsc.progdisc.SpiProgDisclosure;
import com.raytheon.viz.pointdata.thread.GetDataTask;
import com.raytheon.viz.pointdata.thread.GetDataTask.Params;
import com.raytheon.viz.pointdata.thread.PlotThreadOverseer;
import com.raytheon.viz.pointdata.units.PlotUnits;
import com.vividsolutions.jts.geom.Coordinate;
@ -98,6 +98,7 @@ import com.vividsolutions.jts.geom.Coordinate;
* Jun 25, 2013 1869 bsteffen Fix plot sampling.
* Sep 04, 2013 16519 kshresth Fix Metar Display Problem
* Dec 02, 2013 2473 njensen Prog Disclose paint frames at high priority
* Mar 21, 2014 2868 njensen Use PlotThreadOverseer for increased efficiency
*
* </pre>
*
@ -116,7 +117,7 @@ public class PlotResource2 extends
private AbstractProgDisclosure progressiveDisclosure;
private PlotDataThreadPool generator;
private PlotThreadOverseer generator;
private double plotWidth;
@ -265,7 +266,7 @@ public class PlotResource2 extends
Map<String, RequestConstraint> request = resourceData.getMetadataMap();
RequestConstraint plugin = request.get("pluginName");
PlotUnits.register();
generator = new PlotDataThreadPool(aTarget, descriptor,
generator = new PlotThreadOverseer(aTarget, descriptor,
this.resourceData.getPlotModelFile(),
this.resourceData.getLevelKey(), plugin.getConstraintValue(),
this.resourceData.getMetadataMap(), this);
@ -279,7 +280,7 @@ public class PlotResource2 extends
this.generator.setPlotMissingData(resourceData.isPlotMissingData());
this.generator.setLowerLimit(resourceData.getLowerLimit());
this.generator.setUpperLimit(resourceData.getUpperLimit());
this.plotWidth = generator.getPlotModelWidth();
this.plotWidth = generator.getOriginalPlotModelWidth();
this.plotWidth *= getCapability(MagnificationCapability.class)
.getMagnification();
generator.setPlotModelSize(Math.round(plotWidth));
@ -431,7 +432,7 @@ public class PlotResource2 extends
list.add(samplePlot);
Params params = Params.PLOT_AND_SAMPLE;
GetDataTask task = new GetDataTask(list, params);
generator.queueStation(task);
generator.enqueueDataRetrieval(task);
// End DR14996
}
boolean dup = false;
@ -575,7 +576,7 @@ public class PlotResource2 extends
params = Params.SAMPLE_ONLY;
}
GetDataTask task = new GetDataTask(list, params);
generator.queueStation(task);
generator.enqueueDataRetrieval(task);
}
}
@ -616,7 +617,7 @@ public class PlotResource2 extends
}
} else if (object instanceof MagnificationCapability) {
if (generator != null) {
this.plotWidth = generator.getPlotModelWidth();
this.plotWidth = generator.getOriginalPlotModelWidth();
this.plotWidth *= getCapability(
MagnificationCapability.class).getMagnification();
generator.setPlotModelSize(Math.round(plotWidth));
@ -758,7 +759,7 @@ public class PlotResource2 extends
}
if (toQueue.size() > 0) {
GetDataTask task = new GetDataTask(toQueue, Params.PLOT_ONLY);
generator.queueStation(task);
generator.enqueueDataRetrieval(task);
}
} else {
issueRefresh();

View file

@ -0,0 +1,70 @@
/**
* 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.viz.pointdata.thread;
import org.eclipse.core.runtime.jobs.Job;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller;
/**
* Abstract job associated with one of the many details of plot creation.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 21, 2014 2868 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public abstract class AbstractPlotCreationJob extends Job {
protected static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotModelDataRequestJob.class);
protected PlotThreadOverseer overseer;
protected IPlotModelGeneratorCaller listener;
public AbstractPlotCreationJob(String name, PlotThreadOverseer parent,
IPlotModelGeneratorCaller caller) {
super(name);
this.overseer = parent;
this.listener = caller;
this.setSystem(false);
}
public boolean isDone() {
return getState() != Job.RUNNING && getState() != Job.WAITING;
}
public boolean shutdown() {
return super.cancel();
}
}

View file

@ -24,7 +24,8 @@ import java.util.List;
import com.raytheon.viz.pointdata.PlotInfo;
/**
* TODO Add Description
* A task to retrieve plot data for a set of stations, possibly requesting the
* parameters necessary for the plot image or the sample text or both.
*
* <pre>
*
@ -33,6 +34,7 @@ import com.raytheon.viz.pointdata.PlotInfo;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 18, 2011 njensen Initial creation
* Mar 21, 2014 2868 njensen Improved javadoc
*
* </pre>
*

View file

@ -17,7 +17,7 @@
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.pointdata;
package com.raytheon.viz.pointdata.thread;
import java.util.ArrayList;
import java.util.HashMap;
@ -27,30 +27,25 @@ import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.graphics.RGB;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
import com.raytheon.uf.common.pointdata.PointDataContainer;
import com.raytheon.uf.common.pointdata.PointDataView;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle;
import com.raytheon.uf.viz.core.datastructure.DataCubeContainer;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.IMapDescriptor;
import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller;
import com.raytheon.viz.pointdata.PlotData;
import com.raytheon.viz.pointdata.PlotInfo;
import com.raytheon.viz.pointdata.PlotModelFactory2.PlotModelElement;
import com.raytheon.viz.pointdata.PointDataRequest;
import com.raytheon.viz.pointdata.rsc.PlotResourceData;
import com.raytheon.viz.pointdata.thread.GetDataTask;
import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob;
/**
* Job separated from PlotModelGenerator2 that requests plot data and passes it
* on to the PlotModelGeneratorJob.
* Job that requests plot data based on a constraintMap and the parameters
* specified inside the plot model SVG file.
*
* <pre>
*
@ -60,6 +55,7 @@ import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob;
* ------------ ---------- ----------- --------------------------
* Apr 22, 2011 njensen Initial creation
* May 14, 2013 1869 bsteffen Get plots working without dataURI
* Mar 21, 2014 2868 njensen Major refactor
*
* </pre>
*
@ -67,53 +63,42 @@ import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob;
* @version 1.0
*/
public class PlotModelDataRequestJob extends Job {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotModelDataRequestJob.class);
private PlotModelFactory2 plotCreator;
public class PlotModelDataRequestJob extends AbstractPlotCreationJob {
private Map<String, RequestConstraint> constraintMap;
private String plugin;
private final String plugin;
private String levelKey;
private final String levelKey;
private PlotModelGeneratorJob generatorJob;
private final List<PlotModelElement> plotFields;
private PlotSampleGeneratorJob sampleJob;
private final List<PlotModelElement> sampleFields;
private PlotDataThreadPool parent;
public PlotModelDataRequestJob(IGraphicsTarget aTarget,
IMapDescriptor mapDescriptor, String plotModelFile,
String levelKey, String plugin,
Map<String, RequestConstraint> constraintMap,
IPlotModelGeneratorCaller caller, PlotDataThreadPool parent)
public PlotModelDataRequestJob(PlotThreadOverseer parent,
IPlotModelGeneratorCaller caller,
List<PlotModelElement> plotFields,
List<PlotModelElement> sampleFields, String levelKey,
String plugin, Map<String, RequestConstraint> constraintMap)
throws VizException {
super("Requesting Plot Data...");
plotCreator = new PlotModelFactory2(mapDescriptor, plotModelFile);
super("Requesting Plot Data...", parent, caller);
this.plotFields = plotFields;
this.sampleFields = sampleFields;
this.plugin = plugin;
this.levelKey = levelKey;
this.constraintMap = constraintMap;
this.generatorJob = new PlotModelGeneratorJob(plotCreator, caller,
aTarget);
this.generatorJob.setSystem(false);
this.sampleJob = new PlotSampleGeneratorJob(plotCreator, caller);
this.sampleJob.setSystem(false);
this.parent = parent;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
while (parent.stationQueue.size() > 0) {
while (overseer.dataRetrievalQueue.size() > 0) {
List<PlotInfo[]> stationQuery = new ArrayList<PlotInfo[]>();
GetDataTask task = null;
synchronized (this) {
task = parent.stationQueue.poll();
task = overseer.dataRetrievalQueue.poll();
if (task == null) {
// possibility another thread got it first
continue;
}
List<PlotInfo[]> batch = task.getStations();
@ -125,15 +110,15 @@ public class PlotModelDataRequestJob extends Job {
List<PlotModelElement> pme = null;
switch (task.getRequestType()) {
case PLOT_ONLY:
pme = this.plotCreator.getPlotFields();
pme = plotFields;
break;
case SAMPLE_ONLY:
pme = this.plotCreator.getSampleFields();
pme = sampleFields;
break;
case PLOT_AND_SAMPLE:
pme = new ArrayList<PlotModelElement>();
pme.addAll(this.plotCreator.getPlotFields());
pme.addAll(this.plotCreator.getSampleFields());
pme.addAll(plotFields);
pme.addAll(sampleFields);
default:
break;
}
@ -161,14 +146,14 @@ public class PlotModelDataRequestJob extends Job {
if (infos[0].pdv != null) {
switch (task.getRequestType()) {
case PLOT_ONLY:
this.generatorJob.enqueue(infos);
overseer.enqueueImageGeneration(infos);
break;
case SAMPLE_ONLY:
this.sampleJob.enqueue(infos);
overseer.enqueueSamplePlot(infos);
break;
case PLOT_AND_SAMPLE:
this.generatorJob.enqueue(infos);
this.sampleJob.enqueue(infos);
overseer.enqueueImageGeneration(infos);
overseer.enqueueSamplePlot(infos);
break;
}
}
@ -186,12 +171,13 @@ public class PlotModelDataRequestJob extends Job {
List<String> params = new ArrayList<String>();
for (PlotModelElement p : pme) {
if (!p.parameter.equals("") && !p.parameter.contains(",")) {
params.add(p.parameter);
} else if (p.parameter.contains(",")) {
String[] individualParams = p.parameter.split(",");
for (String param : individualParams) {
params.add(param);
String param = p.getParameter();
if (!param.equals("") && !param.contains(",")) {
params.add(param);
} else if (param.contains(",")) {
String[] individualParams = param.split(",");
for (String paramToRequest : individualParams) {
params.add(paramToRequest);
}
}
}
@ -200,7 +186,7 @@ public class PlotModelDataRequestJob extends Job {
.getPluginProperties(plugin).hasDistinctStationId;
String uniquePointDataKey = "stationId";
String uniqueQueryKey = "location.stationId";
if(!hasDistinctStationId){
if (!hasDistinctStationId) {
uniquePointDataKey = "dataURI";
uniqueQueryKey = uniquePointDataKey;
@ -208,7 +194,7 @@ public class PlotModelDataRequestJob extends Job {
if (!params.contains(uniquePointDataKey)) {
params.add(uniquePointDataKey);
}
Map<String, RequestConstraint> map = new HashMap<String, RequestConstraint>();
map.putAll(this.constraintMap);
RequestConstraint rc = new RequestConstraint();
@ -221,7 +207,7 @@ public class PlotModelDataRequestJob extends Job {
String key = null;
if (hasDistinctStationId) {
key = info.stationId;
}else{
} else {
key = info.dataURI;
}
str.add(key);
@ -347,50 +333,4 @@ public class PlotModelDataRequestJob extends Job {
}
}
public void setUpperLimit(double upperLimit) {
this.plotCreator.setUpperLimit(upperLimit);
}
public void setLowerLimit(double lowerLimit) {
this.plotCreator.setLowerLimit(lowerLimit);
}
public int getPlotModelWidth() {
return this.plotCreator.getDefinedPlotModelWidth();
}
public void setPlotModelSize(long width) {
this.plotCreator.setPlotDimensions(width, width);
this.generatorJob.clearImageCache();
}
public void setPlotModelColor(RGB color) {
this.plotCreator.setColor(color);
// this.generatorJob.cleanImages();
}
public void setPlotModelLineWidth(int width) {
this.plotCreator.setLineWidth(width);
this.generatorJob.clearImageCache();
}
public void setPlotModelLineStyle(LineStyle style) {
this.plotCreator.setLineStyle(style);
this.generatorJob.clearImageCache();
}
public void setPlotMissingData(boolean b) {
this.plotCreator.setPlotMissingData(b);
}
public boolean isDone() {
return getState() != Job.RUNNING && getState() != Job.WAITING
&& generatorJob.isDone();
}
public synchronized void shutdown() {
this.cancel();
this.generatorJob.shutdown();
}
}

View file

@ -17,29 +17,28 @@
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.viz.pointdata;
package com.raytheon.viz.pointdata.thread;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.collections.map.LRUMap;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.data.IRenderedImageCallback;
import com.raytheon.uf.viz.core.drawables.IImage;
import com.raytheon.uf.viz.core.drawables.ext.ISingleColorImageExtension;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller;
import com.raytheon.viz.pointdata.PlotInfo;
import com.raytheon.viz.pointdata.PlotModelFactory2;
/**
* Job separated from PlotModelGenerator2 that creates the plot images.
* Job that generates plot images using a PlotModelFactory2.
*
* <pre>
*
@ -48,6 +47,7 @@ import com.raytheon.uf.viz.core.exception.VizException;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Apr 22, 2011 njensen Initial creation
* Mar 21, 2014 2868 njensen Major refactor
*
* </pre>
*
@ -55,27 +55,20 @@ import com.raytheon.uf.viz.core.exception.VizException;
* @version 1.0
*/
public class PlotModelGeneratorJob extends Job {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotModelDataRequestJob.class);
private ConcurrentLinkedQueue<PlotInfo[]> taskQueue = new ConcurrentLinkedQueue<PlotInfo[]>();
public class PlotModelGeneratorJob extends AbstractPlotCreationJob {
private PlotModelFactory2 plotCreator;
private IPlotModelGeneratorCaller caller;
private IGraphicsTarget target;
@SuppressWarnings("unchecked")
private Map<BufferedImage, IImage> imageCache = new LRUMap(1000);
protected PlotModelGeneratorJob(PlotModelFactory2 plotCreator,
IPlotModelGeneratorCaller caller, IGraphicsTarget target) {
super("Creating plots");
protected PlotModelGeneratorJob(PlotThreadOverseer parent,
IPlotModelGeneratorCaller caller, PlotModelFactory2 plotCreator,
IGraphicsTarget target) {
super("Creating plots", parent, caller);
this.plotCreator = plotCreator;
this.caller = caller;
this.target = target;
}
@ -88,9 +81,14 @@ public class PlotModelGeneratorJob extends Job {
@Override
protected IStatus run(IProgressMonitor monitor) {
long t0 = System.currentTimeMillis();
while (!taskQueue.isEmpty()) {
long count = 0;
while (!overseer.imageCreationQueue.isEmpty()) {
try {
PlotInfo[] infos = taskQueue.poll();
PlotInfo[] infos = overseer.imageCreationQueue.poll();
if (infos == null) {
// possibility another thread got it first
continue;
}
final BufferedImage bImage = plotCreator.getStationPlot(
infos[0].pdv, infos[0].latitude, infos[0].longitude);
IImage image = null;
@ -120,46 +118,40 @@ public class PlotModelGeneratorJob extends Job {
}
synchronized (this) {
if (monitor.isCanceled()) {
if(image != null){
if (image != null) {
image.dispose();
}
break;
}
caller.modelGenerated(infos, image);
count++;
listener.modelGenerated(infos, image);
}
} catch (Exception e) {
statusHandler.error("Error creating plot", e);
statusHandler.error("Error creating plot with plotModel "
+ plotCreator.getPlotModelFilename(), e);
}
}
plotCreator.disposeScript();
System.out.println("Time spent creating plots: "
+ (System.currentTimeMillis() - t0));
return Status.OK_STATUS;
}
protected void enqueue(PlotInfo[] task) {
this.taskQueue.add(task);
if (this.getState() != Job.RUNNING) {
this.schedule();
if (count > 0) {
/*
* if count is zero it means by the time this job was scheduled and
* run, a different job took care of everything on the queue
*/
System.out.println("Time spent creating " + count + " plots: "
+ (System.currentTimeMillis() - t0));
}
}
protected int getQueueSize() {
return taskQueue.size();
return Status.OK_STATUS;
}
protected void clearImageCache() {
imageCache.clear();
}
public boolean isDone() {
return getState() != Job.RUNNING && getState() != Job.WAITING;
}
protected synchronized void shutdown() {
cancel();
taskQueue.clear();
@Override
public boolean shutdown() {
boolean result = super.shutdown();
clearImageCache();
plotCreator.dispose();
return result;
}
}

View file

@ -19,22 +19,17 @@
**/
package com.raytheon.viz.pointdata.thread;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller;
import com.raytheon.viz.pointdata.PlotInfo;
import com.raytheon.viz.pointdata.PlotModelFactory2;
/**
* Job that uses the provided factory to generate a sampling message for a
* particular plot.
* Job that uses the provided plot model factory to generate a sampling message
* for a particular plot.
*
* <pre>
*
@ -44,6 +39,7 @@ import com.raytheon.viz.pointdata.PlotModelFactory2;
* ------------ ---------- ----------- --------------------------
* Jul 13, 2011 njensen Initial creation
* Jun 25, 2013 1869 bsteffen Fix plot sampling.
* Mar 21, 2014 2868 njensen Major refactor
*
* </pre>
*
@ -51,22 +47,14 @@ import com.raytheon.viz.pointdata.PlotModelFactory2;
* @version 1.0
*/
public class PlotSampleGeneratorJob extends Job {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotSampleGeneratorJob.class);
public class PlotSampleGeneratorJob extends AbstractPlotCreationJob {
private PlotModelFactory2 plotFactory;
private ConcurrentLinkedQueue<PlotInfo[]> queue = new ConcurrentLinkedQueue<PlotInfo[]>();
private IPlotModelGeneratorCaller caller;
public PlotSampleGeneratorJob(PlotModelFactory2 factory,
IPlotModelGeneratorCaller caller) {
super("Generating samples");
public PlotSampleGeneratorJob(PlotThreadOverseer parent,
IPlotModelGeneratorCaller caller, PlotModelFactory2 factory) {
super("Generating samples", parent, caller);
this.plotFactory = factory;
this.caller = caller;
}
/*
@ -77,27 +65,29 @@ public class PlotSampleGeneratorJob extends Job {
*/
@Override
protected IStatus run(IProgressMonitor monitor) {
while (!queue.isEmpty()) {
while (!overseer.sampleTextQueue.isEmpty()) {
try {
PlotInfo[] infos = queue.poll();
PlotInfo[] infos = overseer.sampleTextQueue.poll();
if (infos == null) {
// possibility another thread got it first
continue;
}
String message = plotFactory.getStationMessage(infos[0].pdv,
infos[0].dataURI);
caller.messageGenerated(infos, message);
listener.messageGenerated(infos, message);
} catch (Exception e) {
statusHandler.error("Error creating plot", e);
statusHandler.error("Error creating sample with plotModel "
+ plotFactory.getPlotModelFilename(), e);
}
}
plotFactory.disposeSampleScript();
return Status.OK_STATUS;
}
public void enqueue(PlotInfo[] task) {
this.queue.add(task);
if (this.getState() != Job.RUNNING) {
this.schedule();
}
@Override
public boolean shutdown() {
boolean result = super.shutdown();
plotFactory.dispose();
return result;
}
}

View file

@ -0,0 +1,365 @@
/**
* 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.viz.pointdata.thread;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.swt.graphics.RGB;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint;
import com.raytheon.uf.viz.core.IGraphicsTarget;
import com.raytheon.uf.viz.core.IGraphicsTarget.LineStyle;
import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.MapDescriptor;
import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller;
import com.raytheon.viz.pointdata.PlotInfo;
import com.raytheon.viz.pointdata.PlotModelFactory2;
import com.raytheon.viz.pointdata.PlotModelFactory2.PlotModelElement;
/**
* Oversees a variety of threads used to concurrently and quickly get plots on
* screen. There should be only one PlotThreadOverseer for each PlotResource2.
*
* This class oversees the pipeline of threads necessary for requesting plot
* data, generating plot images, and generating plot samples. Note that it does
* not involve itself with progressive disclosure, paint, or python threads.
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Mar 21, 2014 2868 njensen Initial creation
*
* </pre>
*
* @author njensen
* @version 1.0
*/
public class PlotThreadOverseer {
private static final int DATA_THREADS = 2;
private static final int IMAGE_THREADS = DATA_THREADS;
private static final int SAMPLE_THREADS = 1;
protected ConcurrentLinkedQueue<GetDataTask> dataRetrievalQueue = new ConcurrentLinkedQueue<GetDataTask>();
protected List<PlotModelDataRequestJob> dataRetrievalJobList = new ArrayList<PlotModelDataRequestJob>(
DATA_THREADS);
protected ConcurrentLinkedQueue<PlotInfo[]> imageCreationQueue = new ConcurrentLinkedQueue<PlotInfo[]>();
protected List<PlotModelGeneratorJob> imageCreationJobList = new ArrayList<PlotModelGeneratorJob>(
IMAGE_THREADS);
protected List<PlotModelFactory2> plotCreatorList = new ArrayList<PlotModelFactory2>(
IMAGE_THREADS);
protected ConcurrentLinkedQueue<PlotInfo[]> sampleTextQueue = new ConcurrentLinkedQueue<PlotInfo[]>();
protected List<PlotSampleGeneratorJob> sampleTextJobList = new ArrayList<PlotSampleGeneratorJob>(
SAMPLE_THREADS);
private int plotModelWidth;
/**
* Constructor
*
* @param target
* @param mapDescriptor
* @param plotModelFile
* @param levelKey
* @param plugin
* @param constraintMap
* @param caller
* @throws VizException
*/
public PlotThreadOverseer(IGraphicsTarget target,
MapDescriptor mapDescriptor, String plotModelFile, String levelKey,
String plugin, HashMap<String, RequestConstraint> constraintMap,
IPlotModelGeneratorCaller caller) throws VizException {
/*
* Set up image generation jobs first because the PlotModelFactory2
* initialization will parse the SVG to determine the parameters to
* request
*/
List<PlotModelElement> plotFields = null;
List<PlotModelElement> sampleFields = null;
for (int i = 0; i < IMAGE_THREADS; i++) {
PlotModelFactory2 plotModelFactory = new PlotModelFactory2(
mapDescriptor, plotModelFile);
plotCreatorList.add(plotModelFactory);
imageCreationJobList.add(new PlotModelGeneratorJob(this, caller,
plotModelFactory, target));
if (plotFields == null) {
/*
* data retrieval jobs need to know the plot and sample fields
* to request, which have now been parsed out of the SVG
*/
plotFields = plotModelFactory.getPlotFields();
sampleFields = plotModelFactory.getSampleFields();
plotModelWidth = plotModelFactory.getDefinedPlotModelWidth();
}
}
/*
* Set up the data retrieval jobs
*/
for (int i = 0; i < DATA_THREADS; i++) {
dataRetrievalJobList.add(new PlotModelDataRequestJob(this, caller,
plotFields, sampleFields, levelKey, plugin, constraintMap));
}
/*
* Set up the sample jobs. We could probably reuse the plot model
* factories of the image jobs, regardless, if it's a sample that uses
* python it will reuse the python job coordinator underneath, and if
* it's not...it's not clear if
* PlotModelFactory2.processSampleDirective() is thread safe
*
* TODO do we care?
*/
for (int i = 0; i < SAMPLE_THREADS; i++) {
PlotModelFactory2 plotModelFactory = new PlotModelFactory2(
mapDescriptor, plotModelFile);
sampleTextJobList.add(new PlotSampleGeneratorJob(this, caller,
plotModelFactory));
}
}
/**
* Enqueues a task of a batch of stations to retrieve data for
*
* @param task
*
*/
public void enqueueDataRetrieval(GetDataTask task) {
List<PlotInfo[]> station = task.getStations();
Iterator<PlotInfo[]> itr = station.iterator();
while (itr.hasNext()) {
PlotInfo[] infos = itr.next();
boolean allQueued = true;
for (PlotInfo info : infos) {
switch (task.getRequestType()) {
case PLOT_ONLY:
if (!info.plotQueued) {
allQueued = false;
info.plotQueued = true;
}
break;
case SAMPLE_ONLY:
if (!info.sampleQueued) {
allQueued = false;
info.sampleQueued = true;
}
break;
case PLOT_AND_SAMPLE:
if (!info.sampleQueued) {
allQueued = false;
info.sampleQueued = true;
}
if (!info.plotQueued) {
allQueued = false;
info.plotQueued = true;
}
}
}
if (allQueued) {
itr.remove();
}
}
if (station.size() > 0) {
task.setStations(station);
dataRetrievalQueue.add(task);
for (PlotModelDataRequestJob job : dataRetrievalJobList) {
if (job.getState() != Job.RUNNING) {
job.schedule();
}
}
}
}
/**
* Adds a batch of PlotInfos to generate images for
*
* @param plotInfo
*/
public void enqueueImageGeneration(PlotInfo[] plotInfo) {
if (plotInfo.length > 0) {
imageCreationQueue.add(plotInfo);
for (PlotModelGeneratorJob job : imageCreationJobList) {
if (job.getState() != Job.RUNNING) {
job.schedule();
}
}
}
}
/**
* Adds a batch of PlotInfos to generate sample messages for
*
* @param plotInfo
*/
public void enqueueSamplePlot(PlotInfo[] plotInfo) {
if (plotInfo.length > 0) {
sampleTextQueue.add(plotInfo);
for (PlotSampleGeneratorJob job : sampleTextJobList) {
if (job.getState() != Job.RUNNING) {
job.schedule();
}
}
}
}
public void setPlotModelColor(RGB color) {
for (PlotModelFactory2 pmf : plotCreatorList) {
pmf.setColor(color);
}
// don't clear the image cache, it's magic
}
public void setPlotModelLineStyle(LineStyle lineStyle) {
for (PlotModelFactory2 pmf : plotCreatorList) {
pmf.setLineStyle(lineStyle);
}
for (PlotModelGeneratorJob job : imageCreationJobList) {
job.clearImageCache();
}
}
public void setPlotModelLineWidth(int outlineWidth) {
for (PlotModelFactory2 pmf : plotCreatorList) {
pmf.setLineWidth(outlineWidth);
}
for (PlotModelGeneratorJob job : imageCreationJobList) {
job.clearImageCache();
}
}
public void setPlotMissingData(boolean plotMissingData) {
for (PlotModelFactory2 pmf : plotCreatorList) {
pmf.setPlotMissingData(plotMissingData);
}
}
public void setLowerLimit(double lowerLimit) {
for (PlotModelFactory2 pmf : plotCreatorList) {
pmf.setLowerLimit(lowerLimit);
}
}
public void setUpperLimit(double upperLimit) {
for (PlotModelFactory2 pmf : plotCreatorList) {
pmf.setUpperLimit(upperLimit);
}
}
public double getOriginalPlotModelWidth() {
return plotModelWidth;
}
public void setPlotModelSize(long round) {
for (PlotModelFactory2 pmf : plotCreatorList) {
pmf.setPlotDimensions(round, round);
}
for (PlotModelGeneratorJob job : imageCreationJobList) {
job.clearImageCache();
}
}
/**
* Checks if all the jobs related to requesting data, creating plot images,
* and creating samples are done
*
* @return
*/
public boolean isDone() {
for (AbstractPlotCreationJob job : dataRetrievalJobList) {
if (!job.isDone()) {
return false;
}
}
for (AbstractPlotCreationJob job : imageCreationJobList) {
if (!job.isDone()) {
return false;
}
}
for (AbstractPlotCreationJob job : sampleTextJobList) {
if (!job.isDone()) {
return false;
}
}
return true;
}
/**
* Shuts down all the threads and clears their queues in a safe manner
*/
public void shutdown() {
/*
* by clearing the job lists first, nothing else can get queued, but we
* still need a copy of them to ensure they are shut down correctly
*/
List<AbstractPlotCreationJob> jobListCopy = null;
// shut down data requests first
jobListCopy = new ArrayList<AbstractPlotCreationJob>(
dataRetrievalJobList);
dataRetrievalJobList.clear();
for (AbstractPlotCreationJob job : jobListCopy) {
job.shutdown();
}
dataRetrievalQueue.clear();
// then shut down images
jobListCopy = new ArrayList<AbstractPlotCreationJob>(
imageCreationJobList);
imageCreationJobList.clear();
for (AbstractPlotCreationJob job : jobListCopy) {
job.shutdown();
}
imageCreationQueue.clear();
// and finally sampling
jobListCopy = new ArrayList<AbstractPlotCreationJob>(sampleTextJobList);
sampleTextJobList.clear();
for (AbstractPlotCreationJob job : jobListCopy) {
job.shutdown();
}
sampleTextQueue.clear();
}
}

View file

@ -25,6 +25,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import jep.JepException;
@ -59,6 +60,9 @@ import com.raytheon.uf.common.python.PythonInterpreter;
* Jan 31, 2013 mnash Initial creation
* Jun 04, 2013 2041 bsteffen Improve exception handling for concurrent
* python.
* Mar 21, 2014 2868 njensen Changed getInstance() from throwing
* RuntimeException to IllegalArgumentException
* Added refCount
*
* </pre>
*
@ -71,10 +75,17 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
private ThreadLocal<P> threadLocal = null;
/**
* Tracks the number of times newInstance() vs shutdown() is called for an
* instance of a PythonJobCoordinator
*/
private AtomicInteger refCount;
private static Map<String, PythonJobCoordinator<? extends PythonInterpreter>> pools = new ConcurrentHashMap<String, PythonJobCoordinator<? extends PythonInterpreter>>();
private PythonJobCoordinator(final AbstractPythonScriptFactory<P> factory) {
threadLocal = new ThreadLocal<P>() {
@Override
protected P initialValue() {
try {
return factory.createPythonScript();
@ -85,10 +96,11 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
};
execService = Executors.newFixedThreadPool(factory.getMaxThreads(),
new PythonThreadFactory(threadLocal, factory.getName()));
refCount = new AtomicInteger();
}
/**
* Gets the instance by name, or throw a {@link RuntimeException}.
* Gets the instance by name, or throw a {@link IllegalArgumentException}.
*
* @param name
* @return
@ -99,7 +111,7 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
if (pools.containsKey(name)) {
return (PythonJobCoordinator<S>) pools.get(name);
} else {
throw new RuntimeException(
throw new IllegalArgumentException(
"Unable to find instance of PythonJobCoordinator named "
+ name
+ ", please call newInstance(AbstractPythonScriptFactory)");
@ -110,7 +122,10 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
/**
* Creates a new instance of this class for a new application. If the same
* name already exists, it assumes that it is the same application and
* returns the existing instance.
* returns the existing instance. Also increments the reference count of
* applications using this PythonJobCoordinator. For each time that
* newInstance() is called, a corresponding call to shutdown() will be
* needed if you truly want to shut the job coordinator down.
*
* @param name
* @param numThreads
@ -119,14 +134,15 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
public static <S extends PythonInterpreter> PythonJobCoordinator<S> newInstance(
AbstractPythonScriptFactory<S> factory) {
synchronized (pools) {
PythonJobCoordinator<S> pool = null;
if (pools.containsKey(factory.getName())) {
return (PythonJobCoordinator<S>) pools.get(factory.getName());
pool = (PythonJobCoordinator<S>) pools.get(factory.getName());
} else {
PythonJobCoordinator<S> pool = new PythonJobCoordinator<S>(
factory);
pool = new PythonJobCoordinator<S>(factory);
pools.put(factory.getName(), pool);
return pool;
}
pool.refCount.getAndIncrement();
return pool;
}
}
@ -171,15 +187,20 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
/**
* This function should take the {@link PythonInterpreter} on each thread in
* the thread pool and dispose of it and then shutdown the
* {@link ExecutorService}
* {@link ExecutorService}. This will reduce the reference count by 1, and
* will only shut down the underlying executor service and python
* interpreters if the refCount is less than 1.
*
* @param name
*/
public void shutdown() {
synchronized (pools) {
pools.values().remove(this);
int count = refCount.decrementAndGet();
if (count < 1) {
pools.values().remove(this);
execService.shutdown();
}
}
execService.shutdown();
}
/**
@ -191,7 +212,9 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
*/
public void shutdownTask(String name) {
/*
* TODO need to add for future functionality
* TODO need to add for future functionality, arg should probably be an
* IPythonExecutor, not a String
*/
}
}