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