Skip to content

Commit a0a16f7

Browse files
malysclaude
andcommitted
feat(chargepilot): the long route stops more than once, and the trip plot
Two sessions' work, committed together because the owner asked for everything and the working tree is entangled — strings.xml, mise.toml and CHANGELOG.md each carry both. CP-062, the multi-leg plan: - `ChargeStopPlan.chain` walks the route leg by leg with the band measured over each leg, so Auzielle → Paris names two stops instead of refusing at 25 %. - A charger is searched per stop, its window bounded by the leg; the request count is worked out before it is spent and shown on the comparison card. - `VehicleSettings.departurePercent` — the charge a stop is planned to leave with, a declared figure and not a curve, carried as `departure_percent`. - `ChargeStopHandoff.of` takes every stop as a waypoint and a leg. - ORS refuses the whole request past 100 km when `alternative_routes` is asked for, error 2004. The request is retried without the block, which is why long destinations rendered nothing at all before. In flight in another session: - The trip plot screen, the destination search rows and the favourites. - `PowerHistoryPolicy` is gone, its readings folded into `DashboardReadings`. - The diagnostics and energy-breakdown screens follow RI-002 and RI-003. The `CP-062` identifier is used in another session's comments for a different feature; the ticket file owns it and the collision is still to arbitrate. Submodule bumped to 7b5d41f. `mise run check`: BUILD SUCCESSFUL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 9db2465 commit a0a16f7

35 files changed

Lines changed: 1280 additions & 281 deletions

‎.gitignore‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ local.properties
1010
*.keystore
1111
mise.local.toml
1212

13+
14+
# Real API keys, the emulator seed. Same secret as the USB stick.
15+
evchargepilot-settings.local.json

