/******************************************************************************* * 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.history.util.subscription; import java.util.Arrays; import java.util.Comparator; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.type.ArrayType; import org.simantics.databoard.type.Component; 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.databoard.util.Limit; import org.simantics.databoard.util.Range; /** * This is an utility class for SubscriptionItem. * * The Simantics Charts samples history data in a specified way. * One data source item is recorded into multiple history item, each identified * with a named SamplingFormat. This class describes a named sampling format. * * @author toni.kalajainen@semantum.fi */ public class SamplingFormat extends Bean { public static final SamplingFormat[] EMPTY = new SamplingFormat[0]; public final static Comparator INTERVAL_COMPARATOR = new Comparator() { @Override public int compare(Bean o1, Bean o2) { double i1 = (Double) o1.getFieldUnchecked("interval"); double i2 = (Double) o2.getFieldUnchecked("interval"); boolean nan1 = Double.isNaN( i1 ); boolean nan2 = Double.isNaN( i2 ); if ( nan1 && nan2 ) return 0; if ( nan1 && !nan2) return -1; if ( !nan1 && nan2) return 1; return i1 == i2 ? 0 : ( i1 < i2 ? -1 : 1 ); } }; public final static Comparator DEADBAND_COMPARATOR = new Comparator() { @Override public int compare(SamplingFormat o1, SamplingFormat o2) { boolean nan1 = Double.isNaN( o1.deadband ); boolean nan2 = Double.isNaN( o2.deadband ); if ( nan1 && nan2 ) return 0; if ( nan1 && !nan2) return -1; if ( !nan1 && nan2) return 1; return o1.deadband == o2.deadband ? 0 : ( o1.deadband < o2.deadband ? -1 : 1 ); } }; /** Identifier, this value is used for separating the time series files */ public String formatId; /** * Describes the format of the packed sample. The sample must be a record. * The record must have any combination of the following named fields. * The field types must one of: byte, integer, long, float, double. * * time, endTime, value - are mandatory fields. * * time - Region start time, the time of the 1st included sample * endTime - Region end time, the time of the last included sample * * value - First value in the region * lastValue - Last value in the region * avg - Average value of all included samples * median - Median value of all samples in the region * min - Lowest value in the region * max - Highest value in the region * * quality - 0 = Good, -1 = No value * count - The number of included samples in the region */ public Datatype format; /** Interval sets the minimum time for a packed sample */ public double interval = Double.NaN; /** Deadband determines the minimum value change for a packed sample when collecting data */ public double deadband = Double.NaN; public SamplingFormat() { } public SamplingFormat(String id, RecordType sampleType, double interval, double deadband) { this.formatId = id; this.format = sampleType; this.interval = interval; this.deadband = deadband; } // Sampling format templates public static SamplingFormat simple, allfields, vector; public RecordType record() { return (RecordType) format; } static { simple = new SamplingFormat(); simple.formatId = "Simple"; RecordType format = (RecordType) (simple.format = new RecordType()); format.addComponent("time", Datatypes.DOUBLE); format.addComponent("endTime", Datatypes.DOUBLE); format.addComponent("value", Datatypes.DOUBLE); format.addComponent("quality", Datatypes.BYTE); simple.interval = Double.NaN; simple.deadband = Double.NaN; allfields = new SamplingFormat(); allfields.formatId = "Allfields"; allfields.format = new RecordType(); format = (RecordType) (allfields.format = new RecordType()); format.addComponent("time", Datatypes.DOUBLE); format.addComponent("endTime", Datatypes.DOUBLE); format.addComponent("value", Datatypes.DOUBLE); format.addComponent("lastValue", Datatypes.DOUBLE); format.addComponent("min", Datatypes.DOUBLE); format.addComponent("max", Datatypes.DOUBLE); format.addComponent("avg", Datatypes.DOUBLE); format.addComponent("median", Datatypes.DOUBLE); format.addComponent("quality", Datatypes.BYTE); format.addComponent("count", Datatypes.INTEGER); allfields.interval = Double.NaN; allfields.deadband = Double.NaN; vector = new SamplingFormat(); vector.formatId = "Vector"; vector.format = new RecordType(); format = (RecordType) (vector.format = new RecordType()); format.addComponent("time", Datatypes.FLOAT); format.addComponent("endTime", Datatypes.FLOAT); format.addComponent("value", new ArrayType( Datatypes.DOUBLE, Range.between(Limit.inclusive(3), Limit.inclusive(3)) )); format.addComponent("count", Datatypes.INTEGER); vector.interval = Double.NaN; vector.deadband = Double.NaN; } @Override public boolean equals(Object obj) { if ( obj == null ) return false; if ( obj == this ) return true; if ( obj instanceof SamplingFormat == false ) return false; SamplingFormat other = (SamplingFormat) obj; // if ( !doubleEquals(interval, other.interval) || !doubleEquals(deadband, other.deadband) ) return false; // return RecordTypeBinding.equals( other.sampleType, sampleType ); return formatId.equals(other.formatId); } @Override public String toString() { return "id="+formatId+", "+format +", interval="+interval+", deadband="+deadband; } @Override public SamplingFormat clone() { try { SamplingFormat result = new SamplingFormat(); result.formatId = formatId; result.interval = interval; result.deadband = deadband; result.format = (Datatype) Bindings.getBindingUnchecked( Datatype.class ).clone( format ); return result; } catch (AdaptException e) { throw new RuntimeException( e ); } } public SamplingFormat clone(double interval, double deadband) { try { SamplingFormat result = new SamplingFormat(); result.formatId = formatId; result.interval = interval; result.deadband = deadband; result.format = (Datatype) Bindings.getBindingUnchecked( Datatype.class ).clone( format ); return result; } catch (AdaptException e) { throw new RuntimeException( e ); } } public SamplingFormat cloneTo(String id, double interval, double deadband) { try { SamplingFormat result = new SamplingFormat(); result.formatId = id; result.interval = interval; result.deadband = deadband; result.format = (Datatype) Bindings.getBindingUnchecked( Datatype.class ).clone( format ); return result; } catch (AdaptException e) { throw new RuntimeException( e ); } } public SamplingFormat setUnit(String unit) { for ( int i = 0; i