- New Server tab: server URL, ingest token and installation id, with
Save, Test connection (health, then a read-only token check) and the
config-file import that upstream removed (app folder / USB stick first,
document picker as fallback). Settings are kept encrypted and read on
every use, so the service picks up a change without a restart. Each
installation generates its own id; the inherited upstream UUID and
the baked-in CONSUMPTION_API_* build fields are gone.
- Live telemetry restored from before ea8032a (TelemetryPayload,
UploadCadence, UploadSettings, GPS) and sent in batches to
POST /v1/telemetry instead of ABRP; the server forwards to ABRP. Adds
ext_temp, voltage and current. Unsent samples queue in memory (about
8 h) while offline.
- Upload tab: the live-telemetry switch and cadence controls work again;
the dead service and autostart switches are removed.
- Manifest: location permissions and foreground service types.
- Unit tests compile again: 114 (stable) / 136 (unstable) pass. Tests
for the deleted AbrpApi and HeadUnitWeather removed; new tests for
ServerConfig and TelemetryQueue. Lint passes.
Not yet tried on the car.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
60 lines
2.0 KiB
Java
60 lines
2.0 KiB
Java
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<String> 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<String> peekBatch() {
|
|
List<String> 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; }
|
|
}
|