package com.evsuite.abrp; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.List; /** * Live samples waiting to reach the server, oldest first. * * Bounded: while the car is offline the oldest samples are dropped once {@link #capacity} * is reached, so a long dead spot costs the start of the gap, never the head unit's memory. * Held in memory only — a process restart loses what is queued, which is the price of not * writing to flash every 15 seconds. * * Free of Android types so the policy is unit-tested on the JVM. */ final class TelemetryQueue { /** About eight hours at one sample per 15-second tick. */ static final int DEFAULT_CAPACITY = 2000; /** The server takes at most 500 samples per request. */ static final int MAX_BATCH = 500; private final int capacity; private final ArrayDeque samples = new ArrayDeque<>(); private int dropped; TelemetryQueue() { this(DEFAULT_CAPACITY); } TelemetryQueue(int capacity) { this.capacity = capacity; } synchronized void add(String sampleJson) { if (samples.size() >= capacity) { samples.removeFirst(); dropped++; } samples.addLast(sampleJson); } /** Up to {@link #MAX_BATCH} of the oldest samples, still queued until {@link #remove}. */ synchronized List peekBatch() { List batch = new ArrayList<>(Math.min(samples.size(), MAX_BATCH)); for (String sample : samples) { if (batch.size() >= MAX_BATCH) break; batch.add(sample); } return batch; } /** Drops the oldest {@code count} samples once the server has confirmed them. */ synchronized void remove(int count) { for (int i = 0; i < count && !samples.isEmpty(); i++) samples.removeFirst(); } synchronized int size() { return samples.size(); } /** Samples lost to the capacity limit since the queue was created. */ synchronized int dropped() { return dropped; } }