Issue #2868 plot delegate python now uses PythonCoordinatorJob API,

Refactored plot data request, image creation, and sample
              threading

Change-Id: I3e1a3dc70b2346aa826ec4509ee7a3e59c61a918

Former-commit-id: 2418c9bdfe [formerly fe33896e7e] [formerly 2418c9bdfe [formerly fe33896e7e] [formerly c4beb293b9 [formerly e421731415d798487c96dd1e56b6f02347d9aa26]]]
Former-commit-id: c4beb293b9
Former-commit-id: 0acb4621f9 [formerly 774091461c]
Former-commit-id: 96339798b5
This commit is contained in:
Nate Jensen 2014-03-21 19:33:48 -05:00
parent 54e1c39279
commit 5b6683bd56
16 changed files with 1055 additions and 597 deletions

View file

@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2 Bundle-ManifestVersion: 2
Bundle-Name: Pointdata Plug-in Bundle-Name: Pointdata Plug-in
Bundle-SymbolicName: com.raytheon.viz.pointdata;singleton:=true 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-Activator: com.raytheon.viz.pointdata.Activator
Bundle-Vendor: Raytheon Bundle-Vendor: Raytheon
Eclipse-RegisterBuddy: com.raytheon.viz.core, com.raytheon.uf.viz.core 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.pointdata;bundle-version="1.12.1174",
com.raytheon.uf.common.dataplugin.level;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.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 Bundle-ActivationPolicy: lazy
Export-Package: com.raytheon.viz.pointdata, Export-Package: com.raytheon.viz.pointdata,
com.raytheon.viz.pointdata.drawables, 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.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Formatter; import java.util.Formatter;
import java.util.HashMap; import java.util.HashMap;
@ -40,8 +41,6 @@ import javax.measure.converter.UnitConverter;
import javax.measure.unit.Unit; import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat; import javax.measure.unit.UnitFormat;
import jep.JepException;
import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.GVTBuilder;
import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.bridge.UserAgentAdapter;
@ -57,10 +56,8 @@ import org.w3c.dom.NodeList;
import org.w3c.dom.Text; import org.w3c.dom.Text;
import com.raytheon.uf.common.localization.IPathManager; 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.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.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;
@ -69,12 +66,16 @@ import com.raytheon.uf.viz.core.exception.VizException;
import com.raytheon.uf.viz.core.map.IMapDescriptor; import com.raytheon.uf.viz.core.map.IMapDescriptor;
import com.raytheon.viz.pointdata.lookup.IAbstractLookupTable; import com.raytheon.viz.pointdata.lookup.IAbstractLookupTable;
import com.raytheon.viz.pointdata.lookup.LookupUtils; 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.PlotResource2;
import com.raytheon.viz.pointdata.rsc.PlotResourceData; import com.raytheon.viz.pointdata.rsc.PlotResourceData;
/** /**
* A singleton that will create a plot model texture based on a passed in * A factory for generating plot images and sample messages by parsing the
* MetarRecord object. * associated plotModel SVG file.
* *
* <pre> * <pre>
* *
@ -89,6 +90,7 @@ import com.raytheon.viz.pointdata.rsc.PlotResourceData;
* Sep 05, 2013 2316 bsteffen Unify pirep and ncpirep. * Sep 05, 2013 2316 bsteffen Unify pirep and ncpirep.
* Sep 05, 2013 2307 dgilling Use better PythonScript constructor. * Sep 05, 2013 2307 dgilling Use better PythonScript constructor.
* Nov 20, 2013 2033 njensen Fix detecting plotModels dirs from multiple plugins * Nov 20, 2013 2033 njensen Fix detecting plotModels dirs from multiple plugins
* Mar 21, 2014 2868 njensen Refactored python usage to PythonJobCoordinator
* *
* </pre> * </pre>
* *
@ -99,7 +101,7 @@ public class PlotModelFactory2 {
private static final transient IUFStatusHandler statusHandler = UFStatus private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotModelFactory2.class); .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"; private static final String DM_ATTRIBUTE = "plotMode";
@ -121,8 +123,6 @@ public class PlotModelFactory2 {
private static final String REQUIRED = "required"; private static final String REQUIRED = "required";
private static String cachedIncludePath;
private final SimpleDateFormat SAMPLE_DATE = new SimpleDateFormat("HHmm"); private final SimpleDateFormat SAMPLE_DATE = new SimpleDateFormat("HHmm");
// Need to include attribute and code to allow for String2String lookups and // Need to include attribute and code to allow for String2String lookups and
@ -137,8 +137,6 @@ public class PlotModelFactory2 {
private Document document; private Document document;
private GraphicsNode theGraphicsNode;
private final GVTBuilder builder; private final GVTBuilder builder;
private final BridgeContext bridgeContext; private final BridgeContext bridgeContext;
@ -171,9 +169,9 @@ public class PlotModelFactory2 {
private Map<String, BufferedImage> imageCache = null; private Map<String, BufferedImage> imageCache = null;
private ScriptInfo scriptInfo; protected final String plotModelFile;
private ScriptInfo sampleScriptInfo; protected PythonJobCoordinator<PlotPythonScript> python;
public static enum DisplayMode { public static enum DisplayMode {
TEXT, BARB, TABLE, AVAIL, RANGE, NULL, SAMPLE, ARROW TEXT, BARB, TABLE, AVAIL, RANGE, NULL, SAMPLE, ARROW
@ -211,6 +209,10 @@ public class PlotModelFactory2 {
public Node getPlotNode() { public Node getPlotNode() {
return plotNode; return plotNode;
} }
public String getParameter() {
return parameter;
}
} }
public class PlotWindElement { public class PlotWindElement {
@ -238,6 +240,7 @@ public class PlotModelFactory2 {
byte[] blue = { 0, full }; byte[] blue = { 0, full };
byte[] green = { 0, zero }; byte[] green = { 0, zero };
regenerateStyle(); regenerateStyle();
this.plotModelFile = plotModelFile;
this.plotFields = new ArrayList<PlotModelElement>(); this.plotFields = new ArrayList<PlotModelElement>();
this.sampleFields = new ArrayList<PlotModelElement>(); this.sampleFields = new ArrayList<PlotModelElement>();
@ -452,12 +455,16 @@ public class PlotModelFactory2 {
imageCache = new HashMap<String, BufferedImage>(); imageCache = new HashMap<String, BufferedImage>();
} }
NodeList scriptNodes = document.getElementsByTagName("script"); NodeList scriptNodes = document.getElementsByTagName("script");
// Only one script node supported // 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); Element scriptNode = (Element) scriptNodes.item(0);
scriptInfo = new ScriptInfo(); String plotDelegateName = scriptNode.getAttribute("plotDelegate");
scriptInfo.plotDelegateName = scriptNode
.getAttribute("plotDelegate");
NodeList childNodes = scriptNode.getChildNodes(); NodeList childNodes = scriptNode.getChildNodes();
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
for (int i = 0; i < childNodes.getLength(); i++) { for (int i = 0; i < childNodes.getLength(); i++) {
@ -466,14 +473,13 @@ public class PlotModelFactory2 {
sb.append(((Text) child).getData()); sb.append(((Text) child).getData());
} }
} }
if (sb.length() > 0) { String scriptText = sb.toString().trim();
scriptInfo.scriptText = sb.toString(); 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 // remove the scriptNode in memory so time isn't wasted
// later attempting to render it // later attempting to render it
scriptNode.getParentNode().removeChild(scriptNode); scriptNode.getParentNode().removeChild(scriptNode);
@ -491,8 +497,8 @@ public class PlotModelFactory2 {
byte fullr = (byte) color.red; byte fullr = (byte) color.red;
byte fullg = (byte) color.green; byte fullg = (byte) color.green;
byte fullb = (byte) color.blue; byte fullb = (byte) color.blue;
String style = "stroke: rgb(" + color.red + "," + color.green + "," // String style = "stroke: rgb(" + color.red + "," + color.green + ","
+ color.blue + ");"; // + color.blue + ");";
// this.svgRoot.setAttribute("style", style); // this.svgRoot.setAttribute("style", style);
// System.out.println(style); // System.out.println(style);
byte[] red = { 0, fullr }; byte[] red = { 0, fullr };
@ -567,8 +573,7 @@ public class PlotModelFactory2 {
} }
/** /**
* Takes the station name and its MetarRecord object and produces a buffered * Takes the station data object and produces a buffered image.
* image.
* *
* @param station * @param station
* The station name * The station name
@ -591,27 +596,28 @@ public class PlotModelFactory2 {
this.gc.setDestinationGeographicPoint(newWorldLocation[0], this.gc.setDestinationGeographicPoint(newWorldLocation[0],
newWorldLocation[1]); newWorldLocation[1]);
} }
StringBuffer imageId = new StringBuffer();
PlotPythonScript script = null;
try { try {
boolean discard = false; boolean discard = false;
if (python != null) {
if (scriptInfo != null) { Boolean result = false;
script = scriptInfo.getScript(); CheckPlotValidityExecutor task = new CheckPlotValidityExecutor(
stationData);
try { try {
Object result = script.executePlotDelegateMethod("isValid", result = python.submitSyncJob(task);
"rec", stationData); } catch (Exception e) {
if (result instanceof Boolean statusHandler.handle(Priority.PROBLEM,
&& !((Boolean) result).booleanValue()) { "Error checking if plot is valid for plot model "
+ getPlotModelFilename(), e);
} finally {
if (result.booleanValue() == false) {
return null; return null;
} }
} catch (JepException e) {
statusHandler.handle(Priority.PROBLEM,
e.getLocalizedMessage(), e);
} }
} }
StringBuilder imageId = new StringBuilder();
for (PlotModelElement element : this.plotFields) { for (PlotModelElement element : this.plotFields) {
boolean valid = true; boolean valid = true;
boolean required = element.required; boolean required = element.required;
@ -680,13 +686,13 @@ public class PlotModelFactory2 {
this.plotModelWidth, this.plotModelHeight, this.plotModelWidth, this.plotModelHeight,
BufferedImage.TYPE_BYTE_INDEXED, tm); BufferedImage.TYPE_BYTE_INDEXED, tm);
long t0 = System.currentTimeMillis(); // long t0 = System.currentTimeMillis();
this.theGraphicsNode = builder.build(this.bridgeContext, GraphicsNode graphicsNode = builder.build(this.bridgeContext,
this.document); this.document);
Graphics2D g2d = null; Graphics2D g2d = null;
try { try {
g2d = bufferedImage.createGraphics(); g2d = bufferedImage.createGraphics();
this.theGraphicsNode.primitivePaint(g2d); graphicsNode.primitivePaint(g2d);
} finally { } finally {
if (g2d != null) { if (g2d != null) {
g2d.dispose(); g2d.dispose();
@ -702,9 +708,6 @@ public class PlotModelFactory2 {
} catch (Exception e) { } catch (Exception e) {
statusHandler.handle(Priority.PROBLEM, statusHandler.handle(Priority.PROBLEM,
"Error:" + e.getLocalizedMessage(), e); "Error:" + e.getLocalizedMessage(), e);
} catch (JepException e) {
statusHandler.handle(Priority.PROBLEM,
"Error:" + e.getLocalizedMessage(), e);
} }
return null; return null;
@ -712,20 +715,20 @@ public class PlotModelFactory2 {
public synchronized String getStationMessage(PlotData stationData, public synchronized String getStationMessage(PlotData stationData,
String dataURI) { String dataURI) {
PlotPythonScript script = null;
StringBuilder sampleMessage = new StringBuilder(); StringBuilder sampleMessage = new StringBuilder();
try { try {
if (sampleScriptInfo != null) { if (python != null) {
script = sampleScriptInfo.getScript(); String result = null;
SampleTextExecutor task = new SampleTextExecutor(stationData);
Object result = script.executePlotDelegateMethod("isValid", try {
"rec", stationData); result = python.submitSyncJob(task);
if (result instanceof Boolean } catch (Exception e) {
&& ((Boolean) result).booleanValue()) { statusHandler.handle(Priority.PROBLEM,
result = script.executePlotDelegateMethod("getSampleText", "Error getting sample text for plot model "
"rec", stationData); + getPlotModelFilename(), e);
if (result instanceof String) { } finally {
sampleMessage.append((String) result); if (result != null) {
sampleMessage.append(result);
} }
} }
} else { } else {
@ -736,11 +739,8 @@ public class PlotModelFactory2 {
} }
} catch (Exception e) { } catch (Exception e) {
// TODO statusHandler.handle(Priority.PROBLEM,
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e); "Error generating sample text with " + plotModelFile, e);
} catch (JepException e) {
// TODO Auto-generated catch block. Please revise as appropriate.
statusHandler.handle(Priority.PROBLEM, e.getLocalizedMessage(), e);
} }
String message = sampleMessage.toString(); String message = sampleMessage.toString();
@ -752,7 +752,7 @@ public class PlotModelFactory2 {
} }
private void addToImageId(PlotData stationData, String parameters, private void addToImageId(PlotData stationData, String parameters,
StringBuffer imageId) { StringBuilder imageId) {
for (String parameter : parameters.split(",")) { for (String parameter : parameters.split(",")) {
switch (stationData.getType(parameter)) { switch (stationData.getType(parameter)) {
case STRING: case STRING:
@ -809,6 +809,7 @@ public class PlotModelFactory2 {
Formatter testing = new Formatter(sb); Formatter testing = new Formatter(sb);
testing.format(element.format, displayValue); testing.format(element.format, displayValue);
sValue = sb.toString(); sValue = sb.toString();
testing.close();
} else { } else {
sValue = Double.toString(displayValue); sValue = Double.toString(displayValue);
} }
@ -1073,6 +1074,7 @@ public class PlotModelFactory2 {
Formatter testing = new Formatter(sb); Formatter testing = new Formatter(sb);
testing.format(element.format, displayValue); testing.format(element.format, displayValue);
sValue = sb.toString(); sValue = sb.toString();
testing.close();
} }
} else { } else {
sValue = Double.toString(displayValue); sValue = Double.toString(displayValue);
@ -1138,6 +1140,7 @@ public class PlotModelFactory2 {
Formatter testing = new Formatter(sb); Formatter testing = new Formatter(sb);
testing.format(element.format, displayValue); testing.format(element.format, displayValue);
sValue = sb.toString(); sValue = sb.toString();
testing.close();
} else { } else {
sValue = Double.toString(displayValue); sValue = Double.toString(displayValue);
} }
@ -1197,8 +1200,8 @@ public class PlotModelFactory2 {
return major * 5; return major * 5;
} }
public synchronized List<PlotModelElement> getPlotFields() { public List<PlotModelElement> getPlotFields() {
return this.plotFields; return Collections.unmodifiableList(this.plotFields);
} }
/** /**
@ -1244,140 +1247,6 @@ public class PlotModelFactory2 {
this.plotMissingData = b; 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) { private File getTableFile(String fileName) {
File rval = PathManagerFactory.getPathManager().getStaticFile( File rval = PathManagerFactory.getPathManager().getStaticFile(
PLOT_MODEL_DIR + IPathManager.SEPARATOR + fileName); PLOT_MODEL_DIR + IPathManager.SEPARATOR + fileName);
@ -1385,10 +1254,24 @@ public class PlotModelFactory2 {
} }
public List<PlotModelElement> getSampleFields() { public List<PlotModelElement> getSampleFields() {
return this.sampleFields; return Collections.unmodifiableList(this.sampleFields);
} }
public boolean isCachingImages() { public boolean isCachingImages() {
return imageCache != null; 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.uf.viz.core.rsc.capabilities.OutlineCapability;
import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller; import com.raytheon.viz.pointdata.IPlotModelGeneratorCaller;
import com.raytheon.viz.pointdata.PlotAlertParser; import com.raytheon.viz.pointdata.PlotAlertParser;
import com.raytheon.viz.pointdata.PlotDataThreadPool;
import com.raytheon.viz.pointdata.PlotInfo; import com.raytheon.viz.pointdata.PlotInfo;
import com.raytheon.viz.pointdata.drawables.IPointImageExtension; import com.raytheon.viz.pointdata.drawables.IPointImageExtension;
import com.raytheon.viz.pointdata.drawables.IPointImageExtension.PointImage; 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.rsc.progdisc.SpiProgDisclosure;
import com.raytheon.viz.pointdata.thread.GetDataTask; import com.raytheon.viz.pointdata.thread.GetDataTask;
import com.raytheon.viz.pointdata.thread.GetDataTask.Params; import com.raytheon.viz.pointdata.thread.GetDataTask.Params;
import com.raytheon.viz.pointdata.thread.PlotThreadOverseer;
import com.raytheon.viz.pointdata.units.PlotUnits; import com.raytheon.viz.pointdata.units.PlotUnits;
import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Coordinate;
@ -98,6 +98,7 @@ import com.vividsolutions.jts.geom.Coordinate;
* Jun 25, 2013 1869 bsteffen Fix plot sampling. * Jun 25, 2013 1869 bsteffen Fix plot sampling.
* Sep 04, 2013 16519 kshresth Fix Metar Display Problem * Sep 04, 2013 16519 kshresth Fix Metar Display Problem
* Dec 02, 2013 2473 njensen Prog Disclose paint frames at high priority * Dec 02, 2013 2473 njensen Prog Disclose paint frames at high priority
* Mar 21, 2014 2868 njensen Use PlotThreadOverseer for increased efficiency
* *
* </pre> * </pre>
* *
@ -116,7 +117,7 @@ public class PlotResource2 extends
private AbstractProgDisclosure progressiveDisclosure; private AbstractProgDisclosure progressiveDisclosure;
private PlotDataThreadPool generator; private PlotThreadOverseer generator;
private double plotWidth; private double plotWidth;
@ -265,7 +266,7 @@ public class PlotResource2 extends
Map<String, RequestConstraint> request = resourceData.getMetadataMap(); Map<String, RequestConstraint> request = resourceData.getMetadataMap();
RequestConstraint plugin = request.get("pluginName"); RequestConstraint plugin = request.get("pluginName");
PlotUnits.register(); PlotUnits.register();
generator = new PlotDataThreadPool(aTarget, descriptor, generator = new PlotThreadOverseer(aTarget, descriptor,
this.resourceData.getPlotModelFile(), this.resourceData.getPlotModelFile(),
this.resourceData.getLevelKey(), plugin.getConstraintValue(), this.resourceData.getLevelKey(), plugin.getConstraintValue(),
this.resourceData.getMetadataMap(), this); this.resourceData.getMetadataMap(), this);
@ -279,7 +280,7 @@ public class PlotResource2 extends
this.generator.setPlotMissingData(resourceData.isPlotMissingData()); this.generator.setPlotMissingData(resourceData.isPlotMissingData());
this.generator.setLowerLimit(resourceData.getLowerLimit()); this.generator.setLowerLimit(resourceData.getLowerLimit());
this.generator.setUpperLimit(resourceData.getUpperLimit()); this.generator.setUpperLimit(resourceData.getUpperLimit());
this.plotWidth = generator.getPlotModelWidth(); this.plotWidth = generator.getOriginalPlotModelWidth();
this.plotWidth *= getCapability(MagnificationCapability.class) this.plotWidth *= getCapability(MagnificationCapability.class)
.getMagnification(); .getMagnification();
generator.setPlotModelSize(Math.round(plotWidth)); generator.setPlotModelSize(Math.round(plotWidth));
@ -431,7 +432,7 @@ public class PlotResource2 extends
list.add(samplePlot); list.add(samplePlot);
Params params = Params.PLOT_AND_SAMPLE; Params params = Params.PLOT_AND_SAMPLE;
GetDataTask task = new GetDataTask(list, params); GetDataTask task = new GetDataTask(list, params);
generator.queueStation(task); generator.enqueueDataRetrieval(task);
// End DR14996 // End DR14996
} }
boolean dup = false; boolean dup = false;
@ -575,7 +576,7 @@ public class PlotResource2 extends
params = Params.SAMPLE_ONLY; params = Params.SAMPLE_ONLY;
} }
GetDataTask task = new GetDataTask(list, params); 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) { } else if (object instanceof MagnificationCapability) {
if (generator != null) { if (generator != null) {
this.plotWidth = generator.getPlotModelWidth(); this.plotWidth = generator.getOriginalPlotModelWidth();
this.plotWidth *= getCapability( this.plotWidth *= getCapability(
MagnificationCapability.class).getMagnification(); MagnificationCapability.class).getMagnification();
generator.setPlotModelSize(Math.round(plotWidth)); generator.setPlotModelSize(Math.round(plotWidth));
@ -758,7 +759,7 @@ public class PlotResource2 extends
} }
if (toQueue.size() > 0) { if (toQueue.size() > 0) {
GetDataTask task = new GetDataTask(toQueue, Params.PLOT_ONLY); GetDataTask task = new GetDataTask(toQueue, Params.PLOT_ONLY);
generator.queueStation(task); generator.enqueueDataRetrieval(task);
} }
} else { } else {
issueRefresh(); 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; 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> * <pre>
* *
@ -33,6 +34,7 @@ import com.raytheon.viz.pointdata.PlotInfo;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Jul 18, 2011 njensen Initial creation * Jul 18, 2011 njensen Initial creation
* Mar 21, 2014 2868 njensen Improved javadoc
* *
* </pre> * </pre>
* *

View file

@ -17,7 +17,7 @@
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for * See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information. * further licensing information.
**/ **/
package com.raytheon.viz.pointdata; package com.raytheon.viz.pointdata.thread;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -27,30 +27,25 @@ import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status; 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;
import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType; import com.raytheon.uf.common.dataquery.requests.RequestConstraint.ConstraintType;
import com.raytheon.uf.common.pointdata.PointDataContainer; import com.raytheon.uf.common.pointdata.PointDataContainer;
import com.raytheon.uf.common.pointdata.PointDataView; 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.status.UFStatus.Priority;
import com.raytheon.uf.common.time.DataTime; 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.datastructure.DataCubeContainer;
import com.raytheon.uf.viz.core.exception.VizException; 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.PlotModelFactory2.PlotModelElement;
import com.raytheon.viz.pointdata.PointDataRequest;
import com.raytheon.viz.pointdata.rsc.PlotResourceData; 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 * Job that requests plot data based on a constraintMap and the parameters
* on to the PlotModelGeneratorJob. * specified inside the plot model SVG file.
* *
* <pre> * <pre>
* *
@ -60,6 +55,7 @@ import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob;
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Apr 22, 2011 njensen Initial creation * Apr 22, 2011 njensen Initial creation
* May 14, 2013 1869 bsteffen Get plots working without dataURI * May 14, 2013 1869 bsteffen Get plots working without dataURI
* Mar 21, 2014 2868 njensen Major refactor
* *
* </pre> * </pre>
* *
@ -67,53 +63,42 @@ import com.raytheon.viz.pointdata.thread.PlotSampleGeneratorJob;
* @version 1.0 * @version 1.0
*/ */
public class PlotModelDataRequestJob extends Job { public class PlotModelDataRequestJob extends AbstractPlotCreationJob {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotModelDataRequestJob.class);
private PlotModelFactory2 plotCreator;
private Map<String, RequestConstraint> constraintMap; 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(PlotThreadOverseer parent,
IPlotModelGeneratorCaller caller,
public PlotModelDataRequestJob(IGraphicsTarget aTarget, List<PlotModelElement> plotFields,
IMapDescriptor mapDescriptor, String plotModelFile, List<PlotModelElement> sampleFields, String levelKey,
String levelKey, String plugin, String plugin, Map<String, RequestConstraint> constraintMap)
Map<String, RequestConstraint> constraintMap,
IPlotModelGeneratorCaller caller, PlotDataThreadPool parent)
throws VizException { throws VizException {
super("Requesting Plot Data..."); super("Requesting Plot Data...", parent, caller);
plotCreator = new PlotModelFactory2(mapDescriptor, plotModelFile); this.plotFields = plotFields;
this.sampleFields = sampleFields;
this.plugin = plugin; this.plugin = plugin;
this.levelKey = levelKey; this.levelKey = levelKey;
this.constraintMap = constraintMap; 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 @Override
protected IStatus run(IProgressMonitor monitor) { protected IStatus run(IProgressMonitor monitor) {
while (parent.stationQueue.size() > 0) { while (overseer.dataRetrievalQueue.size() > 0) {
List<PlotInfo[]> stationQuery = new ArrayList<PlotInfo[]>(); List<PlotInfo[]> stationQuery = new ArrayList<PlotInfo[]>();
GetDataTask task = null; GetDataTask task = null;
synchronized (this) { synchronized (this) {
task = parent.stationQueue.poll(); task = overseer.dataRetrievalQueue.poll();
if (task == null) { if (task == null) {
// possibility another thread got it first
continue; continue;
} }
List<PlotInfo[]> batch = task.getStations(); List<PlotInfo[]> batch = task.getStations();
@ -125,15 +110,15 @@ public class PlotModelDataRequestJob extends Job {
List<PlotModelElement> pme = null; List<PlotModelElement> pme = null;
switch (task.getRequestType()) { switch (task.getRequestType()) {
case PLOT_ONLY: case PLOT_ONLY:
pme = this.plotCreator.getPlotFields(); pme = plotFields;
break; break;
case SAMPLE_ONLY: case SAMPLE_ONLY:
pme = this.plotCreator.getSampleFields(); pme = sampleFields;
break; break;
case PLOT_AND_SAMPLE: case PLOT_AND_SAMPLE:
pme = new ArrayList<PlotModelElement>(); pme = new ArrayList<PlotModelElement>();
pme.addAll(this.plotCreator.getPlotFields()); pme.addAll(plotFields);
pme.addAll(this.plotCreator.getSampleFields()); pme.addAll(sampleFields);
default: default:
break; break;
} }
@ -161,14 +146,14 @@ public class PlotModelDataRequestJob extends Job {
if (infos[0].pdv != null) { if (infos[0].pdv != null) {
switch (task.getRequestType()) { switch (task.getRequestType()) {
case PLOT_ONLY: case PLOT_ONLY:
this.generatorJob.enqueue(infos); overseer.enqueueImageGeneration(infos);
break; break;
case SAMPLE_ONLY: case SAMPLE_ONLY:
this.sampleJob.enqueue(infos); overseer.enqueueSamplePlot(infos);
break; break;
case PLOT_AND_SAMPLE: case PLOT_AND_SAMPLE:
this.generatorJob.enqueue(infos); overseer.enqueueImageGeneration(infos);
this.sampleJob.enqueue(infos); overseer.enqueueSamplePlot(infos);
break; break;
} }
} }
@ -186,12 +171,13 @@ public class PlotModelDataRequestJob extends Job {
List<String> params = new ArrayList<String>(); List<String> params = new ArrayList<String>();
for (PlotModelElement p : pme) { for (PlotModelElement p : pme) {
if (!p.parameter.equals("") && !p.parameter.contains(",")) { String param = p.getParameter();
params.add(p.parameter); if (!param.equals("") && !param.contains(",")) {
} else if (p.parameter.contains(",")) { params.add(param);
String[] individualParams = p.parameter.split(","); } else if (param.contains(",")) {
for (String param : individualParams) { String[] individualParams = param.split(",");
params.add(param); for (String paramToRequest : individualParams) {
params.add(paramToRequest);
} }
} }
} }
@ -200,7 +186,7 @@ public class PlotModelDataRequestJob extends Job {
.getPluginProperties(plugin).hasDistinctStationId; .getPluginProperties(plugin).hasDistinctStationId;
String uniquePointDataKey = "stationId"; String uniquePointDataKey = "stationId";
String uniqueQueryKey = "location.stationId"; String uniqueQueryKey = "location.stationId";
if(!hasDistinctStationId){ if (!hasDistinctStationId) {
uniquePointDataKey = "dataURI"; uniquePointDataKey = "dataURI";
uniqueQueryKey = uniquePointDataKey; uniqueQueryKey = uniquePointDataKey;
@ -208,7 +194,7 @@ public class PlotModelDataRequestJob extends Job {
if (!params.contains(uniquePointDataKey)) { if (!params.contains(uniquePointDataKey)) {
params.add(uniquePointDataKey); params.add(uniquePointDataKey);
} }
Map<String, RequestConstraint> map = new HashMap<String, RequestConstraint>(); Map<String, RequestConstraint> map = new HashMap<String, RequestConstraint>();
map.putAll(this.constraintMap); map.putAll(this.constraintMap);
RequestConstraint rc = new RequestConstraint(); RequestConstraint rc = new RequestConstraint();
@ -221,7 +207,7 @@ public class PlotModelDataRequestJob extends Job {
String key = null; String key = null;
if (hasDistinctStationId) { if (hasDistinctStationId) {
key = info.stationId; key = info.stationId;
}else{ } else {
key = info.dataURI; key = info.dataURI;
} }
str.add(key); 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 * See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information. * further licensing information.
**/ **/
package com.raytheon.viz.pointdata; package com.raytheon.viz.pointdata.thread;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage; import java.awt.image.RenderedImage;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.collections.map.LRUMap; import org.apache.commons.collections.map.LRUMap;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status; 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.IGraphicsTarget;
import com.raytheon.uf.viz.core.data.IRenderedImageCallback; import com.raytheon.uf.viz.core.data.IRenderedImageCallback;
import com.raytheon.uf.viz.core.drawables.IImage; import com.raytheon.uf.viz.core.drawables.IImage;
import com.raytheon.uf.viz.core.drawables.ext.ISingleColorImageExtension; import com.raytheon.uf.viz.core.drawables.ext.ISingleColorImageExtension;
import com.raytheon.uf.viz.core.exception.VizException; 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> * <pre>
* *
@ -48,6 +47,7 @@ import com.raytheon.uf.viz.core.exception.VizException;
* Date Ticket# Engineer Description * Date Ticket# Engineer Description
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Apr 22, 2011 njensen Initial creation * Apr 22, 2011 njensen Initial creation
* Mar 21, 2014 2868 njensen Major refactor
* *
* </pre> * </pre>
* *
@ -55,27 +55,20 @@ import com.raytheon.uf.viz.core.exception.VizException;
* @version 1.0 * @version 1.0
*/ */
public class PlotModelGeneratorJob extends Job { public class PlotModelGeneratorJob extends AbstractPlotCreationJob {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotModelDataRequestJob.class);
private ConcurrentLinkedQueue<PlotInfo[]> taskQueue = new ConcurrentLinkedQueue<PlotInfo[]>();
private PlotModelFactory2 plotCreator; private PlotModelFactory2 plotCreator;
private IPlotModelGeneratorCaller caller;
private IGraphicsTarget target; private IGraphicsTarget target;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Map<BufferedImage, IImage> imageCache = new LRUMap(1000); private Map<BufferedImage, IImage> imageCache = new LRUMap(1000);
protected PlotModelGeneratorJob(PlotModelFactory2 plotCreator, protected PlotModelGeneratorJob(PlotThreadOverseer parent,
IPlotModelGeneratorCaller caller, IGraphicsTarget target) { IPlotModelGeneratorCaller caller, PlotModelFactory2 plotCreator,
super("Creating plots"); IGraphicsTarget target) {
super("Creating plots", parent, caller);
this.plotCreator = plotCreator; this.plotCreator = plotCreator;
this.caller = caller;
this.target = target; this.target = target;
} }
@ -88,9 +81,14 @@ public class PlotModelGeneratorJob extends Job {
@Override @Override
protected IStatus run(IProgressMonitor monitor) { protected IStatus run(IProgressMonitor monitor) {
long t0 = System.currentTimeMillis(); long t0 = System.currentTimeMillis();
while (!taskQueue.isEmpty()) { long count = 0;
while (!overseer.imageCreationQueue.isEmpty()) {
try { 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( final BufferedImage bImage = plotCreator.getStationPlot(
infos[0].pdv, infos[0].latitude, infos[0].longitude); infos[0].pdv, infos[0].latitude, infos[0].longitude);
IImage image = null; IImage image = null;
@ -120,46 +118,40 @@ public class PlotModelGeneratorJob extends Job {
} }
synchronized (this) { synchronized (this) {
if (monitor.isCanceled()) { if (monitor.isCanceled()) {
if(image != null){ if (image != null) {
image.dispose(); image.dispose();
} }
break; break;
} }
caller.modelGenerated(infos, image); count++;
listener.modelGenerated(infos, image);
} }
} catch (Exception e) { } catch (Exception e) {
statusHandler.error("Error creating plot", e); statusHandler.error("Error creating plot with plotModel "
+ plotCreator.getPlotModelFilename(), e);
} }
} }
plotCreator.disposeScript(); if (count > 0) {
System.out.println("Time spent creating plots: " /*
+ (System.currentTimeMillis() - t0)); * if count is zero it means by the time this job was scheduled and
return Status.OK_STATUS; * run, a different job took care of everything on the queue
} */
System.out.println("Time spent creating " + count + " plots: "
protected void enqueue(PlotInfo[] task) { + (System.currentTimeMillis() - t0));
this.taskQueue.add(task);
if (this.getState() != Job.RUNNING) {
this.schedule();
} }
} return Status.OK_STATUS;
protected int getQueueSize() {
return taskQueue.size();
} }
protected void clearImageCache() { protected void clearImageCache() {
imageCache.clear(); imageCache.clear();
} }
public boolean isDone() { @Override
return getState() != Job.RUNNING && getState() != Job.WAITING; public boolean shutdown() {
} boolean result = super.shutdown();
protected synchronized void shutdown() {
cancel();
taskQueue.clear();
clearImageCache(); clearImageCache();
plotCreator.dispose();
return result;
} }
} }

View file

@ -19,22 +19,17 @@
**/ **/
package com.raytheon.viz.pointdata.thread; package com.raytheon.viz.pointdata.thread;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status; 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.IPlotModelGeneratorCaller;
import com.raytheon.viz.pointdata.PlotInfo; import com.raytheon.viz.pointdata.PlotInfo;
import com.raytheon.viz.pointdata.PlotModelFactory2; import com.raytheon.viz.pointdata.PlotModelFactory2;
/** /**
* Job that uses the provided factory to generate a sampling message for a * Job that uses the provided plot model factory to generate a sampling message
* particular plot. * for a particular plot.
* *
* <pre> * <pre>
* *
@ -44,6 +39,7 @@ import com.raytheon.viz.pointdata.PlotModelFactory2;
* ------------ ---------- ----------- -------------------------- * ------------ ---------- ----------- --------------------------
* Jul 13, 2011 njensen Initial creation * Jul 13, 2011 njensen Initial creation
* Jun 25, 2013 1869 bsteffen Fix plot sampling. * Jun 25, 2013 1869 bsteffen Fix plot sampling.
* Mar 21, 2014 2868 njensen Major refactor
* *
* </pre> * </pre>
* *
@ -51,22 +47,14 @@ import com.raytheon.viz.pointdata.PlotModelFactory2;
* @version 1.0 * @version 1.0
*/ */
public class PlotSampleGeneratorJob extends Job { public class PlotSampleGeneratorJob extends AbstractPlotCreationJob {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(PlotSampleGeneratorJob.class);
private PlotModelFactory2 plotFactory; private PlotModelFactory2 plotFactory;
private ConcurrentLinkedQueue<PlotInfo[]> queue = new ConcurrentLinkedQueue<PlotInfo[]>(); public PlotSampleGeneratorJob(PlotThreadOverseer parent,
IPlotModelGeneratorCaller caller, PlotModelFactory2 factory) {
private IPlotModelGeneratorCaller caller; super("Generating samples", parent, caller);
public PlotSampleGeneratorJob(PlotModelFactory2 factory,
IPlotModelGeneratorCaller caller) {
super("Generating samples");
this.plotFactory = factory; this.plotFactory = factory;
this.caller = caller;
} }
/* /*
@ -77,27 +65,29 @@ public class PlotSampleGeneratorJob extends Job {
*/ */
@Override @Override
protected IStatus run(IProgressMonitor monitor) { protected IStatus run(IProgressMonitor monitor) {
while (!queue.isEmpty()) { while (!overseer.sampleTextQueue.isEmpty()) {
try { 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, String message = plotFactory.getStationMessage(infos[0].pdv,
infos[0].dataURI); infos[0].dataURI);
listener.messageGenerated(infos, message);
caller.messageGenerated(infos, message);
} catch (Exception e) { } 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; return Status.OK_STATUS;
} }
public void enqueue(PlotInfo[] task) { @Override
this.queue.add(task); public boolean shutdown() {
if (this.getState() != Job.RUNNING) { boolean result = super.shutdown();
this.schedule(); 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.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;
import jep.JepException; import jep.JepException;
@ -59,6 +60,9 @@ import com.raytheon.uf.common.python.PythonInterpreter;
* Jan 31, 2013 mnash Initial creation * Jan 31, 2013 mnash Initial creation
* Jun 04, 2013 2041 bsteffen Improve exception handling for concurrent * Jun 04, 2013 2041 bsteffen Improve exception handling for concurrent
* python. * python.
* Mar 21, 2014 2868 njensen Changed getInstance() from throwing
* RuntimeException to IllegalArgumentException
* Added refCount
* *
* </pre> * </pre>
* *
@ -71,10 +75,17 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
private ThreadLocal<P> threadLocal = null; 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 static Map<String, PythonJobCoordinator<? extends PythonInterpreter>> pools = new ConcurrentHashMap<String, PythonJobCoordinator<? extends PythonInterpreter>>();
private PythonJobCoordinator(final AbstractPythonScriptFactory<P> factory) { private PythonJobCoordinator(final AbstractPythonScriptFactory<P> factory) {
threadLocal = new ThreadLocal<P>() { threadLocal = new ThreadLocal<P>() {
@Override
protected P initialValue() { protected P initialValue() {
try { try {
return factory.createPythonScript(); return factory.createPythonScript();
@ -85,10 +96,11 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
}; };
execService = Executors.newFixedThreadPool(factory.getMaxThreads(), execService = Executors.newFixedThreadPool(factory.getMaxThreads(),
new PythonThreadFactory(threadLocal, factory.getName())); 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 * @param name
* @return * @return
@ -99,7 +111,7 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
if (pools.containsKey(name)) { if (pools.containsKey(name)) {
return (PythonJobCoordinator<S>) pools.get(name); return (PythonJobCoordinator<S>) pools.get(name);
} else { } else {
throw new RuntimeException( throw new IllegalArgumentException(
"Unable to find instance of PythonJobCoordinator named " "Unable to find instance of PythonJobCoordinator named "
+ name + name
+ ", please call newInstance(AbstractPythonScriptFactory)"); + ", 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 * 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 * 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 name
* @param numThreads * @param numThreads
@ -119,14 +134,15 @@ public class PythonJobCoordinator<P extends PythonInterpreter> {
public static <S extends PythonInterpreter> PythonJobCoordinator<S> newInstance( public static <S extends PythonInterpreter> PythonJobCoordinator<S> newInstance(
AbstractPythonScriptFactory<S> factory) { AbstractPythonScriptFactory<S> factory) {
synchronized (pools) { synchronized (pools) {
PythonJobCoordinator<S> pool = null;
if (pools.containsKey(factory.getName())) { if (pools.containsKey(factory.getName())) {
return (PythonJobCoordinator<S>) pools.get(factory.getName()); pool = (PythonJobCoordinator<S>) pools.get(factory.getName());
} else { } else {
PythonJobCoordinator<S> pool = new PythonJobCoordinator<S>( pool = new PythonJobCoordinator<S>(factory);
factory);
pools.put(factory.getName(), pool); 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 * This function should take the {@link PythonInterpreter} on each thread in
* the thread pool and dispose of it and then shutdown the * 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 * @param name
*/ */
public void shutdown() { public void shutdown() {
synchronized (pools) { 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) { 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
*/ */
} }
} }