diff --git a/java_console/models/src/com/rusefi/TimeBasedBuffer.java b/java_console/models/src/com/rusefi/TimeBasedBuffer.java
index 081c2f5b03..2ebccbab42 100644
--- a/java_console/models/src/com/rusefi/TimeBasedBuffer.java
+++ b/java_console/models/src/com/rusefi/TimeBasedBuffer.java
@@ -1,4 +1,47 @@
package com.rusefi;
-public class TimeBasedBuffer {
+import java.util.TreeMap;
+
+/**
+ * Last X seconds of values.
+ *
+ * This data structure holds only one value for each millisecond timestamp but that should be totally fine
+ * for our purposes
+ *
+ * @see EtbTestSequence
+ */
+public class TimeBasedBuffer implements DataBuffer {
+ private final TreeMap values = new TreeMap<>();
+ private final long duration;
+
+ public TimeBasedBuffer(long duration) {
+ this.duration = duration;
+ }
+
+ private synchronized void truncate() {
+ long now = currentTimeMillis();
+ values.headMap(now - duration).clear();
+ }
+
+ protected long currentTimeMillis() {
+ return System.currentTimeMillis();
+ }
+
+ public synchronized void add(double value) {
+ values.put(currentTimeMillis(), value);
+ }
+
+ public synchronized int getSize() {
+ truncate();
+ return values.size();
+ }
+
+ public synchronized double[] getValues() {
+ truncate();
+ double[] result = new double[values.size()];
+ int i = 0;
+ for (Double v : values.values())
+ result[i++] = v;
+ return result;
+ }
}
diff --git a/java_console/models/src/com/rusefi/test/TimeBasedBufferTest.java b/java_console/models/src/com/rusefi/test/TimeBasedBufferTest.java
index 926a7fefd2..03661daca2 100644
--- a/java_console/models/src/com/rusefi/test/TimeBasedBufferTest.java
+++ b/java_console/models/src/com/rusefi/test/TimeBasedBufferTest.java
@@ -1,4 +1,51 @@
package com.rusefi.test;
+import com.rusefi.DataBuffer;
+import com.rusefi.TimeBasedBuffer;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
public class TimeBasedBufferTest {
+ @Test
+ public void testRealTruncate() throws InterruptedException {
+ TimeBasedBuffer b = new TimeBasedBuffer(100);
+ b.add(1);
+ System.out.println(b.getSize()); // cannot really test size here since who know how time flies
+ b.add(2);
+ b.add(1);
+ System.out.println(b.getSize()); // cannot really test size here since who know how time flies
+ Thread.sleep(200);
+ assertEquals(0, b.getSize());
+ }
+
+ @Test
+ public void testTruncate() {
+ AtomicLong time = new AtomicLong();
+ TimeBasedBuffer b = new TimeBasedBuffer(100) {
+ @Override
+ protected long currentTimeMillis() {
+ return time.get();
+ }
+ };
+
+ b.add(1);
+ assertEquals(1, b.getSize());
+ b.add(2);
+ assertEquals(1, b.getSize()); // value for same timestamp is overriden
+ time.set(3);
+ b.add(3);
+ assertEquals(2, b.getSize()); // value for same timestamp is overriden
+ double[] v = b.getValues();
+ assertTrue(Arrays.equals(new double[]{2, 3}, v));
+
+ assertEquals(0.707106, DataBuffer.getStandardDeviation(b.getValues()), 0.001);
+
+ time.set(150);
+ assertEquals(0, b.getSize());
+ }
}
diff --git a/java_console/ui/src/com/rusefi/ui/widgets/EtbTestSequence.java b/java_console/ui/src/com/rusefi/ui/widgets/EtbTestSequence.java
index 40a1964dbb..a735b14eeb 100644
--- a/java_console/ui/src/com/rusefi/ui/widgets/EtbTestSequence.java
+++ b/java_console/ui/src/com/rusefi/ui/widgets/EtbTestSequence.java
@@ -2,6 +2,7 @@ package com.rusefi.ui.widgets;
import com.rusefi.FileLog;
import com.rusefi.SensorSnifferCentral;
+import com.rusefi.TimeBasedBuffer;
import com.rusefi.core.Sensor;
import com.rusefi.core.SensorCentral;
import com.rusefi.io.CommandQueue;
@@ -19,6 +20,8 @@ import java.util.concurrent.TimeUnit;
*
* 11/16/2017
* (c) Andrey Belomutskiy
+ *
+ * @see TimeBasedBuffer
*/
public class EtbTestSequence {
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();