package org.simantics.history; import gnu.trove.list.array.TDoubleArrayList; import java.io.IOException; import java.math.BigDecimal; import org.simantics.history.csv.ExportInterpolation; import org.simantics.history.util.HistoryExportUtil; import org.simantics.history.util.StreamIterator; import org.simantics.history.util.ValueBand; public class HistorySampler { public synchronized static TDoubleArrayList sample( HistorySamplerItem item, double from, double end, double timeWindow, double timeStep, boolean resample ) throws HistoryException, IOException { try { // If there is something pending at this point, flush before opening for read if(item.collector != null) item.collector.flush(); item.open(); return sample(item.iter, from, end, timeWindow, timeStep, resample); } finally { item.close(); } } public static TDoubleArrayList sample( StreamIterator iter, double from, double end, double timeWindow, double timeStep, boolean resample ) throws HistoryException, IOException { return sample(iter, from, end, timeWindow, timeStep, resample, 0.0); } public static TDoubleArrayList sample( StreamIterator iter, double from, double end, double timeWindow, double timeStep, boolean resample, Double sampleFrom ) throws HistoryException, IOException { ExportInterpolation numberInterpolation = ExportInterpolation.LINEAR_INTERPOLATION; double startTime = from; if(sampleFrom != null) { // This option can be used do define the offset of sampling. Samples will be sampleFrom + n * timeStep startTime = sampleFrom; } TDoubleArrayList result = new TDoubleArrayList(); if(iter.isEmpty()) return result; double allFrom = iter.getFirstTime(); double allEnd = 10e10;//iter.getLastTime(); if(from > (allEnd + timeStep)) { from = allEnd-timeWindow; end = allEnd; } // System.err.println("sample " + from + " " + end); // if(from < 0) // System.err.println("fgag"); // Prepare time boolean hasAnyValues = allFrom != Double.MAX_VALUE && allEnd != -Double.MAX_VALUE; // Make intersection of actual data range (allFrom, allEnd) and requested data (from, end) double _from = Double.MAX_VALUE, _end = -Double.MAX_VALUE; if (hasAnyValues) { _from = Math.max(allFrom, from); _end = Math.min(allEnd, end); } if (!hasAnyValues) { // System.err.println("=> no values"); return result; } // Iterate until endTime is met for all variables double time = _from; 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; } else { // 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; } } // 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.err.println("=> goto " + time); if(!iter.gotoTime(time)) { //System.err.println("=> no sample found at " + time); return result; } //time = iter.getValueBand().getTimeDouble(); boolean ignore = Math.abs(time-from) < 1e-6; do { //System.err.println("process " + time + " " + iter.getValueBand()); // Check for valid value if ( iter.hasValidValue() ) { // Write time if(!ignore) { // System.err.println("Add time : " + time); result.add(time); } // Write value Object value = iter.getValueBand().getValue(); //System.err.print("Add value : " + value); if (value instanceof Number) { if (value instanceof Float || value instanceof Double) { switch (numberInterpolation) { case PREVIOUS_SAMPLE: if(!ignore) { // System.err.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.err.println(" linear .. done!"); result.add(vs); } } else { // Exact timestamp match, or last sample. // Don't interpolate nor extrapolate. if(!ignore) { // System.err.println(" else .. done!"); result.add(((Number) value).doubleValue()); } } break; default: throw new UnsupportedOperationException("Unsupported interpolation: " + numberInterpolation); } } 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()) break; Double itemNextTime = iter.getNextTime( time ); // System.err.println(" "+i.label+" 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) break; } while (time<=_end); //System.err.println("=> " + Arrays.toString(result.toArray())); return result; } }