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