App 2.32.1: drop the cruise control reads; the recorder becomes general
The 2.32.0 recording (ACC set to 101, 90 and 80 km/h behind another car) found none of it among 1,194 values: the head unit does not get the ACC data. Removed the cruise log and the set-speed read (EVHardware is as before 2.27.3); the finding is under "Signals the car does not provide". The recorder stays as the Signal recorder, for finding other signals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LhzAVaUyqcw5PkGjz4mWW6
This commit is contained in:
+2
-2
@@ -34,8 +34,8 @@ android {
|
||||
applicationId "com.stinnesbeck.nilsmg4"
|
||||
minSdk 27
|
||||
targetSdk 36
|
||||
versionCode 130
|
||||
versionName "2.32.0"
|
||||
versionCode 131
|
||||
versionName "2.32.1"
|
||||
|
||||
// Nothing about the server ships in the APK: its URL and ingest token are entered in
|
||||
// the app's Server tab (ServerConfig), and the database is only reachable by the server.
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
package com.evsuite.abrp;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The adaptive cruise control debug log on the Logs tab: the driver-assistance properties the
|
||||
* car reports (`EVHardware.readAccDebug`, raw: the cruise control target speed, the assist
|
||||
* modes, the object the ACC follows) with the time, speed and gear. It is how they get matched
|
||||
* to what the cluster shows: drive with the app open, set the cruise control, change the speed
|
||||
* and the distance, switch it off, and read what the car reported.
|
||||
*
|
||||
* A line is written at once when a mode or the target speed changes, and every
|
||||
* {@link #DRIVING_EVERY_MS} while driving (the followed object's values change all the time,
|
||||
* so they alone never write one). The last {@link #MAX_LINES} lines are kept in a file in the
|
||||
* app's own storage, appended to, so a drive does not rewrite it every few seconds.
|
||||
*/
|
||||
final class CruiseLog {
|
||||
|
||||
/** About 20 minutes of driving at one line per two seconds. */
|
||||
static final int MAX_LINES = 600;
|
||||
/** The newest lines the Logs tab shows. */
|
||||
static final int SHOWN_LINES = 150;
|
||||
static final long DRIVING_EVERY_MS = 2000;
|
||||
/** Values that change all the time while following a car: they never write a line alone. */
|
||||
private static final List<String> MOVING = java.util.Arrays.asList("obj", "dist", "rel");
|
||||
private static final String FILE = "cruise_log.txt";
|
||||
|
||||
private static ArrayDeque<String> lines;
|
||||
private static String lastSteady;
|
||||
private static long lastWriteMs;
|
||||
private static String current = "—";
|
||||
|
||||
private CruiseLog() {}
|
||||
|
||||
/** The values that write a line when they change: everything but the followed object. */
|
||||
static String steady(List<String[]> readings) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String[] r : readings) {
|
||||
if (!MOVING.contains(r[0])) sb.append(r[0]).append(' ').append(r[1]).append(" ");
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/** All readings as the log writes them. */
|
||||
static String values(List<String[]> readings) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String[] r : readings) sb.append(r[0]).append(' ').append(r[1]).append(" ");
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/** Whether this reading gets a line: a changed mode or target, or time while driving. */
|
||||
static boolean due(String steady, String lastSteady, long nowMs, long lastWriteMs, boolean driving) {
|
||||
if (!steady.equals(lastSteady)) return true;
|
||||
return driving && nowMs - lastWriteMs >= DRIVING_EVERY_MS;
|
||||
}
|
||||
|
||||
/** Driving: in D or R, or moving. Unread values are not driving. */
|
||||
static boolean driving(Float speedKmh, Integer gear) {
|
||||
return (gear != null && (gear == 4 || gear == 2)) || (speedKmh != null && speedKmh > 0.5f);
|
||||
}
|
||||
|
||||
/** Called with each reading, off the main thread. */
|
||||
static void note(Context context, List<String[]> readings, Float speedKmh, Integer gear) {
|
||||
if (context == null || readings == null || readings.isEmpty()) return;
|
||||
long now = System.currentTimeMillis();
|
||||
String steady = steady(readings);
|
||||
synchronized (CruiseLog.class) {
|
||||
current = values(readings);
|
||||
if (!due(steady, lastSteady, now, lastWriteMs, driving(speedKmh, gear))) return;
|
||||
lastSteady = steady;
|
||||
lastWriteMs = now;
|
||||
String label = Gear.label(gear);
|
||||
String line = new SimpleDateFormat("dd.MM. HH:mm:ss", Locale.US).format(new Date(now))
|
||||
+ " " + (speedKmh == null ? "—" : String.format(Locale.US, "%.0f km/h", speedKmh))
|
||||
+ " " + (label.isEmpty() ? "?" : label)
|
||||
+ " " + current;
|
||||
load(context);
|
||||
lines.addLast(line);
|
||||
File file = file(context);
|
||||
if (lines.size() > MAX_LINES + 100) {
|
||||
// Trim now and then: the file is otherwise only appended to.
|
||||
while (lines.size() > MAX_LINES) lines.removeFirst();
|
||||
write(file, lines, false);
|
||||
} else {
|
||||
List<String> one = new ArrayList<>();
|
||||
one.add(line);
|
||||
write(file, one, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** All readings now, "—" before the first. */
|
||||
static synchronized String current() {
|
||||
return current;
|
||||
}
|
||||
|
||||
/** The newest {@link #SHOWN_LINES}, newest first. */
|
||||
static synchronized List<String> lines(Context context) {
|
||||
load(context);
|
||||
List<String> newest = new ArrayList<>();
|
||||
Iterator<String> it = lines.descendingIterator();
|
||||
while (it.hasNext() && newest.size() < SHOWN_LINES) newest.add(it.next());
|
||||
return newest;
|
||||
}
|
||||
|
||||
private static File file(Context context) {
|
||||
return new File(context.getApplicationContext().getFilesDir(), FILE);
|
||||
}
|
||||
|
||||
/** Reads the file once per process; drops the change-only log of 2.27.3. */
|
||||
private static void load(Context context) {
|
||||
if (lines != null) return;
|
||||
lines = new ArrayDeque<>();
|
||||
context.getApplicationContext().deleteSharedPreferences("cruise_log");
|
||||
File file = file(context);
|
||||
if (!file.exists()) return;
|
||||
try (BufferedReader in = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = in.readLine()) != null) {
|
||||
if (line.isEmpty()) continue;
|
||||
lines.addLast(line);
|
||||
if (lines.size() > MAX_LINES) lines.removeFirst();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// An unreadable log starts again empty.
|
||||
}
|
||||
}
|
||||
|
||||
private static void write(File file, Iterable<String> text, boolean append) {
|
||||
try (Writer out = new OutputStreamWriter(new FileOutputStream(file, append), StandardCharsets.UTF_8)) {
|
||||
for (String line : text) out.write(line + "\n");
|
||||
} catch (Exception ignored) {
|
||||
// A debug log that cannot be written is not worth disturbing the screen for.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,6 +363,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
settingsPane, logPane };
|
||||
|
||||
UpdateLog.noteVersion(this, BuildConfig.VERSION_NAME);
|
||||
// The cruise control logs of 2.31.1–2.32.0, gone with the feature (SPECS.md).
|
||||
deleteFile("cruise_log.txt");
|
||||
deleteFile("acc_recording.txt");
|
||||
bindVehiclePane();
|
||||
bindGauges();
|
||||
bindCockpit();
|
||||
@@ -375,10 +378,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
statusText = servicePane.findViewById(R.id.status_text);
|
||||
callLogText = logPane.findViewById(R.id.call_log_text);
|
||||
logPane.findViewById(R.id.flush_now_button).setOnClickListener(v -> manualFlush());
|
||||
logPane.findViewById(R.id.acc_record_button).setOnClickListener(v -> {
|
||||
if (AccRecorder.running()) AccRecorder.stop();
|
||||
else AccRecorder.start(this);
|
||||
refreshAccRecorder();
|
||||
logPane.findViewById(R.id.signal_record_button).setOnClickListener(v -> {
|
||||
if (SignalRecorder.running()) SignalRecorder.stop();
|
||||
else SignalRecorder.start(this);
|
||||
refreshSignalRecorder();
|
||||
});
|
||||
|
||||
setUpPager();
|
||||
@@ -1508,9 +1511,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
* About twenty calls into the car's services: on a background thread, so the screen never
|
||||
* waits for them; the values are shown on the main thread once they are in.
|
||||
*/
|
||||
/** Counts readings on the reader thread, for the once-a-second cruise control log. */
|
||||
private int accDebugTick;
|
||||
|
||||
private void readVehicle() {
|
||||
if (!reading.compareAndSet(false, true)) return;
|
||||
try {
|
||||
@@ -1522,14 +1522,6 @@ public class MainActivity extends AppCompatActivity {
|
||||
// The timestamp is passed explicitly: its Kotlin default is not visible from Java.
|
||||
EnergySnapshot s = vehicleReader.read(System.currentTimeMillis());
|
||||
latestSnapshot = s;
|
||||
// The cruise control debug log, once a second: about eight more calls.
|
||||
if (accDebugTick++ % 5 == 0) {
|
||||
java.util.List<String[]> acc = new java.util.ArrayList<>();
|
||||
for (kotlin.Pair<String, String> r : com.evsuite.hardware.EVHardware.INSTANCE.readAccDebug()) {
|
||||
acc.add(new String[] { r.getFirst(), r.getSecond() });
|
||||
}
|
||||
CruiseLog.note(getApplicationContext(), acc, s.getSpeedKmh(), s.getGearRaw());
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
if (!isDestroyed()) showVehicle(s);
|
||||
});
|
||||
@@ -2134,11 +2126,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
/** The ACC recorder's button and what it has found so far. */
|
||||
private void refreshAccRecorder() {
|
||||
MaterialButton button = logPane.findViewById(R.id.acc_record_button);
|
||||
button.setText(AccRecorder.running() ? R.string.btn_acc_stop : R.string.btn_acc_record);
|
||||
TextView text = logPane.findViewById(R.id.acc_record_text);
|
||||
java.util.List<String> status = AccRecorder.status(this);
|
||||
private void refreshSignalRecorder() {
|
||||
MaterialButton button = logPane.findViewById(R.id.signal_record_button);
|
||||
button.setText(SignalRecorder.running() ? R.string.btn_signal_stop : R.string.btn_signal_record);
|
||||
TextView text = logPane.findViewById(R.id.signal_record_text);
|
||||
java.util.List<String> status = SignalRecorder.status(this);
|
||||
text.setVisibility(status.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
// The rarest changes are the leads; the rest is in the saved recording.
|
||||
if (status.size() > 81) status = status.subList(0, 81);
|
||||
@@ -2146,18 +2138,11 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
private void refreshCallLog() {
|
||||
refreshAccRecorder();
|
||||
refreshSignalRecorder();
|
||||
java.util.List<UploadLog.Entry> entries = AbrpUploadService.log().recent();
|
||||
// Update steps first: after a failed install, they are what the driver came here for.
|
||||
java.util.List<String> updates = UpdateLog.lines(this);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
// The cruise control target speed as the car reports it, to match it on the car.
|
||||
java.util.List<String> cruise = CruiseLog.lines(this);
|
||||
if (!cruise.isEmpty()) {
|
||||
sb.append(getString(R.string.cruise_log_title, CruiseLog.current()));
|
||||
for (String line : cruise) sb.append('\n').append(line);
|
||||
sb.append("\n\n");
|
||||
}
|
||||
if (!updates.isEmpty()) {
|
||||
sb.append(getString(R.string.update_log_title));
|
||||
for (String line : updates) sb.append('\n').append(line);
|
||||
|
||||
+18
-18
@@ -23,12 +23,12 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The ACC change recorder on the Logs tab: for {@link #DURATION_MS} it reads every value the
|
||||
* The signal recorder on the Logs tab: for {@link #DURATION_MS} it reads every value the
|
||||
* head unit offers (`VendorSurfaceProbe.capture`: every CarPropertyManager property and every
|
||||
* value getter of the vendor objects, read only) again and again, and every steering-wheel key
|
||||
* the head unit reports, into an {@link AccRecording}. The driver notes the times they switch
|
||||
* the cruise control on, change the set speed and the distance, and switch it off; the values
|
||||
* that change then are the leads.
|
||||
* the head unit reports, into a {@link SignalRecording}. The driver notes the times they do
|
||||
* what is to be found; the values that change then are the leads. (Built to find the adaptive
|
||||
* cruise control, 2.32.0: on SWI68 the head unit gets none of it, see SPECS.md.)
|
||||
*
|
||||
* A full capture is far heavier than the Overview's reading, so the next one waits at least
|
||||
* {@link #MIN_INTERVAL_MS} and at least as long as the last one took: the recorder uses at most
|
||||
@@ -36,19 +36,19 @@ import java.util.Map;
|
||||
* keeps the process), and stops by itself. At the end the recording goes to the app's storage
|
||||
* and to the app's folder on every USB stick plugged in.
|
||||
*/
|
||||
final class AccRecorder {
|
||||
final class SignalRecorder {
|
||||
|
||||
static final long DURATION_MS = 3 * 60_000L;
|
||||
static final long MIN_INTERVAL_MS = 2_000L;
|
||||
static final String FILE = "acc_recording.txt";
|
||||
static final String FILE = "signal_recording.txt";
|
||||
|
||||
private static AccRecording recording;
|
||||
private static SignalRecording recording;
|
||||
private static volatile boolean running;
|
||||
private static long startedMs;
|
||||
private static String result;
|
||||
private static Thread thread;
|
||||
|
||||
private AccRecorder() {}
|
||||
private SignalRecorder() {}
|
||||
|
||||
static synchronized boolean running() {
|
||||
return running;
|
||||
@@ -57,11 +57,11 @@ final class AccRecorder {
|
||||
static synchronized void start(Context context) {
|
||||
if (running) return;
|
||||
Context app = context.getApplicationContext();
|
||||
recording = new AccRecording();
|
||||
recording = new SignalRecording();
|
||||
result = null;
|
||||
startedMs = System.currentTimeMillis();
|
||||
running = true;
|
||||
thread = new Thread(() -> record(app), "acc-recorder");
|
||||
thread = new Thread(() -> record(app), "signal-recorder");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ final class AccRecorder {
|
||||
}
|
||||
|
||||
private static void record(Context app) {
|
||||
AccRecording rec = recording;
|
||||
SignalRecording rec = recording;
|
||||
BroadcastReceiver keys = new BroadcastReceiver() {
|
||||
@Override public void onReceive(Context c, Intent intent) {
|
||||
rec.note(System.currentTimeMillis(), "steering-wheel key: " + extras(intent.getExtras()));
|
||||
@@ -107,7 +107,7 @@ final class AccRecorder {
|
||||
}
|
||||
}
|
||||
String saved = save(app, rec);
|
||||
synchronized (AccRecorder.class) {
|
||||
synchronized (SignalRecorder.class) {
|
||||
running = false;
|
||||
result = saved;
|
||||
}
|
||||
@@ -122,10 +122,10 @@ final class AccRecorder {
|
||||
}
|
||||
|
||||
/** Writes the recording; says where it went. */
|
||||
private static String save(Context app, AccRecording rec) {
|
||||
private static String save(Context app, SignalRecording rec) {
|
||||
List<String> text = new ArrayList<>();
|
||||
text.add("ACC recording, " + rec.captures() + " captures from "
|
||||
+ AccRecording.time(startedMs) + ", app " + BuildConfig.VERSION_NAME);
|
||||
text.add("Signal recording, " + rec.captures() + " captures from "
|
||||
+ SignalRecording.time(startedMs) + ", app " + BuildConfig.VERSION_NAME);
|
||||
text.add("");
|
||||
text.add("Steering-wheel keys and notes:");
|
||||
text.addAll(rec.notes());
|
||||
@@ -137,7 +137,7 @@ final class AccRecorder {
|
||||
text.addAll(rec.lines());
|
||||
List<String> places = new ArrayList<>();
|
||||
if (write(new File(app.getFilesDir(), FILE), text)) places.add("app");
|
||||
String name = "acc-recording-" + new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US)
|
||||
String name = "signal-recording-" + new SimpleDateFormat("yyyyMMdd-HHmmss", Locale.US)
|
||||
.format(new Date(startedMs)) + ".txt";
|
||||
File[] dirs = app.getExternalFilesDirs(null);
|
||||
for (File dir : dirs == null ? new File[0] : dirs) {
|
||||
@@ -167,10 +167,10 @@ final class AccRecorder {
|
||||
if (recording == null) return out;
|
||||
long elapsed = (running ? System.currentTimeMillis() : startedMs + DURATION_MS) - startedMs;
|
||||
if (running) {
|
||||
out.add(context.getString(R.string.acc_recorder_running, elapsed / 60_000, (elapsed / 1000) % 60,
|
||||
out.add(context.getString(R.string.signal_recorder_running, elapsed / 60_000, (elapsed / 1000) % 60,
|
||||
recording.captures()));
|
||||
} else {
|
||||
out.add(context.getString(R.string.acc_recorder_done, AccRecording.time(startedMs),
|
||||
out.add(context.getString(R.string.signal_recorder_done, SignalRecording.time(startedMs),
|
||||
recording.captures(), result == null ? "…" : result));
|
||||
}
|
||||
// Steering-wheel keys and the like first: few, and each one a lead.
|
||||
+8
-8
@@ -11,14 +11,14 @@ import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* One recording of the ACC change recorder ({@link AccRecorder}): every value the head unit
|
||||
* offers, captured again and again while the driver switches the adaptive cruise control on,
|
||||
* changes the set speed and the distance, and switches it off. It keeps what changed, when,
|
||||
* and per value how often and through which values, so the value that follows the cruise
|
||||
* control stands out: it changes a few times, at the moments the driver noted.
|
||||
* One recording of the signal recorder ({@link SignalRecorder}): every value the head unit
|
||||
* offers, captured again and again while the driver does what is to be found (switches a
|
||||
* feature on and off, changes a setting). It keeps what changed, when, and per value how
|
||||
* often and through which values, so the value that follows the action stands out: it
|
||||
* changes a few times, at the moments the driver noted.
|
||||
* Pure Java, unit-tested.
|
||||
*/
|
||||
final class AccRecording {
|
||||
final class SignalRecording {
|
||||
|
||||
/** Distinct values kept per key for the summary. */
|
||||
static final int VALUES_KEPT = 6;
|
||||
@@ -89,8 +89,8 @@ final class AccRecording {
|
||||
}
|
||||
|
||||
/**
|
||||
* Every value that changed, the rarest first (the cruise control's switch and set speed
|
||||
* change a handful of times; speed and power all the time): key, how often, the values.
|
||||
* Every value that changed, the rarest first (a switch the driver flips changes a handful
|
||||
* of times; speed and power all the time): key, how often, the values.
|
||||
*/
|
||||
synchronized List<String> summary() {
|
||||
List<String> keys = new ArrayList<>(changes.keySet());
|
||||
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.evsuite.abrp.StableScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fillViewport="true" android:clipToPadding="false">
|
||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="12dp" android:paddingBottom="12dp">
|
||||
<!-- The ACC change recorder (AccRecorder): every value the head unit offers, recorded while driving. -->
|
||||
<!-- The signal recorder (SignalRecorder): every value the head unit offers, recorded over 3 minutes. -->
|
||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="16dp" android:paddingBottom="16dp" android:layout_marginBottom="16dp" android:background="@drawable/bg_glass">
|
||||
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/label_acc_recorder" android:textAppearance="@style/Text.EV.Eyebrow" />
|
||||
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:text="@string/acc_recorder_hint" android:textAppearance="@style/Text.EV.Secondary" />
|
||||
<com.google.android.material.button.MaterialButton android:id="@+id/acc_record_button" android:layout_width="match_parent" android:layout_height="72dp" android:layout_marginTop="16dp" android:text="@string/btn_acc_record" style="@style/Widget.EV.Button.Outlined" />
|
||||
<TextView android:id="@+id/acc_record_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:textAppearance="@style/Text.EV.Body" android:fontFamily="monospace" android:textSize="17sp" android:lineSpacingExtra="8dp" android:textIsSelectable="true" android:visibility="gone" />
|
||||
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/label_signal_recorder" android:textAppearance="@style/Text.EV.Eyebrow" />
|
||||
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="12dp" android:text="@string/signal_recorder_hint" android:textAppearance="@style/Text.EV.Secondary" />
|
||||
<com.google.android.material.button.MaterialButton android:id="@+id/signal_record_button" android:layout_width="match_parent" android:layout_height="72dp" android:layout_marginTop="16dp" android:text="@string/btn_signal_record" style="@style/Widget.EV.Button.Outlined" />
|
||||
<TextView android:id="@+id/signal_record_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="16dp" android:textAppearance="@style/Text.EV.Body" android:fontFamily="monospace" android:textSize="17sp" android:lineSpacingExtra="8dp" android:textIsSelectable="true" android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingStart="24dp" android:paddingEnd="24dp" android:paddingTop="16dp" android:paddingBottom="16dp" android:background="@drawable/bg_glass">
|
||||
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/label_call_log" android:textAppearance="@style/Text.EV.Eyebrow" />
|
||||
|
||||
@@ -302,15 +302,14 @@
|
||||
|
||||
<!-- Call log -->
|
||||
<string name="label_call_log">Recent uploads</string>
|
||||
<string name="label_acc_recorder">ACC recorder</string>
|
||||
<string name="acc_recorder_hint">Records every value the head unit offers for 3 minutes, plus the steering-wheel keys it reports, and lists what changed. Start it while driving, note the times you switch the cruise control on, change the set speed and the distance, and switch it off. With a USB stick plugged in, the whole recording is saved on it too (Android/data/…/files).</string>
|
||||
<string name="btn_acc_record">Record 3 minutes</string>
|
||||
<string name="btn_acc_stop">Stop recording</string>
|
||||
<string name="acc_recorder_running">Recording: %1$d:%2$02d of 3:00, %3$d captures. Keys reported, then values changed so far, the rarest first:</string>
|
||||
<string name="acc_recorder_done">Recording from %1$s: %2$d captures, %3$s. Keys reported, then values changed, the rarest first:</string>
|
||||
<string name="label_signal_recorder">Signal recorder</string>
|
||||
<string name="signal_recorder_hint">Records every value the head unit offers for 3 minutes, plus the steering-wheel keys it reports, and lists what changed, the rarest first. To find a signal: start it, note the times you switch the feature on and off or change it, and look for the values that changed then. With a USB stick plugged in, the whole recording is saved on it too (Android/data/…/files).</string>
|
||||
<string name="btn_signal_record">Record 3 minutes</string>
|
||||
<string name="btn_signal_stop">Stop recording</string>
|
||||
<string name="signal_recorder_running">Recording: %1$d:%2$02d of 3:00, %3$d captures. Keys reported, then values changed so far, the rarest first:</string>
|
||||
<string name="signal_recorder_done">Recording from %1$s: %2$d captures, %3$s. Keys reported, then values changed, the rarest first:</string>
|
||||
<string name="call_log_empty">No upload attempted yet.</string>
|
||||
<string name="update_log_title">Updates, newest first:</string>
|
||||
<string name="cruise_log_title">Adaptive cruise control as the car reports it, raw, newest first: a line when a mode or the target changes, and every 2 s while driving with this app open. cc target speed, ica assist mode, tja traffic jam assist, sas/sam speed assist, obj/dist/rel the car ahead (presence, distance, relative speed).\nNow: %1$s</string>
|
||||
<string name="btn_flush_now">Upload now</string>
|
||||
<string name="status_starting">Starting…</string>
|
||||
<string name="status_running_no_upload">Running — waiting for first upload</string>
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
package com.evsuite.abrp;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/** The cruise control debug log's lines and when it writes one — pure JVM. */
|
||||
public class CruiseLogTest {
|
||||
|
||||
private static List<String[]> readings(String cc, String ica, String dist) {
|
||||
return Arrays.asList(new String[] { "cc", cc }, new String[] { "ica", ica },
|
||||
new String[] { "dist", dist });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lineHasEveryValue() {
|
||||
assertEquals("cc 120.00 ica 1 dist 35.20", CruiseLog.values(readings("120.00", "1", "35.20")));
|
||||
assertEquals("cc unread (not available) ica 0 dist unread",
|
||||
CruiseLog.values(readings("unread (not available)", "0", "unread")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void followedObjectAloneIsNoChange() {
|
||||
assertEquals(CruiseLog.steady(readings("120.00", "1", "35.20")),
|
||||
CruiseLog.steady(readings("120.00", "1", "12.00")));
|
||||
assertFalse(CruiseLog.steady(readings("120.00", "1", "1")).contains("dist"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writesOnChangeAndEveryTwoSecondsWhileDriving() {
|
||||
String a = CruiseLog.steady(readings("120.00", "1", "35"));
|
||||
String b = CruiseLog.steady(readings("110.00", "1", "35"));
|
||||
assertTrue(CruiseLog.due(a, null, 0, 0, false));
|
||||
assertTrue(CruiseLog.due(b, a, 1000, 500, false));
|
||||
assertFalse(CruiseLog.due(a, a, 1000, 500, true));
|
||||
assertTrue(CruiseLog.due(a, a, 2500, 500, true));
|
||||
// Parked: nothing new, nothing written.
|
||||
assertFalse(CruiseLog.due(a, a, 60_000, 500, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void drivingIsDOrROrMoving() {
|
||||
assertTrue(CruiseLog.driving(null, 4));
|
||||
assertTrue(CruiseLog.driving(0f, 2));
|
||||
assertTrue(CruiseLog.driving(30f, 3));
|
||||
assertFalse(CruiseLog.driving(0f, 1));
|
||||
assertFalse(CruiseLog.driving(null, null));
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -9,20 +9,20 @@ import java.util.TreeMap;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/** The ACC change recorder's bookkeeping — pure JVM. */
|
||||
public class AccRecordingTest {
|
||||
/** The signal recorder's bookkeeping — pure JVM. */
|
||||
public class SignalRecordingTest {
|
||||
|
||||
private static Map<String, String> capture(String speed, String acc) {
|
||||
private static Map<String, String> capture(String speed, String feature) {
|
||||
Map<String, String> m = new TreeMap<>();
|
||||
m.put("speed", speed);
|
||||
m.put("acc", acc);
|
||||
m.put("feature", feature);
|
||||
m.put("steady", "1");
|
||||
return m;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rarestChangeFirst() {
|
||||
AccRecording r = new AccRecording();
|
||||
SignalRecording r = new SignalRecording();
|
||||
r.add(0, capture("10", "0"));
|
||||
r.add(2000, capture("20", "0"));
|
||||
r.add(4000, capture("30", "120"));
|
||||
@@ -30,14 +30,14 @@ public class AccRecordingTest {
|
||||
r.add(8000, capture("50", "0"));
|
||||
List<String> summary = r.summary();
|
||||
assertEquals(2, summary.size());
|
||||
assertEquals("acc 2× 0 → 120", summary.get(0));
|
||||
assertEquals("feature 2× 0 → 120", summary.get(0));
|
||||
assertTrue(summary.get(1).startsWith("speed 4× 10 → 20 → 30 → 40 → 50"));
|
||||
assertEquals(5, r.captures());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appearingAndVanishingKeysAreChanges() {
|
||||
AccRecording r = new AccRecording();
|
||||
SignalRecording r = new SignalRecording();
|
||||
Map<String, String> a = capture("1", "0");
|
||||
Map<String, String> b = capture("1", "0");
|
||||
b.put("new", "5");
|
||||
@@ -50,13 +50,13 @@ public class AccRecordingTest {
|
||||
@Test
|
||||
public void longValuesAreShortened() {
|
||||
String v = "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]";
|
||||
assertEquals(AccRecording.VALUE_CHARS, AccRecording.shorten(v).length());
|
||||
assertEquals("short", AccRecording.shorten("short"));
|
||||
assertEquals(SignalRecording.VALUE_CHARS, SignalRecording.shorten(v).length());
|
||||
assertEquals("short", SignalRecording.shorten("short"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstCaptureIsKeptWhole() {
|
||||
AccRecording r = new AccRecording();
|
||||
SignalRecording r = new SignalRecording();
|
||||
r.add(0, capture("1", "0"));
|
||||
r.note(500, "key 287 down");
|
||||
assertEquals(5, r.lines().size());
|
||||
Reference in New Issue
Block a user