1 package org.simantics.history;
3 import java.io.IOException;
4 import java.math.BigDecimal;
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;
11 import gnu.trove.list.array.TDoubleArrayList;
13 public class HistorySampler {
15 public synchronized static TDoubleArrayList sample( HistorySamplerItem item, double from, double end, double timeWindow, double timeStep, boolean resample ) throws HistoryException, IOException {
18 // If there is something pending at this point, flush before opening for read
19 if(item.collector != null)
20 item.collector.flush();
22 return sample(item.iter, from, end, timeWindow, timeStep, resample);
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);
32 public static TDoubleArrayList sample( StreamIterator iter, double from, double end, double timeWindow, double timeStep, boolean resample, Double sampleFrom ) throws HistoryException, IOException {
34 ExportInterpolation numberInterpolation = ExportInterpolation.LINEAR_INTERPOLATION;
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;
42 TDoubleArrayList result = new TDoubleArrayList();
44 if(iter.isEmpty()) return result;
46 double allFrom = iter.getFirstTime();
47 double allEnd = 10e10;//iter.getLastTime();
49 if(from > (allEnd + timeStep)) {
50 from = allEnd-timeWindow;
54 // System.err.println("sample " + from + " " + end);
56 // System.err.println("fgag");
59 boolean hasAnyValues = allFrom != Double.MAX_VALUE && allEnd != -Double.MAX_VALUE;
61 // Make intersection of actual data range (allFrom, allEnd) and requested data (from, end)
62 double _from = Double.MAX_VALUE, _end = -Double.MAX_VALUE;
64 _from = Math.max(allFrom, from);
65 _end = Math.min(allEnd, end);
69 // System.err.println("=> no values");
73 // Iterate until endTime is met for all variables
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
84 // time = startTime + n*timeStep
86 // Sampling based on given startTime and timeStep
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;
95 // Start sampling from startTime but make sure that it is not less than _from
96 if(startTime > _from) time = startTime;
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.
107 BigDecimal bigTime = new BigDecimal(String.valueOf(time));
108 BigDecimal bigTimeStep = new BigDecimal(String.valueOf(timeStep));
110 //System.err.println("=> goto " + time);
112 if(!iter.gotoTime(time)) {
113 //System.err.println("=> no sample found at " + time);
117 //time = iter.getValueBand().getTimeDouble();
119 boolean ignore = Math.abs(time-from) < 1e-6;
123 //System.err.println("process " + time + " " + iter.getValueBand());
127 // Check for valid value
128 if ( iter.hasValidValue() ) {
132 // System.err.println("Add time : " + time);
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:
144 // System.err.println(" previous .. done!");
145 result.add(((Number) value).doubleValue());
150 case LINEAR_INTERPOLATION:
151 if (time != iter.getValueBand().getTimeDouble() && iter.hasNext()) {
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();
160 double t2 = iter.getValueBand().getTimeDouble();
161 Number v2 = (Number) iter.getValueBand().getValue();
162 iter.gotoIndex(currentIndex);
164 double vs = v1.doubleValue();
166 vs = HistoryExportUtil.biglerp(t12, v1.doubleValue(), t2, v2.doubleValue(), time);
169 // System.err.println(" linear .. done!");
174 // Exact timestamp match, or last sample.
175 // Don't interpolate nor extrapolate.
177 // System.err.println(" else .. done!");
178 result.add(((Number) value).doubleValue());
183 throw new UnsupportedOperationException("Unsupported interpolation: " + numberInterpolation);
186 throw new IllegalStateException("Value is not a number " + value);
188 } else if (value instanceof Boolean) {
190 result.add( (Boolean)value ? 1.0: 0.0);
192 throw new IllegalStateException("Value is not a number " + value);
198 // Read next values, and the following times
199 if ( timeStep>0.0 ) {
200 bigTime = bigTime.add(bigTimeStep);
201 time = bigTime.doubleValue();
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;
214 boolean hasMore = false;
216 iter.proceedToTime(time);
217 if(HistoryExportUtil.contains(iter, time)) hasMore = true;
221 } while (time<=_end);
223 //System.err.println("=> " + Arrays.toString(result.toArray()));
229 // ------------------------------------------------------------------------
230 // New history sampling routine, supports mip-mapped subscriptions
232 public synchronized static TDoubleArrayList sample(HistorySamplerItem2 item, double end, double timeWindow, int maxSamples, boolean resample) throws HistoryException, IOException {
236 return new TDoubleArrayList(0);
238 // If there is something pending at this point, flush before opening for read
239 if (item.collector != null)
240 item.collector.flush();
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);
247 return sample(item.iter, end, timeWindow, maxSamples, resample);
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;
258 if (iter.isEmpty() || (resample && maxSamples <= 0))
259 return new TDoubleArrayList(0);
261 double dataFrom = iter.getFirstTime();
262 double dataEnd = iter.getLastTime();
265 boolean hasAnyValues = dataFrom != Double.MAX_VALUE && dataEnd != -Double.MAX_VALUE;
267 //System.out.println("=> no values");
268 return new TDoubleArrayList(0);
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);
275 //System.out.println("data available [" + dataFrom + " .. " + dataEnd + "]");
276 //System.out.println("will sample between [" + from + " .. " + end + "]");
278 // Iterate until endTime is met
283 timeStep = timeWindow / maxSamples;
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.
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));
295 //System.out.println("=> goto " + time);
297 TDoubleArrayList result = new TDoubleArrayList();
298 if (!iter.gotoTime(time)) {
299 //System.out.println("=> no sample found at " + time);
303 //time = iter.getValueBand().getTimeDouble();
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;
310 //System.out.println("process " + time + " " + iter.getValueBand() + " (ignore = " + ignore + ")");
312 // Check for valid value
313 if ( iter.hasValidValue() ) {
317 //System.out.println("Add time : " + time);
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:
328 //System.out.println(" previous .. done!");
329 result.add(((Number) value).doubleValue());
333 case LINEAR_INTERPOLATION:
334 if (time != iter.getValueBand().getTimeDouble() && iter.hasNext()) {
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();
342 double t2 = iter.getValueBand().getTimeDouble();
343 Number v2 = (Number) iter.getValueBand().getValue();
344 iter.gotoIndex(currentIndex);
346 double vs = v1.doubleValue();
348 vs = HistoryExportUtil.biglerp(t12, v1.doubleValue(), t2, v2.doubleValue(), time);
351 //System.out.println(" linear .. done!");
356 // Exact timestamp match, or last sample.
357 // Don't interpolate nor extrapolate.
359 //System.out.println(" else .. done!");
360 result.add(((Number) value).doubleValue());
366 throw new UnsupportedOperationException("Unsupported interpolation: " + interpolation);
369 throw new IllegalStateException("Value is not a number " + value);
371 } else if (value instanceof Boolean) {
373 result.add( (Boolean)value ? 1.0: 0.0);
375 throw new IllegalStateException("Value is not a number " + value);
381 // Read next values, and the following times
382 if ( timeStep>0.0 ) {
383 bigTime = bigTime.add(bigTimeStep);
384 time = bigTime.doubleValue();
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);
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;
400 boolean hasMore = false;
402 iter.proceedToTime(time);
403 if (HistoryExportUtil.contains(iter, time)) hasMore = true;
407 duplicateLastDataPoint(result, end);
412 } while (time <= end);
414 //System.out.println("=> [" + result.size() + "]" + Arrays.toString(result.toArray()));
415 //System.out.println("=> [" + result.size() + "]");
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);