]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.history/src/org/simantics/history/util/Stream.java
Added utility for truncating collected history data
[simantics/platform.git] / bundles / org.simantics.history / src / org / simantics / history / util / Stream.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2011 Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.history.util;
13
14 import org.simantics.databoard.Bindings;
15 import org.simantics.databoard.accessor.ArrayAccessor;
16 import org.simantics.databoard.accessor.CloseableAccessor;
17 import org.simantics.databoard.accessor.StreamAccessor;
18 import org.simantics.databoard.accessor.error.AccessorException;
19 import org.simantics.databoard.adapter.AdaptException;
20 import org.simantics.databoard.binding.Binding;
21 import org.simantics.databoard.binding.NumberBinding;
22 import org.simantics.databoard.binding.RecordBinding;
23 import org.simantics.databoard.binding.error.BindingException;
24 import org.simantics.databoard.type.Datatype;
25 import org.simantics.databoard.type.NumberType;
26 import org.simantics.databoard.type.RecordType;
27 import org.simantics.history.HistoryException;
28
29 /**
30  * This utility adds random access (time) to array accessor.
31  * 
32  * @author toni.kalajainen
33  */
34 public class Stream {
35         
36         public ArrayAccessor accessor;
37         public RecordType sampleType;
38         public RecordBinding sampleBinding;
39         
40         public int timeIndex;
41         public Datatype timeType;
42         public Binding timeBinding;
43         
44         public int endTimeIndex;
45         public Datatype endTimeType;
46         public Binding endTimeBinding;
47         
48         public int valueIndex;
49         public Datatype valueType;
50         public Binding valueBinding;
51
52         public int qualityIndex=-1;
53         public NumberType qualityType;
54         public NumberBinding qualityBinding;
55         
56         /**
57          * Construct stream 
58          * 
59          * @param accessor 
60          */
61         public Stream(ArrayAccessor accessor)
62         {
63                 this.accessor = accessor;
64                 this.sampleType = (RecordType) accessor.type().componentType();
65                 this.sampleBinding = (RecordBinding) Bindings.getBeanBinding( sampleType );
66                 
67                 this.valueIndex = sampleType.getComponentIndex2("value");
68                 if (valueIndex<0) throw new IllegalArgumentException("Array is not a sample array, value field is missing");
69                 this.valueType = sampleType.getComponentType(valueIndex);
70                 this.valueBinding = this.sampleBinding.getComponentBinding(valueIndex);         
71                 
72                 this.timeIndex = sampleType.getComponentIndex2("time");
73                 if (timeIndex<0) throw new IllegalArgumentException("Array is not a sample array, time field is missing");
74                 this.timeType = sampleType.getComponentType(timeIndex);
75                 this.timeBinding = this.sampleBinding.getComponentBinding(timeIndex);
76                 
77                 this.endTimeIndex = sampleType.getComponentIndex2("endTime");
78                 
79                 this.qualityIndex = sampleType.getComponentIndex2("quality");
80                 this.qualityType = qualityIndex>=0?(NumberType)sampleType.getComponentType(qualityIndex):null;
81                 this.qualityBinding = qualityType!=null?(NumberBinding)this.sampleBinding.getComponentBinding(qualityIndex):null;               
82         }
83
84         /**
85          * Construct stream 
86          * 
87          * @param accessor 
88          */
89         public Stream(ArrayAccessor accessor, RecordBinding recordBinding)
90         {
91                 this.accessor = accessor;
92                 this.sampleType = (RecordType) accessor.type().componentType();
93                 if (!this.sampleType.equals(recordBinding.type())) throw new IllegalArgumentException("Wrong binding. Got " + recordBinding.type() + ", expected " + this.sampleType);
94                 this.sampleBinding = recordBinding;
95
96                 this.valueIndex = sampleType.getComponentIndex2("value");
97                 this.timeIndex = sampleType.getComponentIndex2("time");
98                 this.endTimeIndex = sampleType.getComponentIndex2("endTime");
99
100                 if (valueIndex<0) throw new IllegalArgumentException("Array is not a sample array, value field is missing");
101                 if (timeIndex<0) throw new IllegalArgumentException("Array is not a sample array, time field is missing");
102                 //if (endTimeIndex<0) throw new IllegalArgumentException("Array is not a sample array, time field is missing");
103                 
104                 this.valueType = sampleType.getComponentType(valueIndex);
105                 this.timeType = sampleType.getComponentType(timeIndex);
106                 this.endTimeType = endTimeIndex>=0 ? sampleType.getComponentType(endTimeIndex) : null;
107                 
108                 this.valueBinding = this.sampleBinding.getComponentBinding("value");            
109                 this.timeBinding = this.sampleBinding.getComponentBinding("time");
110                 this.endTimeBinding = endTimeIndex>=0 ? this.sampleBinding.getComponentBinding("endTime") : null;
111         }
112
113         public void close() {
114                 if (accessor instanceof CloseableAccessor) {
115                         CloseableAccessor ca = (CloseableAccessor) accessor;
116                         try {
117                                 ca.close();
118                         } catch (AccessorException e) {
119                         }
120                 }
121         }
122         
123         public void reset() {
124                 if (accessor instanceof StreamAccessor) {
125                         StreamAccessor sa = (StreamAccessor) accessor;
126                         try {
127                                 sa.reset();
128                         } catch (AccessorException e) {
129                         }
130                 }
131         }
132         
133         /**
134          * Make a binary search to stream data 
135          * 
136          * @param array
137          * @param timeBinding
138          * @param time 
139      * @return index of the search key, if it is contained in the array
140      *         within the specified range;
141      *         otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>.  The
142      *         <i>insertion point</i> is defined as the point at which the
143      *         key would be inserted into the array: the index of the first
144      *         element in the range greater than the key,
145      *         or <tt>toIndex</tt> if all
146      *         elements in the range are less than the specified key.  Note
147      *         that this guarantees that the return value will be &gt;= 0 if
148      *         and only if the key is found.
149          * @throws HistoryException 
150          */
151         public int binarySearch(Binding timeBinding, Object time) throws HistoryException 
152         {               
153                 try {
154                         Object time_ = Bindings.adapt(time, timeBinding, this.timeBinding);
155                         
156                         int fromIndex = 0;
157                         int toIndex = accessor.size();
158                         
159                         int ix = binarySearch0(fromIndex, toIndex, time_);
160                         return ix;
161                 } catch (AccessorException e) {
162                         throw new HistoryException(e);
163                 } catch (BindingException e) {
164                         throw new HistoryException(e);
165                 } catch (AdaptException e) {
166                         throw new HistoryException(e);
167                 }
168         }
169         
170     // Like public version, but without range checks.
171     private int binarySearch0(int fromIndex, int toIndex, Object key) throws AccessorException, BindingException {
172                 int low = fromIndex;
173                 int high = toIndex - 1;
174                 Binding timeBinding = sampleBinding.getComponentBinding(timeIndex);
175         
176                 while (low <= high) {
177                     int mid = (low + high) >>> 1;
178                         Object midSam = accessor.get(mid, sampleBinding);
179                         Object midVal = sampleBinding.getComponent(midSam, timeIndex);
180                 int cmp = timeBinding.compare(midVal, key);
181                     if (cmp < 0)
182                         low = mid + 1;
183                     else if (cmp > 0)
184                         high = mid - 1;
185                     else
186                         return mid; // key found
187                 }
188                 return -(low + 1);  // key not found.
189     }
190         
191     public Object getLowerSample(Binding timeBinding, Object time) throws HistoryException
192     {
193                 try {
194                         int index = binarySearch(timeBinding, time);
195                         // Exact match
196                         if (index==0) return null;
197                         if (index>0) {
198                                 return accessor.get(index-1, sampleBinding);
199                         }
200                         index = -index-2;
201                         if (index<0) return null;
202                         if (index>=accessor.size()) return null;
203                         return accessor.get(index, sampleBinding);
204                 } catch (AccessorException e) {
205                         throw new HistoryException( e );
206                 }
207     }
208
209     
210         public Object getFloorSample(Binding timeBinding, Object time) throws HistoryException
211         {
212                 try {
213                         int index = binarySearch(timeBinding, time);
214                         // Exact match
215                         if (index>=0) {
216                                 return accessor.get(index, sampleBinding);
217                         }
218                         // The position where the sample would be inserted
219                         index = -index-2;
220                         if (index<0) return null;
221                         if (index>=accessor.size()) return null;
222                         return accessor.get(index, sampleBinding);
223                 } catch (AccessorException e) {
224                         throw new HistoryException( e );
225                 }
226         }
227     
228     public Object getSample(Binding timeBinding, Object time) throws HistoryException
229     {
230                 try {
231                 int pos = binarySearch(timeBinding, time);
232                 if (pos>=0) {
233                                 return accessor.get(pos, sampleBinding);
234                 }
235                 return null;
236                 } catch (AccessorException e) {
237                         throw new HistoryException(e);
238                 }
239     }
240     
241     public boolean getSample(Binding timeBinding, Object time, Object sample) throws HistoryException
242     {
243                 try {
244                 int pos = binarySearch(timeBinding, time);
245                 if (pos>=0) {
246                         accessor.get(pos, sampleBinding, sample);
247                                 return true;
248                 }
249                 return false;
250                 } catch (AccessorException e) {
251                         throw new HistoryException(e);
252                 }
253     }
254     
255     public Object getCeilingSample(Binding timeBinding, Object time) throws HistoryException
256     {
257                 try {
258                         int index = binarySearch(timeBinding, time);
259                         // Exact match
260                         if (index>=0) {
261                                 return accessor.get(index, sampleBinding);
262                         }
263                         // The position where the sample would be inserted
264                         index = -index-1;
265                         if (index<0) return null;
266                         if (index>=accessor.size()) return null;
267                         return accessor.get(index, sampleBinding);
268                 } catch (AccessorException e) {
269                         throw new HistoryException( e );
270                 }
271     }
272     
273     public Object getHigherSample(Binding timeBinding, Object time) throws HistoryException
274     {
275                 try {
276                         int index = binarySearch(timeBinding, time);
277                         // Exact match
278                         int count = accessor.size();
279                         if (index>=0) {
280                                 index++; // exact match, -> next
281                         } else {
282                                 // The position where the sample would be inserted
283                                 index = -index-1;
284                         }
285                         if (index<0 || index>=count) return null;
286                         return accessor.get(index, sampleBinding);
287                 } catch (AccessorException e) {
288                         throw new HistoryException( e );
289                 }
290     }
291
292     private Object _getTime(Object sample, Binding timeBinding) throws HistoryException
293     {
294         try {
295                 Object time__ = sampleBinding.getComponent( sample, timeIndex );
296                         return Bindings.adapt(time__, this.timeBinding, timeBinding);
297                 } catch (AdaptException e) {
298                         throw new HistoryException(e);
299                 } catch (BindingException e) {
300                         throw new HistoryException(e);
301                 }
302     }
303
304         public Object getItemTime(Binding timeBinding, int index) throws HistoryException
305         {
306                 try {
307                         return _getTime( accessor.get(index, sampleBinding), timeBinding );
308                 } catch (AccessorException e) {
309                         throw new HistoryException( e );
310                 }
311         }
312
313     public Object getLowerTime(Binding timeBinding, Object time) throws HistoryException
314     {
315                 try {
316                         int index = binarySearch(timeBinding, time);
317                         // Exact match
318                         if (index==0) return null;
319                         if (index>0) {
320                                 return time;
321                         }
322                         index = -index-2;
323                         if (index<0) return null;
324                         if (index>=accessor.size()) return null;
325                         return _getTime(accessor.get(index, sampleBinding), timeBinding);
326                 } catch (AccessorException e) {
327                         throw new HistoryException( e );
328                 }
329     }
330
331     
332         public Object getFloorTime(Binding timeBinding, Object time) throws HistoryException
333         {
334                 try {
335                         int index = binarySearch(timeBinding, time);
336                         // Exact match
337                         if (index>=0) {
338                                 return time;
339                         }
340                         // The position where the sample would be inserted
341                         index = -index-2;
342                         if (index<0) return null;
343                         if (index>=accessor.size()) return null;
344                         return _getTime( accessor.get(index, sampleBinding), timeBinding );
345                 } catch (AccessorException e) {
346                         throw new HistoryException( e );
347                 }
348         }
349     
350     public Object getCeilingTime(Binding timeBinding, Object time) throws HistoryException
351     {
352                 try {
353                         int index = binarySearch(timeBinding, time);
354                         // Exact match
355                         if (index>=0) {
356                                 return time;
357                         }
358                         // The position where the sample would be inserted
359                         index = -index-1;
360                         if (index<0) return null;
361                         if (index>=accessor.size()) return null;
362                         return _getTime( accessor.get(index, sampleBinding), timeBinding );
363                 } catch (AccessorException e) {
364                         throw new HistoryException( e );
365                 }
366     }
367     
368     public Object getHigherTime(Binding timeBinding, Object time) throws HistoryException
369     {
370                 try {
371                         int index = binarySearch(timeBinding, time);
372                         
373                         // Exact match
374                         int count = accessor.size();
375                         if (index>=0) {
376                                 index++; // exact match, -> next
377                         } else {
378                                 // The position where the sample would be inserted
379                                 index = -index-1;
380                         }
381                         if (index<0 || index>=count) return null;
382                         return _getTime( accessor.get(index, sampleBinding), timeBinding );
383                 } catch (AccessorException e) {
384                         throw new HistoryException( e );
385                 }
386     }
387     
388     
389     public Object getValue(Binding timeBinding, Object time, Binding valueBinding) throws HistoryException
390     {
391                 try {
392                 int pos = binarySearch(timeBinding, time);
393                 if (pos>=0) {
394                                 Object sample = accessor.get(pos, sampleBinding);
395                                 Object value = sampleBinding.getComponent(sample, valueIndex);
396                                 
397                                 if (valueBinding != this.valueBinding) {
398                                         value = Bindings.adapt(value, this.valueBinding, valueBinding);
399                                 } 
400                                 return value;
401                 }
402                 return null;
403                 } catch (AccessorException e) {
404                         throw new HistoryException(e);
405                 } catch (BindingException e) {
406                         throw new HistoryException(e);
407                 } catch (AdaptException e) {
408                         throw new HistoryException(e);
409                 }
410     }
411         
412     public Object getValue(Binding timeBinding, Object time) throws HistoryException
413     {
414                 try {
415                 int pos = binarySearch(timeBinding, time);
416                 if (pos>=0) {
417                                 Object sample = accessor.get(pos, sampleBinding);
418                                 Object value = sampleBinding.getComponent(sample, valueIndex);
419                                 return value;
420                 }
421                 return null;
422                 } catch (AccessorException e) {
423                         throw new HistoryException(e);
424                 } catch (BindingException e) {
425                         throw new HistoryException(e);
426                 }
427     }
428     
429     public Object getQuality(Binding timeBinding, Object time) throws HistoryException
430     {
431                 try {
432                 int pos = binarySearch(timeBinding, time);
433                 if (pos>=0) {
434                                 Object sample = accessor.get(pos, sampleBinding);
435                                 Object value = sampleBinding.getComponent(sample, qualityIndex);
436                                 return value;
437                 }
438                 return null;
439                 } catch (AccessorException e) {
440                         throw new HistoryException(e);
441                 } catch (BindingException e) {
442                         throw new HistoryException(e);
443                 }
444     }
445     
446     /**
447      * Get value if exists, otherwise null
448      * @param timeBinding
449      * @param time
450      * @return value or null
451      * @throws HistoryException
452      */
453     public Object getPossibleValue(Binding timeBinding, Object time) throws HistoryException
454     {
455                 try {
456                 int pos = binarySearch(timeBinding, time);
457                 if (pos>=0) {
458                                 Object sample = accessor.get(pos, sampleBinding);
459                                 if (qualityBinding != null) {                                   
460                                         Object quality = sampleBinding.getComponent(sample, qualityIndex);                                      
461                                         if ( !qualityBinding.getValue(quality).equals( ValueBand.QUALITY_GOOD ) ) return null;                                  
462                                 }
463                                 Object value = sampleBinding.getComponent(sample, valueIndex);
464                                 return value;
465                 }
466                 return null;
467                 } catch (AccessorException e) {
468                         throw new HistoryException(e);
469                 } catch (BindingException e) {
470                         throw new HistoryException(e);
471                 }
472     }
473     
474     public int count() throws HistoryException {
475         try {
476                         return accessor.size();
477                 } catch (AccessorException e) {
478                         throw new HistoryException(e);
479                 }
480     }
481         
482     public Object getFirstTime(Binding binding) throws HistoryException {
483         try {
484                 if (accessor.size()==0) {
485                         return null;
486                 }
487                 Object sample = accessor.get(0, sampleBinding);
488                 Object time = sampleBinding.getComponent(sample, timeIndex);
489                 if (timeBinding!=binding) time = Bindings.adapt(time, timeBinding, binding);
490                 return time;
491         } catch (BindingException e) {
492                 throw new HistoryException(e);
493         } catch (AccessorException e) {
494                 throw new HistoryException(e);
495                 } catch (AdaptException e) {
496                 throw new HistoryException(e);
497                 }
498     }
499     
500     public Object getEndTime(Binding binding) throws HistoryException {
501         try {
502                 if (accessor.size()==0) {
503                         return null;
504                 }
505                 Object sample = accessor.get(0, sampleBinding);
506                 
507                 if (endTimeIndex>=0) {
508                         Object endtime = sampleBinding.getComponent(sample, endTimeIndex);
509                         if (endTimeBinding!=binding) endtime = Bindings.adapt(endtime, endTimeBinding, binding);
510                         return endtime;
511                 } else {
512                         Object time = sampleBinding.getComponent(sample, timeIndex);
513                         if (timeBinding!=binding) time = Bindings.adapt(time, timeBinding, binding);
514                         return time;                    
515                 }
516         } catch (BindingException e) {
517                 throw new HistoryException(e);
518         } catch (AccessorException e) {
519                 throw new HistoryException(e);
520                 } catch (AdaptException e) {
521                 throw new HistoryException(e);
522                 }
523     }
524
525         public boolean isEmpty() throws HistoryException {
526                 try {
527                         return accessor.size() == 0;
528                 } catch (AccessorException e) {
529                 throw new HistoryException(e);
530                 }
531         }
532     
533 }