package org.simantics.history;
-import gnu.trove.list.array.TDoubleArrayList;
-
import java.io.IOException;
import java.math.BigDecimal;
import org.simantics.history.util.StreamIterator;
import org.simantics.history.util.ValueBand;
+import gnu.trove.list.array.TDoubleArrayList;
+
public class HistorySampler {
public synchronized static TDoubleArrayList sample( HistorySamplerItem item, double from, double end, double timeWindow, double timeStep, boolean resample ) throws HistoryException, IOException {
}
-
+ // ------------------------------------------------------------------------
+ // New history sampling routine, supports mip-mapped subscriptions
+
+ public synchronized static TDoubleArrayList sample(HistorySamplerItem2 item, double end, double timeWindow, int maxSamples, boolean resample) throws HistoryException, IOException {
+ try {
+ // Avoid div / 0
+ if (maxSamples <= 0)
+ return new TDoubleArrayList(0);
+
+ // If there is something pending at this point, flush before opening for read
+ if (item.collector != null)
+ item.collector.flush();
+
+ // Open data source with most suitable sampling interval
+ double secondsPerPixel = timeWindow / (double) maxSamples;
+ //System.out.println("SECONDS / PIXEL: " + secondsPerPixel);
+ item.open(secondsPerPixel);
+
+ return sample(item.iter, end, timeWindow, maxSamples, resample);
+ } finally {
+ item.close();
+ }
+ }
+
+ private static TDoubleArrayList sample(StreamIterator iter, double endTime, double timeWindow, int maxSamples, boolean resample) throws HistoryException, IOException {
+ double fromTime = endTime - timeWindow;
+ //System.out.println("sample: [" + fromTime + " .. " + endTime + "] window = " + timeWindow + " s, max samples = " + maxSamples + ", resample = " + resample);
+ ExportInterpolation interpolation = ExportInterpolation.LINEAR_INTERPOLATION;
+
+ if (iter.isEmpty() || (resample && maxSamples <= 0))
+ return new TDoubleArrayList(0);
+
+ double dataFrom = iter.getFirstTime();
+ double dataEnd = iter.getLastTime();
+
+ // Prepare time
+ boolean hasAnyValues = dataFrom != Double.MAX_VALUE && dataEnd != -Double.MAX_VALUE;
+ if (!hasAnyValues) {
+ //System.out.println("=> no values");
+ return new TDoubleArrayList(0);
+ }
+
+ // Make intersection of actual data range (allFrom, allEnd) and requested data (from, end)
+ double from = Math.max(fromTime, dataFrom);
+ double end = Math.min(endTime, dataEnd);
+
+ //System.out.println("data available [" + dataFrom + " .. " + dataEnd + "]");
+ //System.out.println("will sample between [" + from + " .. " + end + "]");
+
+ // Iterate until endTime is met
+ double time = from;
+ double timeStep = 0;
+
+ if (resample) {
+ timeStep = timeWindow / maxSamples;
+ } else {
+ // 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.
+ }
+
+ // Must convert double times to String when initializing BigDecimal.
+ // Otherwise BigDecimal will pick up inaccuracies from beyond 15 precise digits
+ // thus making a mess of the time step calculations.
+ BigDecimal bigTime = new BigDecimal(String.valueOf(time));
+ BigDecimal bigTimeStep = new BigDecimal(String.valueOf(timeStep));
+
+ //System.out.println("=> goto " + time);
+
+ TDoubleArrayList result = new TDoubleArrayList();
+ if (!iter.gotoTime(time)) {
+ //System.out.println("=> no sample found at " + time);
+ return result;
+ }
+
+ //time = iter.getValueBand().getTimeDouble();
+
+ //System.out.println("=> ignore first item?: " + time + " - " + from + " = " + (time-from) + " => " + (Math.abs(time-from) < 1e-6));
+ //boolean ignore = Math.abs(time-from) < 1e-6;
+ boolean ignore = false;
+
+ do {
+ //System.out.println("process " + time + " " + iter.getValueBand() + " (ignore = " + ignore + ")");
+
+ // Check for valid value
+ if ( iter.hasValidValue() ) {
+
+ // Write time
+ if (!ignore) {
+ //System.out.println("Add time : " + time);
+ result.add(time);
+ }
+ // Write value
+ Object value = iter.getValueBand().getValue();
+ //System.out.print("Add value : " + value);
+ if (value instanceof Number) {
+ if (value instanceof Float || value instanceof Double) {
+ switch (interpolation) {
+ case PREVIOUS_SAMPLE:
+ if (!ignore) {
+ //System.out.println(" previous .. done!");
+ result.add(((Number) value).doubleValue());
+ }
+ break;
+
+ case LINEAR_INTERPOLATION:
+ if (time != iter.getValueBand().getTimeDouble() && iter.hasNext()) {
+ // Interpolate
+ int currentIndex = iter.getIndex();
+ ValueBand band = iter.getValueBand();
+ //double t1 = band.getTimeDouble();
+ Number v1 = (Number) value;
+ double t12 = band.getEndTimeDouble();
+ iter.next();
+ double t2 = iter.getValueBand().getTimeDouble();
+ Number v2 = (Number) iter.getValueBand().getValue();
+ iter.gotoIndex(currentIndex);
+
+ double vs = v1.doubleValue();
+ if (time > t12)
+ vs = HistoryExportUtil.biglerp(t12, v1.doubleValue(), t2, v2.doubleValue(), time);
+
+ if (!ignore) {
+ //System.out.println(" linear .. done!");
+ result.add(vs);
+ }
+
+ } else {
+ // Exact timestamp match, or last sample.
+ // Don't interpolate nor extrapolate.
+ if (!ignore) {
+ //System.out.println(" else .. done!");
+ result.add(((Number) value).doubleValue());
+ }
+ }
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unsupported interpolation: " + interpolation);
+ }
+ } else {
+ throw new IllegalStateException("Value is not a number " + value);
+ }
+ } else if (value instanceof Boolean) {
+ if(!ignore)
+ result.add( (Boolean)value ? 1.0: 0.0);
+ } else {
+ throw new IllegalStateException("Value is not a number " + value);
+ }
+ }
+
+ ignore = false;
+
+ // Read next values, and the following times
+ if ( timeStep>0.0 ) {
+ bigTime = bigTime.add(bigTimeStep);
+ time = bigTime.doubleValue();
+ } else {
+ // Get smallest end time that is larger than current time
+ Double nextTime = null;
+ //System.out.println(" time = "+time);
+ if(!iter.hasNext()) {
+ duplicateLastDataPoint(result, end);
+ break;
+ }
+ Double itemNextTime = iter.getNextTime( time );
+ //System.out.println(" "+iter.toString()+" nextTime="+itemNextTime);
+ if ( nextTime == null || ( nextTime > itemNextTime && !itemNextTime.equals( time ) ) ) nextTime = itemNextTime;
+ if ( nextTime == null || nextTime.equals( time ) ) break;
+ time = nextTime;
+ }
+
+ boolean hasMore = false;
+
+ iter.proceedToTime(time);
+ if (HistoryExportUtil.contains(iter, time)) hasMore = true;
+
+ if (!hasMore) {
+ if (time <= end) {
+ duplicateLastDataPoint(result, end);
+ }
+ break;
+ }
+
+ } while (time <= end);
+
+ //System.out.println("=> [" + result.size() + "]" + Arrays.toString(result.toArray()));
+ //System.out.println("=> [" + result.size() + "]");
+ return result;
+ }
+
+ private static void duplicateLastDataPoint(TDoubleArrayList data, double timestamp) {
+ double lastValue = data.get(data.size() - 1);
+ //System.out.println("Duplicating last sample value " + lastValue + " @ " + timestamp);
+ data.add(timestamp);
+ data.add(lastValue);
+ }
+
}