Implemented selection of sampled history data source based on the
seconds/pixel (= timeWindow / maxSamples) ratio provided to the sampler.
This is one way to attempt not sampling too much data for trend views
that cannot inherently handle large amounts of data.
gitlab #9
Change-Id: I8c67068d6e832d33d4ac3adef270d31f1cb0e722
(cherry picked from commit
e3940e67813aa0988bdd0fe6161500034e6e7518)
package org.simantics.history;
package org.simantics.history;
-import gnu.trove.list.array.TDoubleArrayList;
-
import java.io.IOException;
import java.math.BigDecimal;
import java.io.IOException;
import java.math.BigDecimal;
import org.simantics.history.util.StreamIterator;
import org.simantics.history.util.ValueBand;
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 {
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 {
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();
// If there is something pending at this point, flush before opening for read
if (item.collector != null)
item.collector.flush();
- double pixelsPerSecond = (double) maxSamples / timeWindow;
- item.open(pixelsPerSecond);
+
+ // 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();
}
}
return sample(item.iter, end, timeWindow, maxSamples, resample);
} finally {
item.close();
}
}
- private static TDoubleArrayList sample(StreamIterator iter, double end, double timeWindow, int maxSamples, boolean resample) throws HistoryException, IOException {
- //System.out.println("sample: " + end + ", " + timeWindow + ", " + maxSamples + ", " + resample);
- ExportInterpolation numberInterpolation = ExportInterpolation.LINEAR_INTERPOLATION;
+ 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;
- double timeStep = 0;
- double startTime = -1e10;
-
- TDoubleArrayList result = new TDoubleArrayList();
- if (iter.isEmpty())
- return result;
+ if (iter.isEmpty() || (resample && maxSamples <= 0))
+ return new TDoubleArrayList(0);
- double allFrom = iter.getFirstTime();
- double allEnd = iter.getLastTime();
- //System.out.println("data available [" + allFrom + " .. " + allEnd + "]");
-
- double from = allEnd-timeWindow;
- end = allEnd;
- //System.out.println("sample between [" + from + " .. " + end + "]");
+ double dataFrom = iter.getFirstTime();
+ double dataEnd = iter.getLastTime();
- boolean hasAnyValues = allFrom != Double.MAX_VALUE && allEnd != -Double.MAX_VALUE;
+ boolean hasAnyValues = dataFrom != Double.MAX_VALUE && dataEnd != -Double.MAX_VALUE;
- //System.err.println("=> no values");
- return result;
+ //System.out.println("=> no values");
+ return new TDoubleArrayList(0);
}
// Make intersection of actual data range (allFrom, allEnd) and requested data (from, end)
}
// Make intersection of actual data range (allFrom, allEnd) and requested data (from, end)
- double _from = Math.max(allFrom, from);
- double _end = Math.min(allEnd, end);
+ double from = Math.max(fromTime, dataFrom);
+ double end = Math.min(endTime, dataEnd);
- // Iterate until endTime is met for all variables
- double time = _from;
+ //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) {
- // 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
- time = _from;
- timeStep = 0.0;
+ if (resample) {
+ timeStep = timeWindow / maxSamples;
- // time = startTime + n*timeStep
- // Sampling based on given startTime and timeStep
- if (timeStep > 0) {
- // Find the first sample time that contains data
- double n = Math.max(0, Math.ceil((_from-startTime) / timeStep));
- time = startTime + n*timeStep;
- } else {
- // Start sampling from startTime but make sure that it is not less than _from
- if(startTime > _from) time = startTime;
- }
+ // 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.
}
// Must convert double times to String when initializing BigDecimal.
BigDecimal bigTime = new BigDecimal(String.valueOf(time));
BigDecimal bigTimeStep = new BigDecimal(String.valueOf(timeStep));
BigDecimal bigTime = new BigDecimal(String.valueOf(time));
BigDecimal bigTimeStep = new BigDecimal(String.valueOf(timeStep));
- //System.err.println("=> goto " + time);
+ //System.out.println("=> goto " + time);
- if(!iter.gotoTime(time)) {
- //System.err.println("=> no sample found at " + time);
+ TDoubleArrayList result = new TDoubleArrayList();
+ if (!iter.gotoTime(time)) {
+ //System.out.println("=> no sample found at " + time);
return result;
}
//time = iter.getValueBand().getTimeDouble();
return result;
}
//time = iter.getValueBand().getTimeDouble();
- boolean ignore = Math.abs(time-from) < 1e-6;
+ //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;
-
- //System.err.println("process " + time + " " + iter.getValueBand());
-
-
+ //System.out.println("process " + time + " " + iter.getValueBand() + " (ignore = " + ignore + ")");
// Check for valid value
if ( iter.hasValidValue() ) {
// Write time
// Check for valid value
if ( iter.hasValidValue() ) {
// Write time
- if(!ignore) {
- //System.err.println("Add time : " + time);
+ if (!ignore) {
+ //System.out.println("Add time : " + time);
result.add(time);
}
// Write value
Object value = iter.getValueBand().getValue();
result.add(time);
}
// Write value
Object value = iter.getValueBand().getValue();
- //System.err.print("Add value : " + value);
+ //System.out.print("Add value : " + value);
if (value instanceof Number) {
if (value instanceof Float || value instanceof Double) {
if (value instanceof Number) {
if (value instanceof Float || value instanceof Double) {
- switch (numberInterpolation) {
+ switch (interpolation) {
-
- if(!ignore) {
- // System.err.println(" previous .. done!");
+ if (!ignore) {
+ //System.out.println(" previous .. done!");
result.add(((Number) value).doubleValue());
}
result.add(((Number) value).doubleValue());
}
break;
case LINEAR_INTERPOLATION:
if (time != iter.getValueBand().getTimeDouble() && iter.hasNext()) {
break;
case LINEAR_INTERPOLATION:
if (time != iter.getValueBand().getTimeDouble() && iter.hasNext()) {
// Interpolate
int currentIndex = iter.getIndex();
ValueBand band = iter.getValueBand();
// Interpolate
int currentIndex = iter.getIndex();
ValueBand band = iter.getValueBand();
iter.gotoIndex(currentIndex);
double vs = v1.doubleValue();
iter.gotoIndex(currentIndex);
double vs = v1.doubleValue();
vs = HistoryExportUtil.biglerp(t12, v1.doubleValue(), t2, v2.doubleValue(), time);
vs = HistoryExportUtil.biglerp(t12, v1.doubleValue(), t2, v2.doubleValue(), time);
- if(!ignore) {
- //System.err.println(" linear .. done!");
+ if (!ignore) {
+ //System.out.println(" linear .. done!");
result.add(vs);
}
} else {
// Exact timestamp match, or last sample.
// Don't interpolate nor extrapolate.
result.add(vs);
}
} else {
// Exact timestamp match, or last sample.
// Don't interpolate nor extrapolate.
- if(!ignore) {
- //System.err.println(" else .. done!");
+ if (!ignore) {
+ //System.out.println(" else .. done!");
result.add(((Number) value).doubleValue());
}
}
break;
result.add(((Number) value).doubleValue());
}
}
break;
- throw new UnsupportedOperationException("Unsupported interpolation: " + numberInterpolation);
+ throw new UnsupportedOperationException("Unsupported interpolation: " + interpolation);
}
} else {
throw new IllegalStateException("Value is not a number " + value);
}
} else {
throw new IllegalStateException("Value is not a number " + value);
} else {
// Get smallest end time that is larger than current time
Double nextTime = null;
} else {
// Get smallest end time that is larger than current time
Double nextTime = null;
- // System.out.println("time = "+time);
- if(!iter.hasNext()) break;
+ //System.out.println(" time = "+time);
+ if(!iter.hasNext()) {
+ duplicateLastDataPoint(result, end);
+ break;
+ }
Double itemNextTime = iter.getNextTime( time );
Double itemNextTime = iter.getNextTime( time );
- // System.err.println(" "+i.label+" nextTime="+itemNextTime);
+ //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;
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);
boolean hasMore = false;
iter.proceedToTime(time);
- if(HistoryExportUtil.contains(iter, time)) hasMore = true;
-
- if(!hasMore) break;
+ if (HistoryExportUtil.contains(iter, time)) hasMore = true;
+ if (!hasMore) {
+ if (time <= end) {
+ duplicateLastDataPoint(result, end);
+ }
+ break;
+ }
- //System.err.println("=> " + Arrays.toString(result.toArray()));
+ //System.out.println("=> [" + result.size() + "]" + Arrays.toString(result.toArray()));
+ //System.out.println("=> [" + result.size() + "]");
+ 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);
- private LevelItem getFormat(double pixelsPerSecond) throws HistoryException {
+ private LevelItem getFormat(double secondsPerPixel) throws HistoryException {
LevelItem result = null;
for (LevelItem format : items) {
double interval = format.samplingInterval;
LevelItem result = null;
for (LevelItem format : items) {
double interval = format.samplingInterval;
- if (Double.isNaN( interval ) || interval <= pixelsPerSecond) {
+ if (Double.isNaN( interval ) || interval <= secondsPerPixel) {
result = format;
} else {
break;
result = format;
} else {
break;