]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.history/src/org/simantics/history/HistorySampler.java
HistorySampler.sample(HistorySamplerItem2, ...) now uses mipmapped data
[simantics/platform.git] / bundles / org.simantics.history / src / org / simantics / history / HistorySampler.java
1 package org.simantics.history;
2
3 import java.io.IOException;
4 import java.math.BigDecimal;
5
6 import org.simantics.history.csv.ExportInterpolation;
7 import org.simantics.history.util.HistoryExportUtil;
8 import org.simantics.history.util.StreamIterator;
9 import org.simantics.history.util.ValueBand;
10
11 import gnu.trove.list.array.TDoubleArrayList;
12
13 public class HistorySampler {
14     
15     public synchronized static TDoubleArrayList sample( HistorySamplerItem item, double from, double end, double timeWindow, double timeStep, boolean resample ) throws HistoryException, IOException {
16
17         try {
18                 // If there is something pending at this point, flush before opening for read
19                 if(item.collector != null)
20                         item.collector.flush();
21                 item.open();
22                 return sample(item.iter, from, end, timeWindow, timeStep, resample);
23         } finally {
24                 item.close();
25         }
26     }
27
28     public static TDoubleArrayList sample( StreamIterator iter, double from, double end, double timeWindow, double timeStep, boolean resample ) throws HistoryException, IOException {
29         return sample(iter, from, end, timeWindow, timeStep, resample, 0.0);
30     }
31
32     public static TDoubleArrayList sample( StreamIterator iter, double from, double end, double timeWindow, double timeStep, boolean resample, Double sampleFrom ) throws HistoryException, IOException {
33
34         ExportInterpolation numberInterpolation = ExportInterpolation.LINEAR_INTERPOLATION;
35
36         double startTime = from;
37         if(sampleFrom != null) {
38                  // This option can be used do define the offset of sampling. Samples will be sampleFrom + n * timeStep
39                 startTime = sampleFrom;
40         }
41
42         TDoubleArrayList result = new TDoubleArrayList();
43
44         if(iter.isEmpty()) return result;
45
46         double allFrom = iter.getFirstTime();
47         double allEnd = 10e10;//iter.getLastTime();
48         
49         if(from > (allEnd + timeStep)) {
50                 from = allEnd-timeWindow;
51                 end = allEnd;
52         }
53         
54 //      System.err.println("sample " + from + " " + end);
55 //      if(from < 0)
56 //              System.err.println("fgag");
57
58         // Prepare time         
59         boolean hasAnyValues = allFrom != Double.MAX_VALUE && allEnd != -Double.MAX_VALUE;
60
61         // Make intersection of actual data range (allFrom, allEnd) and requested data (from, end)
62         double _from = Double.MAX_VALUE, _end = -Double.MAX_VALUE;              
63         if (hasAnyValues) {
64                 _from = Math.max(allFrom, from);
65                 _end = Math.min(allEnd, end);
66         }
67
68         if (!hasAnyValues) {
69 //              System.err.println("=> no values");
70                 return result;
71         }
72
73         // Iterate until endTime is met for all variables
74         double time = _from;
75
76         if(!resample) {
77
78                 // If resample is false then all samples are reported as is. The code achieves this by setting startTime to _from and timeStep to 0.0 
79                 time = _from;
80                 timeStep = 0.0;
81
82         } else {
83
84                 // time = startTime + n*timeStep 
85
86                 // Sampling based on given startTime and timeStep
87                 if(timeStep > 0) {
88
89                         // Find the first sample time that contains data 
90                         double n = Math.max(0, Math.ceil((_from-startTime) / timeStep));
91                         time = startTime + n*timeStep;
92
93                 } else {
94
95                         // Start sampling from startTime but make sure that it is not less than _from
96                         if(startTime > _from) time = startTime;
97
98                 }
99
100
101         }
102
103         // Must convert double times to String when initializing BigDecimal.
104         // Otherwise BigDecimal will pick up inaccuracies from beyond 15 precise digits
105         // thus making a mess of the time step calculations.
106
107         BigDecimal bigTime = new BigDecimal(String.valueOf(time));
108         BigDecimal bigTimeStep = new BigDecimal(String.valueOf(timeStep));
109
110         //System.err.println("=> goto " + time);
111         
112         if(!iter.gotoTime(time)) {
113                 //System.err.println("=> no sample found at " + time);
114                 return result;
115         }
116         
117         //time = iter.getValueBand().getTimeDouble();
118
119         boolean ignore = Math.abs(time-from) < 1e-6; 
120
121         do {
122                 
123                 //System.err.println("process " + time + " " + iter.getValueBand());
124
125
126
127             // Check for valid value
128                 if ( iter.hasValidValue() ) {
129                     
130                  // Write time
131                 if(!ignore) {
132 //                    System.err.println("Add time : " + time);
133                     result.add(time);
134                 }
135                 // Write value
136                         Object value = iter.getValueBand().getValue();
137                         //System.err.print("Add value : " + value);
138                         if (value instanceof Number) {
139                                 if (value instanceof Float || value instanceof Double) {
140                                         switch (numberInterpolation) {
141                                         case PREVIOUS_SAMPLE:
142
143                                         if(!ignore) {
144 //                                          System.err.println(" previous .. done!");
145                                                 result.add(((Number) value).doubleValue());
146                                         }
147                                         
148                                                 break;
149
150                                         case LINEAR_INTERPOLATION:
151                                                 if (time != iter.getValueBand().getTimeDouble() && iter.hasNext()) {
152
153                                                         // Interpolate
154                                                         int currentIndex = iter.getIndex();
155                                                         ValueBand band = iter.getValueBand();
156                                                         //double t1 = band.getTimeDouble();
157                                                         Number v1 = (Number) value;
158                                                         double t12 = band.getEndTimeDouble();
159                                                         iter.next();
160                                                         double t2 = iter.getValueBand().getTimeDouble();
161                                                         Number v2 = (Number) iter.getValueBand().getValue();
162                                                         iter.gotoIndex(currentIndex);
163
164                                                         double vs = v1.doubleValue();
165                                                         if(time > t12)
166                                                                 vs = HistoryExportUtil.biglerp(t12, v1.doubleValue(), t2, v2.doubleValue(), time);
167
168                                                 if(!ignore) {
169 //                                                  System.err.println(" linear .. done!");
170                                                         result.add(vs);
171                                                 }
172
173                                                 } else {
174                                                         // Exact timestamp match, or last sample.
175                                                         // Don't interpolate nor extrapolate.
176                                                 if(!ignore) {
177 //                                                  System.err.println(" else .. done!");
178                                                         result.add(((Number) value).doubleValue());
179                                                 }
180                                                 }
181                                                 break;
182                                         default:
183                                                 throw new UnsupportedOperationException("Unsupported interpolation: " + numberInterpolation);
184                                         }
185                                 } else {
186                                         throw new IllegalStateException("Value is not a number " + value);
187                                 }
188                         } else if (value instanceof Boolean) {
189                         if(!ignore)
190                                 result.add( (Boolean)value ? 1.0: 0.0);
191                         } else {
192                                 throw new IllegalStateException("Value is not a number " + value);
193                         }
194                 }
195                 
196                 ignore = false;
197
198                 // Read next values, and the following times
199                 if ( timeStep>0.0 ) {
200                         bigTime = bigTime.add(bigTimeStep);
201                         time = bigTime.doubleValue();
202                 } else {
203                         // Get smallest end time that is larger than current time
204                         Double nextTime = null;
205                         //                      System.out.println("time = "+time);
206                         if(!iter.hasNext()) break;
207                         Double itemNextTime = iter.getNextTime( time );
208                         //                              System.err.println("  "+i.label+" nextTime="+itemNextTime);
209                         if ( nextTime == null || ( nextTime > itemNextTime && !itemNextTime.equals( time ) ) ) nextTime = itemNextTime; 
210                         if ( nextTime == null || nextTime.equals( time ) ) break;
211                         time = nextTime;
212                 }
213
214                 boolean hasMore = false;
215
216                 iter.proceedToTime(time);
217                 if(HistoryExportUtil.contains(iter, time)) hasMore = true;
218
219                 if(!hasMore) break;
220
221         } while (time<=_end);
222
223                 //System.err.println("=> " + Arrays.toString(result.toArray()));
224         
225         return result;
226
227     }
228
229     // ------------------------------------------------------------------------
230     // New history sampling routine, supports mip-mapped subscriptions
231
232         public synchronized static TDoubleArrayList sample(HistorySamplerItem2 item, double end, double timeWindow, int maxSamples, boolean resample) throws HistoryException, IOException {
233                 try {
234                         // Avoid div / 0
235                         if (maxSamples <= 0)
236                                 return new TDoubleArrayList(0);
237
238                         // If there is something pending at this point, flush before opening for read
239                         if (item.collector != null)
240                                 item.collector.flush();
241
242                         // Open data source with most suitable sampling interval
243                         double secondsPerPixel = timeWindow / (double) maxSamples;
244                         //System.out.println("SECONDS / PIXEL: " + secondsPerPixel);
245                         item.open(secondsPerPixel);
246
247                         return sample(item.iter, end, timeWindow, maxSamples, resample);
248                 } finally {
249                         item.close();
250                 }
251         }
252
253         private static TDoubleArrayList sample(StreamIterator iter, double endTime, double timeWindow, int maxSamples, boolean resample) throws HistoryException, IOException {
254                 double fromTime = endTime - timeWindow;
255                 //System.out.println("sample: [" + fromTime + " .. " + endTime + "] window = " + timeWindow + " s, max samples = " + maxSamples + ", resample = " + resample);
256                 ExportInterpolation interpolation = ExportInterpolation.LINEAR_INTERPOLATION;
257
258                 if (iter.isEmpty() || (resample && maxSamples <= 0))
259                         return new TDoubleArrayList(0);
260
261                 double dataFrom = iter.getFirstTime();
262                 double dataEnd = iter.getLastTime();
263
264                 // Prepare time
265                 boolean hasAnyValues = dataFrom != Double.MAX_VALUE && dataEnd != -Double.MAX_VALUE;
266                 if (!hasAnyValues) {
267                         //System.out.println("=> no values");
268                         return new TDoubleArrayList(0);
269                 }
270
271                 // Make intersection of actual data range (allFrom, allEnd) and requested data (from, end)
272                 double from = Math.max(fromTime, dataFrom);
273                 double end = Math.min(endTime, dataEnd);
274
275                 //System.out.println("data available [" + dataFrom + " .. " + dataEnd + "]");
276                 //System.out.println("will sample between [" + from + " .. " + end + "]");
277
278                 // Iterate until endTime is met
279                 double time = from;
280                 double timeStep = 0;
281
282                 if (resample) {
283                         timeStep = timeWindow / maxSamples;
284                 } else {
285                         // If resample is false then all samples are reported as is.
286                         // The code achieves this by setting startTime to from and timeStep to 0.0. 
287                 }
288
289                 // Must convert double times to String when initializing BigDecimal.
290                 // Otherwise BigDecimal will pick up inaccuracies from beyond 15 precise digits
291                 // thus making a mess of the time step calculations.
292                 BigDecimal bigTime = new BigDecimal(String.valueOf(time));
293                 BigDecimal bigTimeStep = new BigDecimal(String.valueOf(timeStep));
294
295                 //System.out.println("=> goto " + time);
296
297                 TDoubleArrayList result = new TDoubleArrayList();
298                 if (!iter.gotoTime(time)) {
299                         //System.out.println("=> no sample found at " + time);
300                         return result;
301                 }
302
303                 //time = iter.getValueBand().getTimeDouble();
304
305                 //System.out.println("=> ignore first item?: " + time + " - " + from + " = " + (time-from) + " => " + (Math.abs(time-from) < 1e-6));
306                 //boolean ignore = Math.abs(time-from) < 1e-6;
307                 boolean ignore = false;
308
309                 do {
310                         //System.out.println("process " + time + " " + iter.getValueBand() + " (ignore = " + ignore + ")");
311
312                         // Check for valid value
313                         if ( iter.hasValidValue() ) {
314
315                                 // Write time
316                                 if (!ignore) {
317                                         //System.out.println("Add time : " + time);
318                                         result.add(time);
319                                 }
320                                 // Write value
321                                 Object value = iter.getValueBand().getValue();
322                                 //System.out.print("Add value : " + value);
323                                 if (value instanceof Number) {
324                                         if (value instanceof Float || value instanceof Double) {
325                                                 switch (interpolation) {
326                                                 case PREVIOUS_SAMPLE:
327                                                         if (!ignore) {
328                                                                 //System.out.println(" previous .. done!");
329                                                                 result.add(((Number) value).doubleValue());
330                                                         }
331                                                         break;
332
333                                                 case LINEAR_INTERPOLATION:
334                                                         if (time != iter.getValueBand().getTimeDouble() && iter.hasNext()) {
335                                                                 // Interpolate
336                                                                 int currentIndex = iter.getIndex();
337                                                                 ValueBand band = iter.getValueBand();
338                                                                 //double t1 = band.getTimeDouble();
339                                                                 Number v1 = (Number) value;
340                                                                 double t12 = band.getEndTimeDouble();
341                                                                 iter.next();
342                                                                 double t2 = iter.getValueBand().getTimeDouble();
343                                                                 Number v2 = (Number) iter.getValueBand().getValue();
344                                                                 iter.gotoIndex(currentIndex);
345
346                                                                 double vs = v1.doubleValue();
347                                                                 if (time > t12)
348                                                                         vs = HistoryExportUtil.biglerp(t12, v1.doubleValue(), t2, v2.doubleValue(), time);
349
350                                                                 if (!ignore) {
351                                                                         //System.out.println(" linear .. done!");
352                                                                         result.add(vs);
353                                                                 }
354
355                                                         } else {
356                                                                 // Exact timestamp match, or last sample.
357                                                                 // Don't interpolate nor extrapolate.
358                                                                 if (!ignore) {
359                                                                         //System.out.println(" else .. done!");
360                                                                         result.add(((Number) value).doubleValue());
361                                                                 }
362                                                         }
363                                                         break;
364
365                                                 default:
366                                                         throw new UnsupportedOperationException("Unsupported interpolation: " + interpolation);
367                                                 }
368                                         } else {
369                                                 throw new IllegalStateException("Value is not a number " + value);
370                                         }
371                                 } else if (value instanceof Boolean) {
372                                         if(!ignore)
373                                                 result.add( (Boolean)value ? 1.0: 0.0);
374                                 } else {
375                                         throw new IllegalStateException("Value is not a number " + value);
376                                 }
377                         }
378
379                         ignore = false;
380
381                         // Read next values, and the following times
382                         if ( timeStep>0.0 ) {
383                                 bigTime = bigTime.add(bigTimeStep);
384                                 time = bigTime.doubleValue();
385                         } else {
386                                 // Get smallest end time that is larger than current time
387                                 Double nextTime = null;
388                                 //System.out.println(" time = "+time);
389                                 if(!iter.hasNext()) {
390                                         duplicateLastDataPoint(result, end);
391                                         break;
392                                 }
393                                 Double itemNextTime = iter.getNextTime( time );
394                                 //System.out.println("  "+iter.toString()+" nextTime="+itemNextTime);
395                                 if ( nextTime == null || ( nextTime > itemNextTime && !itemNextTime.equals( time ) ) ) nextTime = itemNextTime; 
396                                 if ( nextTime == null || nextTime.equals( time ) ) break;
397                                 time = nextTime;
398                         }
399
400                         boolean hasMore = false;
401
402                         iter.proceedToTime(time);
403                         if (HistoryExportUtil.contains(iter, time)) hasMore = true;
404
405                         if (!hasMore) {
406                                 if (time <= end) {
407                                         duplicateLastDataPoint(result, end);
408                                 }
409                                 break;
410                         }
411
412                 } while (time <= end);
413
414                 //System.out.println("=> [" + result.size() + "]" + Arrays.toString(result.toArray()));
415                 //System.out.println("=> [" + result.size() + "]");
416                 return result;
417         }
418
419         private static void duplicateLastDataPoint(TDoubleArrayList data, double timestamp) {
420                 double lastValue = data.get(data.size() - 1);
421                 //System.out.println("Duplicating last sample value " + lastValue + " @ " + timestamp);
422                 data.add(timestamp);
423                 data.add(lastValue);
424         }
425
426 }