‎CHANGELOG.md‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,80 @@ All notable changes follow [Keep a Changelog](https://keepachangelog.com/en/1.1.
66

77
### Added
88

9+
- **The trip trace opens at full size.** In the ledger it is a thumbnail a couple of hundred
10+
pixels tall, and on a panel shorter than the emulator's it is the first thing squeezed. Tap it
11+
and it gets a screen of its own: speed and charge on labelled axes, and a cursor dragged along
12+
the trace that reads back what the car actually held at that moment — elapsed time, speed,
13+
charge, outside temperature, battery power, climate. With no finger on it the row shows the
14+
trip's own extremes instead of six dashes. Nothing here is computed: a gap in the record reads
15+
as a gap, never as a zero.
16+
17+
- **`mise run seed-settings` loads the keys into the emulator.** Validating a routing change cost
18+
a USB stick or a key typed on a touch keyboard. A debug build now applies a settings file
19+
dropped in its own files directory, once, at the next start; the task pushes
20+
`evchargepilot-settings.local.json` (gitignored — it holds keys in clear text) there over adb.
21+
`mise run run` and `run-unstable` do it on every install, and launch without it when the file
22+
is absent. Release builds have no such path: the only way in stays the stick the driver browsed
23+
to.
24+
25+
- **A search result can be kept without going there.** Every row in the destination results now
26+
carries a star beside it: tap the place to drive to it, tap the star to put it in the favourites
27+
and keep searching. Saving used to be a long press, which is an affordance nobody finds on a
28+
panel; the long press still works for a hand that learned it.
29+
30+
- **The diagnostics capture reports the panel.** A `[panel]` section records what the resource
31+
system and the window manager actually see — the dp the layouts are written in, the density, the
32+
font scale, the pixels, and the insets the platform reserves — so the next layout question about
33+
this screen is settled by a capture instead of by a guess from a desk.
34+
35+
- **The probe asks the property channel, the charging counters and the platform's diagnostics.**
36+
Three unblocked tickets' worth of questions in one capture: the typed property channel reached
37+
by name rather than by reflection, asked for eight ids including the two the energy screen has
38+
no source for; the charging service's own energy counters *with the validity flag the vehicle
39+
publishes beside each one*, which is what can finally settle the unit nobody could prove; and
40+
whether the platform's own diagnostic service is reachable here at all, so the OBD question has
41+
a recorded answer rather than an assumed one. Reads only — no transaction that starts, stops or
42+
limits a charge, or writes a property, is named anywhere in the probe, and a test asserts it
43+
against the full write set harvested from each interface.
44+
45+
- **The probe bundle asks three more questions.** The pack's voltage and current are reported by
46+
name, so a capture says which of the two answered rather than only that their product did not;
47+
and the one remaining in-car temperature candidate the firmware declares is probed rather than
48+
read, because guessing wrong would put the outside temperature in the cabin tile.
49+
50+
### Changed
51+
52+
- **The trip screen no longer assumes the emulator's height.** The car has no system bars but its
53+
per-app DPI is the driver's to set, and a taller density buys fewer dp — so the real panel is
54+
shorter than the emulator and the trip readouts came out squashed. The readout rows now take a
55+
share of whatever height the panel has, with the tile size as a floor rather than a fixed slice,
56+
and the trace keeps the largest share. No height bucket decides it: a `-hXXXdp` qualifier only
57+
moves the guess, and a panel on the wrong side of the threshold gets the layout meant for the
58+
other one.
59+
60+
- **The speed comparison and the energy breakdown can train again.** Both rest on a local model,
61+
and the model refused to train because no drive has ever confirmed a battery-power conversion on
62+
this firmware — so on the only car this project runs on, they were empty by construction. The
63+
model now trains on the pack pair the dashboard already derives its power from, and carries a
64+
separate interpretation stamp saying so. Nothing is relabelled as measured: the figure on the
65+
dashboard stays derived, the model's own predictions stay estimates with a band, and the day a
66+
validated conversion arrives the stamp changes and the derived model is retrained from scratch
67+
rather than quietly surviving underneath it.
68+
69+
- **Battery power is shown where the car publishes it, labelled for what it is.** On this firmware
70+
the standard power property never answers, so instantaneous power, consumption, the trip's
71+
energy and the adaptive range were all blank. The pack's own voltage and current are readable
72+
and multiply out to watts, so they are read and published as a derived figure — arithmetic,
73+
drawn differently from a vehicle reading, and still barred from training any model: a trip
74+
recorded this way carries no power evidence, which is what the models filter on.
75+
76+
- **A trip with no power reading is priced from the pack you declared.** The charge dropped by a
77+
measured number of points and the settings say what a point is worth, so the trip's energy and
78+
its average consumption are estimated from that instead of left empty — with the band that a
79+
declared capacity and a state of health deserve. Regeneration keeps its dash: the gauge cannot
80+
tell energy that came back from energy never spent. The same figure closes the energy breakdown
81+
when neither model can produce one, instead of a screen that only says no.
82+
983
- **Unstable builds now tell you when a newer one exists.** A tester's only way to find out was
1084
to go and look at the releases page. An unstable build now asks the rolling `unstable`
1185
pre-release at start, downloads a newer APK into the download folder and names the file and the

‎app/src/main/AndroidManifest.xml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@
9999
android:exported="false"
100100
android:screenOrientation="landscape" />
101101

102+
<activity
103+
android:name=".TripPlotActivity"
104+
android:exported="false"
105+
android:screenOrientation="landscape" />
106+
102107
<activity
103108
android:name=".SpeedWhatIfActivity"
104109
android:exported="false"

‎app/src/main/java/com/evsuite/chargepilot/ChargePilotApp.kt‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.evsuite.chargepilot
22

33
import android.app.Application
4+
import android.content.pm.ApplicationInfo
45
import com.evsuite.hardware.diag.CrashLogger
6+
import java.io.File
57

68
class ChargePilotApp : Application() {
79
override fun onCreate() {
@@ -10,6 +12,27 @@ class ChargePilotApp : Application() {
1012
// Unstable arms the validation probes here so a drive is recorded without anyone
1113
// opening a screen first; stable does nothing, and contains none of this.
1214
EvidenceCaptureHook.startProbes(this)
15+
if (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0) seedSettings()
16+
}
17+
18+
/**
19+
* Applies settings dropped into this build's own files directory, once, then renames them.
20+
*
21+
* Debug builds only: it is how the emulator gets the driver's keys, favourites and vehicle
22+
* figures without a key typed on a touch keyboard or a USB stick carried back from the car.
23+
* `mise run seed-settings` puts the file there. A release build has no such path — the only
24+
* way in stays the stick the driver browsed to.
25+
*/
26+
private fun seedSettings() {
27+
val seed = File(filesDir, SEED_FILE)
28+
if (!seed.isFile) return
29+
val settings = SettingsTransfer.read(seed)
30+
if (!settings.isEmpty()) SettingsTransfer.apply(this, settings)
31+
seed.renameTo(File(filesDir, "$SEED_FILE.applied"))
32+
}
33+
34+
private companion object {
35+
const val SEED_FILE = "seed-settings.json"
1336
}
1437
}
1538

0 commit comments

Comments
 (0)