--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2011 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.trend.impl;\r
+\r
+import java.awt.AlphaComposite;\r
+import java.awt.BasicStroke;\r
+import java.awt.Color;\r
+import java.awt.Composite;\r
+import java.awt.Graphics2D;\r
+import java.awt.RenderingHints;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Path2D;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+import java.util.Arrays;\r
+import java.util.List;\r
+import java.util.TreeSet;\r
+import java.util.logging.Level;\r
+import java.util.logging.Logger;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.accessor.StreamAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.BooleanBinding;\r
+import org.simantics.databoard.binding.ByteBinding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.type.BooleanType;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.NumberType;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.util.Bean;\r
+import org.simantics.history.HistoryException;\r
+import org.simantics.history.HistoryItem;\r
+import org.simantics.history.HistoryManager;\r
+import org.simantics.history.ItemManager;\r
+import org.simantics.history.util.Stream;\r
+import org.simantics.history.util.ValueBand;\r
+import org.simantics.history.util.subscription.SamplingFormat;\r
+import org.simantics.scenegraph.g2d.G2DNode;\r
+import org.simantics.trend.configuration.LineQuality;\r
+import org.simantics.trend.configuration.Scale;\r
+import org.simantics.trend.configuration.TrendItem;\r
+import org.simantics.trend.configuration.TrendItem.DrawMode;\r
+import org.simantics.trend.configuration.TrendItem.Renderer;\r
+import org.simantics.trend.util.KvikDeviationBuilder;\r
+\r
+/**\r
+ * Data node for a TrendItem\r
+ * \r
+ * @author toni.kalajainen\r
+ */\r
+public class ItemNode extends G2DNode implements TrendLayout {\r
+\r
+ private static final long serialVersionUID = -4741446944761752871L;\r
+\r
+ public static final AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .61f);\r
+\r
+ public TrendItem item;\r
+ VertRuler ruler;\r
+ TrendNode trendNode;\r
+\r
+ /** History Items */\r
+ Bean historyItems[];\r
+ \r
+ /** A list of items that is ordered in by interval value, starting from the lowest interval */\r
+ Bean[] renderableItems = new Bean[0];\r
+ \r
+ /** Format for the minmax stream, or null */ \r
+ Bean minmaxItem;\r
+ \r
+ /** Known item min->max value range, and from->end time range */\r
+ double from=Double.NaN, end=Double.NaN, min=0, max=1;\r
+\r
+ BasicStroke stroke;\r
+ Color color;\r
+ \r
+ Stream openStream;\r
+ StreamAccessor openStreamAccessor;\r
+ Bean openStreamItem;\r
+ \r
+ Logger log = Logger.getLogger( this.getClass().getName() ); \r
+ \r
+ boolean disabled = false;\r
+ Point2D.Double pt = new Point2D.Double();\r
+ Point2D.Double pt2 = new Point2D.Double();\r
+ \r
+ // Cached shapes\r
+ KvikDeviationBuilder dev = new KvikDeviationBuilder();\r
+ Path2D.Double line = new Path2D.Double();\r
+ \r
+ /**\r
+ * Set trend item and initialize history\r
+ * @param ti\r
+ * @param items all items in history\r
+ */\r
+ public void setTrendItem(TrendItem ti, ItemManager items) {\r
+ if (openStream!=null) {\r
+ openStream.close();\r
+ openStream = null;\r
+ openStreamAccessor = null;\r
+ openStreamItem = null;\r
+ }\r
+ this.item = ti;\r
+ this.minmaxItem = null;\r
+ this.renderableItems = new HistoryItem[0];\r
+ disabled = true;\r
+ if (ti==null) return;\r
+ \r
+ try {\r
+ List<Bean> trendItems = items.search("groupId", ti.groupId, "groupItemId", ti.groupItemId, "variableId", ti.variableId);\r
+ this.historyItems = trendItems.toArray( new Bean[trendItems.size()] );\r
+ Arrays.sort( this.historyItems, SamplingFormat.INTERVAL_COMPARATOR );\r
+ \r
+ // Read renderable formats, and minmax format\r
+ TreeSet<Bean> streamFormats = new TreeSet<Bean>( SamplingFormat.INTERVAL_COMPARATOR );\r
+ for (Bean item : trendItems) {\r
+ SamplingFormat format = new SamplingFormat();\r
+ format.readAvailableFields(item);\r
+ RecordType rt = (RecordType) format.format;\r
+ Boolean enabled = (Boolean) item.getField("enabled");\r
+ if (!enabled) continue;\r
+ \r
+ boolean isMinMaxFormat = format.interval==Double.MAX_VALUE &&\r
+ format.deadband==Double.MAX_VALUE &&\r
+ rt.getComponentIndex2("min")>=0 &&\r
+ rt.getComponentIndex2("max")>=0;\r
+ \r
+ if (isMinMaxFormat) {\r
+ this.minmaxItem = item;\r
+ } else {\r
+ streamFormats.add(item);\r
+ } \r
+ }\r
+ if (streamFormats.isEmpty()) return;\r
+ \r
+ renderableItems = streamFormats.toArray( new Bean[streamFormats.size()] );\r
+ disabled = false;\r
+ } catch (BindingException e) {\r
+ throw new RuntimeException( e );\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Draw to graphics context as time,value pairs are.\r
+ * \r
+ * Phases, 0-Init data, 1-Draw deviation, 2-Draw line, 3-Cleanup \r
+ * \r
+ * @param g\r
+ * @param phase \r
+ */\r
+ public void draw(Graphics2D g, int phase, boolean bold) {\r
+ boolean devAndLine = \r
+ item.drawMode==DrawMode.DeviationAndAverage || \r
+ item.drawMode==DrawMode.DeviationAndLine || \r
+ item.drawMode==DrawMode.DeviationAndSample;\r
+ \r
+ // Draw deviation\r
+ Object newQuality = getTrendNode().printing||getTrendNode().quality.lineQuality==LineQuality.Antialias?RenderingHints.VALUE_ANTIALIAS_ON:RenderingHints.VALUE_ANTIALIAS_OFF;\r
+ if (phase == 1) { \r
+ g.setColor(color);\r
+ if (!dev.isEmpty()) {\r
+ Object old = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);\r
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, newQuality);\r
+\r
+ if (item.renderer == Renderer.Binary) {\r
+ dev.drawRectangles(g);\r
+ } \r
+ \r
+ if (item.renderer == Renderer.Analog) {\r
+ if (devAndLine) {\r
+ // Draw opaque\r
+ Composite c = g.getComposite();\r
+ g.setComposite( composite );\r
+ dev.drawRectangles(g);\r
+ g.setComposite( c );\r
+ } else if (item.drawMode == DrawMode.Deviation) {\r
+ // Draw solid\r
+ dev.drawRectangles(g);\r
+ }\r
+ }\r
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, old);\r
+ } \r
+ }\r
+ \r
+ // Draw line\r
+ if (phase == 2) {\r
+ g.setColor(color);\r
+ Object old = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);\r
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, newQuality);\r
+ g.draw(line); \r
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, old);\r
+ }\r
+ \r
+ }\r
+ \r
+ private boolean validSample(ValueBand band) throws HistoryException {\r
+ if(band.getTimeDouble() > 1e50) return false;\r
+ if(band.getTimeDouble() > band.getEndTimeDouble()) return false;\r
+// if(band.getTimeDouble() < 0) return false;\r
+// if(band.getEndTimeDouble() < 0) return false;\r
+ //if(band.getEndTimeDouble()-band.getTimeDouble() < 1e-9) return false;\r
+ return true;\r
+ }\r
+ \r
+ // Draw state needed for clipping\r
+ double currentX;\r
+ double currentY;\r
+ double minX;\r
+ double maxX;\r
+ \r
+ void moveTo(double x, double y) {\r
+ line.moveTo(x, y);\r
+ currentX = x;\r
+ currentY = y;\r
+ }\r
+\r
+ void lineToOrdered(double x, double y) {\r
+ \r
+ if(x < minX) return;\r
+ if(currentX > maxX) return;\r
+ \r
+ // We have something to draw\r
+ \r
+ // Clip left\r
+ if(currentX < minX) {\r
+ double ny = currentY + (y-currentY) * (minX-currentX) / (x-currentX);\r
+ line.moveTo(minX, ny);\r
+ currentX = minX;\r
+ currentY = ny;\r
+ }\r
+\r
+ // Right clip\r
+ if(x > maxX) {\r
+ double ny = currentY + (y-currentY) * (maxX-currentX) / (x-currentX);\r
+ line.lineTo(maxX, ny);\r
+ line.moveTo(x, y); \r
+ } else {\r
+ line.lineTo(x, y); \r
+ }\r
+ \r
+ }\r
+ \r
+ void lineTo(double x, double y) {\r
+\r
+ // First assert ordering - if samples are in wrong order draw nothing\r
+ if(currentX <= x) {\r
+ lineToOrdered(x, y);\r
+ } else {\r
+ line.moveTo(x, y);\r
+ }\r
+ \r
+ currentX = x;\r
+ currentY = y;\r
+ \r
+ }\r
+ \r
+\r
+ /**\r
+ * This method prepares line and deviation shapes.\r
+ * @param from\r
+ * @param end\r
+ * @param secondsPerPixel\r
+ * @param at \r
+ * @throws HistoryException \r
+ * @throws AccessorException \r
+ */\r
+ public void prepareLine(double from, double end, double pixelsPerSecond, AffineTransform at) throws HistoryException, AccessorException\r
+ {\r
+// boolean devAndLine = \r
+// item.drawMode==DrawMode.DeviationAndAverage || \r
+// item.drawMode==DrawMode.DeviationAndLine || \r
+// item.drawMode==DrawMode.DeviationAndSample;\r
+// boolean deviationEnabled = devAndLine || \r
+// item.drawMode == DrawMode.Deviation;\r
+\r
+ boolean devAndLine = false;\r
+ boolean deviationEnabled = false;\r
+ \r
+ // Collect data\r
+ dev.reset();\r
+ line.reset();\r
+ Stream s = openStream(pixelsPerSecond);\r
+ if (s==null) return;\r
+ ValueBand vb = new ValueBand(s.sampleBinding, s.sampleBinding.createDefaultUnchecked());\r
+ boolean hasDeviation = vb.hasMin() && vb.hasMax();\r
+ boolean drawDeviation = hasDeviation & deviationEnabled;\r
+ \r
+ Datatype valueType = vb.getValueBinding().type();\r
+ s.reset();\r
+ if ( valueType instanceof BooleanType == false && valueType instanceof NumberType == false ) return;\r
+ \r
+ int start = s.binarySearch(Bindings.DOUBLE, from);\r
+ int count = s.count();\r
+ if (start<0) start = -2-start;\r
+ if (start<0) start = 0;\r
+ \r
+ // moveTo, on next draw, if true\r
+ boolean lineNotAttached = true; // =next draw should be move to\r
+ boolean devNotAttached = true;\r
+ \r
+ currentX = Double.NaN;\r
+ currentY = Double.NaN;\r
+\r
+ pt.setLocation(from, 0);\r
+ at.transform(pt, pt);\r
+ minX = pt.x;\r
+ \r
+ pt.setLocation(end, 0);\r
+ at.transform(pt, pt);\r
+ maxX = pt.x;\r
+ \r
+ s.reset();\r
+ \r
+ boolean wentOver = false;\r
+ \r
+ for (int i=start; i<count; i++) {\r
+ // Read sample\r
+ s.accessor.get(i, s.sampleBinding, vb.getSample());\r
+ \r
+ if(!validSample(vb)) {\r
+ System.err.println("-invalid value band: " + i + "/" + count + ":" + vb);\r
+ continue;\r
+ }\r
+ // Non-continuation point\r
+ if (vb.hasQuality()) {\r
+ Byte b = (Byte) vb.getQuality(Bindings.BYTE);\r
+ boolean noncontinuation = b.equals( ValueBand.QUALITY_NOVALUE ); \r
+ if ( noncontinuation ) {\r
+ lineNotAttached = true;\r
+ devNotAttached = true;\r
+ continue;\r
+ }\r
+ }\r
+ \r
+ // Line\r
+ double t1 = vb.getTimeDouble();\r
+ double t2 = vb.hasEndTime() ? vb.getEndTimeDouble() : t1;\r
+ \r
+ // Analog signal\r
+ if (item.renderer == Renderer.Analog) {\r
+ \r
+ // Add points to line\r
+ if (item.drawMode == DrawMode.Deviation && hasDeviation) {\r
+ // ...\r
+ } else {\r
+ \r
+ double yy = Double.NaN;\r
+// boolean showBand = true;\r
+ boolean flat = false;\r
+\r
+ if( trendNode != null && trendNode.drawSamples ) {\r
+ yy = vb.getValueDouble();\r
+ flat = true;\r
+ } else {\r
+ yy = vb.getValueDouble();\r
+ // Only render the last band\r
+// if(i < count-1) showBand = false; \r
+ }\r
+ \r
+// if (item.drawMode == DrawMode.DeviationAndAverage) {\r
+// yy = vb.hasMedian() ? vb.getMedianDouble() : ( vb.hasAvg() ? vb.getAvgDouble() : vb.getValueDouble() );\r
+// } else if (item.drawMode == DrawMode.Average || item.drawMode == DrawMode.DeviationAndAverage) {\r
+// yy = vb.hasAvg() ? vb.getAvgDouble() : vb.getValueDouble();\r
+// } else if (item.drawMode == DrawMode.Line || item.drawMode == DrawMode.DeviationAndLine) {\r
+// yy = vb.getValueDouble();\r
+// // Only render the last band\r
+// if(i < count-1) showBand = false; \r
+// } else /* if (item.drawMode == DrawMode.Sample || item.drawMode == DrawMode.DeviationAndSample) */ {\r
+// yy = vb.getValueDouble();\r
+// flat = true;\r
+// } \r
+ \r
+ if ( Double.isNaN(yy) ) {\r
+ lineNotAttached = true;\r
+ } else {\r
+// pt.setLocation(t1, yy);\r
+// at.transform(pt, pt);\r
+// if ( t1==t2 ) {\r
+// if (lineNotAttached) {\r
+// moveTo(pt.getX(), pt.getY());\r
+// } else {\r
+// if(flat) {\r
+// \r
+// } else {\r
+// lineTo(pt.getX(), pt.getY());\r
+// }\r
+// }\r
+// } else {\r
+ // Static variables may have months data that consists of single value\r
+ // When zoomed in, the single line may be drawn from -1e10, ... 1e10\r
+ // This is too much for Graphics2D, it refuses to draw.\r
+// if (t1<from) {\r
+// t1 = from;\r
+// }\r
+// if (t2>end) {\r
+// t2 = end;\r
+// }\r
+ \r
+ pt.setLocation(t1, yy);\r
+ at.transform(pt, pt);\r
+ pt2.setLocation(t2, yy);\r
+ at.transform(pt2, pt2);\r
+\r
+ double x1 = pt.x, y1 = pt.y, x2 = pt2.x, y2 = pt2.y;\r
+\r
+ if (lineNotAttached) {\r
+ moveTo(x1, y1);\r
+ } else {\r
+ if(flat) {\r
+ lineTo(x1, currentY);\r
+ lineTo(x1, y1);\r
+ } else {\r
+ lineTo(x1, y1);\r
+ }\r
+ }\r
+\r
+ lineTo(x2, y2);\r
+ \r
+// if(showBand) {\r
+// lineTo(x2, y2);\r
+// }\r
+ \r
+// }\r
+ lineNotAttached = false; \r
+ }\r
+ }\r
+ \r
+ if (drawDeviation) {\r
+ double min = vb.getMinDouble();\r
+ double max = vb.getMaxDouble();\r
+\r
+ pt.setLocation(t1, min);\r
+ at.transform(pt, pt);\r
+ pt2.setLocation(t2, max);\r
+ at.transform(pt2, pt2);\r
+ double x1 = pt.x;\r
+ double x2 = pt2.x;\r
+ double y1 = pt.y;\r
+ double y2 = pt2.y;\r
+ \r
+ double width = x2-x1;\r
+ boolean tooWide = devAndLine && (width>2.0);\r
+ if (Double.isNaN(min) || Double.isNaN(max) || tooWide || width<=0.0 || y1==y2) {\r
+ devNotAttached = true;\r
+ } else {\r
+ if (devNotAttached) {\r
+ dev.addRectangle(x1, x2, y1, y2);\r
+ } else {\r
+ dev.extendRectangle(x1);\r
+ dev.addRectangle(x1, x2, y1, y2);\r
+ }\r
+ devNotAttached = false;\r
+ }\r
+ }\r
+ }\r
+ \r
+ // Binary signal\r
+ if (item.renderer == Renderer.Binary) {\r
+ byte value = 0;\r
+ if (vb.getValueBinding() instanceof BooleanBinding) {\r
+ value = ((Boolean) vb.getValue(Bindings.BOOLEAN)) ? (byte)0 : (byte)1;\r
+ } else if (vb.getValueBinding() instanceof ByteBinding) {\r
+ value = ((Byte) vb.getValue(Bindings.BYTE));\r
+ } else if (vb.hasMax()) {\r
+ value = (Byte) vb.getMax(Bindings.BYTE);\r
+ } else {\r
+ value = (Byte) vb.getValue(Bindings.BYTE);\r
+ }\r
+ pt.setLocation(t1, value==1 ? BINARY[1] : BINARY[0]);\r
+ at.transform(pt, pt);\r
+ pt2.setLocation(t2, BINARY[2]);\r
+ at.transform(pt2, pt2);\r
+ double x1 = pt.x;\r
+ double x2 = pt2.x;\r
+ double y1 = pt.y;\r
+ double y2 = pt2.y;\r
+ dev.extendRectangle(x1);\r
+ dev.addRectangle(x1, x2, y1, y2);\r
+ devNotAttached = false; \r
+ }\r
+ \r
+ // Already over => break\r
+ if(wentOver) break;\r
+ \r
+ // Out of range\r
+ if (t2>=end) {\r
+ wentOver = true;\r
+ }\r
+ } \r
+ \r
+ }\r
+ \r
+ public boolean readMinMaxFromEnd() {\r
+ if (disabled) return false;\r
+ HistoryManager history = getTrendNode().historian;\r
+\r
+ boolean hasVariable = !item.variableId.isEmpty() && !item.groupItemId.isEmpty() && !item.groupId.isEmpty();\r
+ boolean canReadMinMax = minmaxItem != null;\r
+ boolean manualScale = item.scale instanceof Scale.Manual;\r
+ \r
+ if ( !hasVariable ) {\r
+ min = 0;\r
+ max = 1;\r
+ from = Double.NaN;\r
+ end = Double.NaN;\r
+ return false;\r
+ }\r
+ \r
+ try {\r
+ if (canReadMinMax && !manualScale) { \r
+ String id = (String) minmaxItem.getFieldUnchecked("id");\r
+ StreamAccessor sa = history.openStream(id, "r");\r
+ if ( sa==null ) {\r
+ min = 0;\r
+ max = 1;\r
+ from = Double.NaN;\r
+ end = Double.NaN;\r
+ return false;\r
+ } else \r
+ try {\r
+ if (sa.size()==0) return false;\r
+ min = Double.MAX_VALUE;\r
+ max = -Double.MAX_VALUE;\r
+ from = Double.MAX_VALUE;\r
+ end = -Double.MAX_VALUE;\r
+ for (int i=0; i<sa.size(); i++) {\r
+ Binding binding = Bindings.getBinding( sa.type().componentType() );\r
+ Object sample = sa.get(i, binding);\r
+ ValueBand vb = new ValueBand(binding, sample);\r
+ if (!vb.isNullValue() && !vb.isNanSample()) {\r
+ min = Math.min(min, vb.getMinDouble());\r
+ max = Math.max(max, vb.getMaxDouble());\r
+ } \r
+ if (!vb.isNullValue()) {\r
+ from = Math.min(from, vb.getTimeDouble());\r
+ end = Math.max(end, vb.getEndTimeDouble());\r
+ }\r
+ }\r
+ if ( min==Double.MAX_VALUE || max==-Double.MAX_VALUE) {\r
+ min = 0; max = 1.0;\r
+ }\r
+ if ( from==Double.MAX_VALUE || end==-Double.MAX_VALUE) {\r
+ from = 0; end = 100.0;\r
+ }\r
+ return true;\r
+ } finally {\r
+ try { sa.close(); } catch (AccessorException e) {}\r
+ } \r
+ } else {\r
+ \r
+ if (manualScale) {\r
+ Scale.Manual ms = (Scale.Manual) item.scale;\r
+ min = ms.min;\r
+ max = ms.max;\r
+ } \r
+ \r
+ // Read from, end from any stream\r
+ if (openStreamAccessor==null) {\r
+ openStream(1);\r
+ } \r
+ if (openStreamAccessor!=null){\r
+ // Open some stream\r
+ StreamAccessor sa = openStreamAccessor;\r
+ sa.reset();\r
+ int count = sa.size();\r
+ if (count>0) {\r
+ Binding binding = Bindings.getBinding( sa.type().componentType() );\r
+ Object sample = sa.get(0, binding);\r
+ ValueBand vb = new ValueBand(binding, sample);\r
+ from = vb.getTimeDouble();\r
+ sa.get(count-1, binding, sample);\r
+ end = vb.hasEndTime() ? vb.getEndTimeDouble() : vb.getTimeDouble();\r
+ }\r
+ return true;\r
+ } else {\r
+ return false;\r
+ }\r
+ }\r
+ } catch (AccessorException e) {\r
+ log.log(Level.FINE, e.toString(), e);\r
+ return false;\r
+ } catch (HistoryException e) {\r
+ log.log(Level.FINE, e.toString(), e);\r
+ return false;\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void cleanup() {\r
+ trendNode = null;\r
+ if (openStreamAccessor != null) {\r
+ try {\r
+ openStreamAccessor.close();\r
+ } catch (AccessorException e) {\r
+ }\r
+ openStreamAccessor = null;\r
+ openStreamItem = null;\r
+ }\r
+ super.cleanup();\r
+ }\r
+ \r
+ Bean getFormat(double pixelsPerSecond) {\r
+ Bean result = null;\r
+ if (renderableItems == null)\r
+ return null;\r
+ \r
+ for (Bean format : renderableItems)\r
+ {\r
+ double interval = 0.0;\r
+ try {\r
+ interval = format.hasField("interval") ? (Double) format.getFieldUnchecked("interval") : 0.0;\r
+ } catch (RuntimeBindingException e) {\r
+ } catch (BindingException e) {\r
+ }\r
+ if (Double.isNaN( interval ) || interval<=pixelsPerSecond) {\r
+ result = format;\r
+ } else {\r
+ break;\r
+ }\r
+ } \r
+ if (result==null) {\r
+ if ( renderableItems.length == 0 ) {\r
+ result = null;\r
+ } else {\r
+ result = renderableItems[0];\r
+ }\r
+ }\r
+ \r
+ return result;\r
+ }\r
+ \r
+ public Stream openStream(double pixelsPerSecond) {\r
+ Bean f = getFormat(pixelsPerSecond);\r
+ if (f==openStreamItem) return openStream;\r
+ \r
+ if (openStream != null) {\r
+ openStream.close();\r
+ openStreamAccessor = null;\r
+ openStreamItem = null;\r
+ openStream = null;\r
+ }\r
+ \r
+ if (disabled) return null;\r
+ \r
+ if (f!=null) {\r
+ HistoryManager historian = getTrendNode().historian;\r
+ try {\r
+ String id = (String) f.getFieldUnchecked("id");\r
+ openStreamAccessor = historian.openStream(id, "r");\r
+ if ( openStreamAccessor!=null ) {\r
+ openStream = new Stream(openStreamAccessor);\r
+ openStreamItem = f; \r
+ } else {\r
+ openStream = null;\r
+ }\r
+ } catch (HistoryException e) {\r
+ log.log(Level.FINE, e.toString(), e);\r
+ }\r
+ }\r
+ \r
+ return openStream;\r
+ }\r
+ \r
+ @Override\r
+ public void render(Graphics2D g2d) {\r
+ }\r
+\r
+ @Override\r
+ public Rectangle2D getBoundsInLocal() {\r
+ return null;\r
+ }\r
+ \r
+ TrendNode getTrendNode() {\r
+ return (TrendNode) getParent();\r
+ }\r
+ \r
+}\r