I did the backstroke in college

This commit is contained in:
firestar5683
2026-06-16 21:19:57 -05:00
parent 6f907e2060
commit 6b0e5aed24
8 changed files with 227 additions and 29 deletions
+2 -2
View File
@@ -28,11 +28,11 @@ AUTO_HOLD_VOLT_CARS = {
CAR.CHEVROLET_VOLT_ASCM,
CAR.CHEVROLET_VOLT_CAMERA,
}
AUTO_HOLD_DRIVE_GEARS = {
AUTO_HOLD_DRIVE_GEARS = (
GearShifter.drive,
GearShifter.low,
GearShifter.manumatic,
}
)
AUTO_HOLD_MIN_BRAKE = 80
AUTO_HOLD_MAX_BRAKE = 240
AUTO_HOLD_MIN_DRIVE_TIME_S = 3.0
@@ -2,6 +2,8 @@ import sys
import types
from types import SimpleNamespace
from opendbc.car import structs
fake_interfaces = types.ModuleType("opendbc.car.interfaces")
@@ -33,6 +35,7 @@ fake_testing_grounds.testing_ground = SimpleNamespace(use_1=False)
sys.modules.setdefault("openpilot.starpilot.common.testing_grounds", fake_testing_grounds)
from opendbc.car.gm.carcontroller import (
AUTO_HOLD_DRIVE_GEARS,
CarController,
estimate_auto_hold_brake,
get_adas_keepalive_step,
@@ -220,6 +223,13 @@ def test_auto_hold_brake_estimate_uses_driver_or_op_brake_and_clamps():
assert estimate_auto_hold_brake(100.0, 400.0) == 240
def test_auto_hold_drive_gears_accept_capnp_dynamic_enum_membership():
msg = structs.CarState.new_message()
msg.gearShifter = structs.CarState.GearShifter.drive
assert msg.gearShifter in AUTO_HOLD_DRIVE_GEARS
def test_auto_hold_activation_allows_direct_entry_from_stopped_brake_press():
assert should_activate_auto_hold(
True,
@@ -35,10 +35,18 @@ def make_cem(*, model_length: float, model_stopped: bool = False, tracking_lead:
model_length=model_length,
model_stopped=model_stopped,
tracking_lead=tracking_lead,
starpilot_vcruise=SimpleNamespace(stop_sign_confirmed=stop_sign_confirmed, forcing_stop=forcing_stop),
starpilot_vcruise=SimpleNamespace(
stop_sign_confirmed=stop_sign_confirmed,
forcing_stop=forcing_stop,
slc=SimpleNamespace(experimental_mode=False),
),
starpilot_following=SimpleNamespace(slower_lead=False, following_lead=False),
lead_one=SimpleNamespace(status=lead_status, dRel=lead_d_rel, vLead=lead_v_lead,
modelProb=lead_model_prob, radar=lead_radar),
road_curvature_detected=False,
driving_in_curve=False,
lane_width_left=0.0,
lane_width_right=0.0,
)
return ConditionalExperimentalMode(planner)
@@ -368,6 +376,35 @@ def test_standstill_update_can_activate_exp_from_red_light_detection(monkeypatch
assert cem.status_value == conditional_experimental_mode_module.CEStatus["STOP_LIGHT"]
def test_first_post_standstill_pullaway_frame_does_not_blip_into_exp(monkeypatch):
cem = make_cem(
model_length=70.0,
tracking_lead=True,
lead_status=True,
lead_d_rel=8.4,
lead_v_lead=2.7,
lead_model_prob=0.999,
)
toggles = make_update_toggles()
toggles.conditional_lead = True
toggles.conditional_slower_lead = True
toggles.conditional_stopped_lead = True
standstill_sm = make_update_sm(standstill=True)
moving_sm = make_update_sm(standstill=False)
now = [100.0]
monkeypatch.setattr(conditional_experimental_mode_module.time, "monotonic", lambda: now[0])
cem.update(0.0, standstill_sm, toggles)
assert not cem.experimental_mode
now[0] = 100.1
cem.update(0.12, moving_sm, toggles)
assert not cem.experimental_mode
assert cem.status_value == conditional_experimental_mode_module.CEStatus["OFF"]
def test_standstill_update_can_activate_exp_from_dashboard_stop_sign(monkeypatch):
cem = make_cem(model_length=80.0, model_stopped=False)
toggles = make_update_toggles()
@@ -120,8 +120,10 @@ class ConditionalExperimentalMode:
if triggered:
self.mode_hold_until = now + self.CEM_TRANSITION_GUARD_TIME
self.mode_false_since = 0.0
elif self.mode_false_since == 0.0:
elif self.prev_experimental_mode and self.mode_false_since == 0.0:
self.mode_false_since = now
elif not self.prev_experimental_mode:
self.mode_false_since = 0.0
hold_active = now < self.mode_hold_until
transition_buffer_active = self.mode_false_since != 0.0 and (now - self.mode_false_since) < self.CEM_TRANSITION_BUFFER_TIME
@@ -403,7 +403,7 @@
border-top: 1px solid rgba(255, 255, 255, 0.08);
display: grid;
gap: 1rem;
grid-template-columns: minmax(170px, 1.3fr) minmax(230px, 1fr) minmax(210px, 1fr) minmax(180px, 0.9fr);
grid-template-columns: minmax(170px, 1.35fr) minmax(170px, 0.8fr) minmax(210px, 1fr) minmax(180px, 0.9fr);
min-width: 0;
padding: 0.9rem 0;
}
@@ -412,6 +412,10 @@
border-top: 0;
}
.dashboard-drive-row.is-pending {
color: var(--dashboard-muted);
}
.dashboard-drive-main,
.dashboard-drive-details,
.dashboard-attention,
@@ -110,6 +110,7 @@ function fallbackDashboard(data, unit) {
segmentCount: 0,
distractedMoments: 0,
unresponsiveMoments: 0,
attentionKnown: true,
distanceUnit: unit,
speedUnit: unit === "kilometers" ? "kph" : "mph",
},
@@ -144,21 +145,28 @@ function fallbackDashboard(data, unit) {
};
}
function driveStatsReady(drive) {
return drive?.attentionKnown !== false;
}
function renderLastDrive(drive) {
const ready = driveStatsReady(drive);
return `
<section class="dashboard-card dashboard-last-drive">
<div class="dashboard-card-kicker"><span></span>Last drive</div>
<div class="dashboard-drive-date">${escapeHtml(formatDate(drive.date))}</div>
<div class="dashboard-drive-metrics">
<div><strong>${formatOneDecimal(drive.distance)}</strong><span>${escapeHtml(drive.distanceUnit || "miles")}</span></div>
<div><strong>${ready ? formatOneDecimal(drive.distance) : "..."}</strong><span>${ready ? escapeHtml(drive.distanceUnit || "miles") : "analyzing"}</span></div>
<div><strong>${formatDuration(drive.duration)}</strong><span>duration</span></div>
<div><strong>${formatInt(drive.avgSpeed)}</strong><span>${escapeHtml(drive.speedUnit || "mph")} avg</span></div>
<div><strong>${formatPercent(drive.engagedPercent)}</strong><span>engaged</span></div>
<div><strong>${ready ? formatInt(drive.avgSpeed) : "..."}</strong><span>${ready ? `${escapeHtml(drive.speedUnit || "mph")} avg` : "speed"}</span></div>
<div><strong>${ready ? formatPercent(drive.engagedPercent) : "..."}</strong><span>engaged</span></div>
</div>
<div class="dashboard-drive-footer">
<span><i class="bi bi-cpu"></i>${escapeHtml(drive.model || "Unknown model")}</span>
<span><i class="bi bi-eye"></i>${formatInt(drive.distractedMoments)} distracted</span>
<span><i class="bi bi-exclamation-triangle"></i>${formatInt(drive.unresponsiveMoments)} unresponsive</span>
${ready
? `<span><i class="bi bi-eye"></i>${formatInt(drive.distractedMoments)} distracted</span>
<span><i class="bi bi-exclamation-triangle"></i>${formatInt(drive.unresponsiveMoments)} unresponsive</span>`
: `<span><i class="bi bi-hourglass-split"></i>Analyzing stats</span>`}
</div>
</section>
`;
@@ -236,27 +244,30 @@ function renderRecentDrives(drives) {
`;
}
const rows = drives.map(drive => `
<div class="dashboard-drive-row">
const rows = drives.map(drive => {
const ready = driveStatsReady(drive);
return `
<div class="dashboard-drive-row ${ready ? "" : "is-pending"}">
<div class="dashboard-drive-main">
<strong>${escapeHtml(formatDate(drive.date))}</strong>
<span>${escapeHtml(drive.model || "Unknown model")}</span>
</div>
<div class="dashboard-drive-details">
<span>${formatOneDecimal(drive.distance)} ${escapeHtml(drive.distanceUnit || "miles")}</span>
<span>${ready ? `${formatOneDecimal(drive.distance)} ${escapeHtml(drive.distanceUnit || "miles")}` : "Analyzing stats"}</span>
<span>${formatDuration(drive.duration)}</span>
<span>${formatInt(drive.segmentCount)} segments</span>
</div>
<div class="dashboard-attention">
<span>${formatInt(drive.distractedMoments)} distracted</span>
<span>${formatInt(drive.unresponsiveMoments)} unresponsive</span>
${ready
? `<span>${formatInt(drive.distractedMoments)} distracted</span><span>${formatInt(drive.unresponsiveMoments)} unresponsive</span>`
: `<span>Waiting for full route analysis</span>`}
</div>
<div class="dashboard-engaged-cell">
<div class="dashboard-mini-bar"><span style="width:${Math.max(0, Math.min(100, numberValue(drive.engagedPercent)))}%"></span></div>
<strong>${formatPercent(drive.engagedPercent)} engaged</strong>
<div class="dashboard-mini-bar"><span style="width:${ready ? Math.max(0, Math.min(100, numberValue(drive.engagedPercent))) : 0}%"></span></div>
<strong>${ready ? `${formatPercent(drive.engagedPercent)} engaged` : "Pending"}</strong>
</div>
</div>
`).join("");
`;
}).join("");
return `
<section class="dashboard-card dashboard-recent">
@@ -207,6 +207,11 @@ class FakeParams:
self.values[key] = value
class FailingPutParams(FakeParams):
def put(self, key, value):
raise RuntimeError("unknown key")
class FakeMessage:
def __init__(self, kind, log_mono_time, payload):
self._kind = kind
@@ -331,6 +336,20 @@ def test_route_sampling_bounds_analyzed_segments():
assert [segment["num"] for segment in sampled["segments"]] == [0, 4, 9]
def test_route_listing_prefers_segment_candidates_with_logs(tmp_path):
hd_root = tmp_path / "realdata_HD"
standard_root = tmp_path / "realdata"
hd_segment = hd_root / "00000001--abcdef1234--0"
standard_segment = standard_root / "00000001--abcdef1234--0"
hd_segment.mkdir(parents=True)
standard_segment.mkdir(parents=True)
(standard_segment / "qlog.zst").write_bytes(b"")
routes = utilities._list_dashboard_routes([hd_root, standard_root])
assert routes[0]["segments"][0]["path"] == standard_segment
def test_top_models_are_ranked_from_persisted_usage_not_favorites():
params = FakeParams({
"AvailableModels": "orion,vega,atlas,nova",
@@ -439,6 +458,29 @@ def test_persistent_loader_accepts_decoded_param_dict():
assert stats["routes"]["route-1"]["attentionKnown"] is False
def test_dashboard_persistent_stats_fallback_to_file_when_param_put_fails(tmp_path, monkeypatch):
monkeypatch.setattr(utilities, "DASHBOARD_PARAMS_DIR", tmp_path)
params = FailingPutParams()
drive = {
"name": "route-1",
"date": "2026-06-15T08:00:00",
"distanceMeters": 1000.0,
"duration": 60,
"engagedSeconds": 30.0,
"model": "Orion",
"routeModifiedAt": 100,
"attentionKnown": True,
"analysisComplete": True,
}
utilities._update_dashboard_persistent_stats(params, [drive], wall_now=1000)
stats = utilities._load_dashboard_persistent_stats(params)
assert (tmp_path / utilities.DASHBOARD_PERSISTENT_STATS_PARAM).is_file()
assert stats["routes"]["route-1"]["distanceMeters"] == 1000
assert stats["routes"]["route-1"]["analysisComplete"] is True
def test_lightweight_routes_surface_recent_drives_without_log_analysis(monkeypatch):
utilities._invalidate_dashboard_cache()
route_infos = [
@@ -513,6 +555,38 @@ def test_unknown_attention_rows_do_not_reset_persisted_clean_records():
assert records["cleanDriveStreak"]["value"] == "1 drive"
def test_lightweight_route_update_preserves_parsed_duration():
params = FakeParams()
parsed_drive = {
"name": "route-1",
"date": "2026-06-15T08:00:00",
"distanceMeters": 10000.0,
"duration": 58,
"engagedSeconds": 30.0,
"model": "Orion",
"routeModifiedAt": 100,
"attentionKnown": True,
"analysisComplete": True,
}
shell_drive = {
"name": "route-1",
"date": "2026-06-15T08:00:00",
"distanceMeters": 0.0,
"duration": 60,
"engagedSeconds": 0.0,
"model": "Orion",
"routeModifiedAt": 100,
"attentionKnown": False,
"analysisComplete": False,
}
utilities._update_dashboard_persistent_stats(params, [parsed_drive], wall_now=1000)
stats = utilities._update_dashboard_persistent_stats(params, [shell_drive], wall_now=1000)
assert stats["routes"]["route-1"]["duration"] == 58
assert stats["routes"]["route-1"]["analysisComplete"] is True
def test_github_urls_accept_owner_repo_origin():
assert utilities.get_github_changelog_url("owner/repo", "main") == "https://github.com/owner/repo/commits/main/"
assert utilities.get_github_changelog_url("github.com/owner/repo", "main") == "https://github.com/owner/repo/commits/main/"
+70 -10
View File
@@ -61,12 +61,13 @@ DASHBOARD_CACHE_TTL_SECONDS = 5.0
DASHBOARD_ROUTE_SCAN_LIMIT = 24
DASHBOARD_ROUTE_ANALYSIS_LIMIT = 0
DASHBOARD_ANALYSIS_TIME_BUDGET_SECONDS = 0.0
DASHBOARD_BACKGROUND_ROUTE_ANALYSIS_LIMIT = 24
DASHBOARD_BACKGROUND_ROUTE_ANALYSIS_LIMIT = 5
DASHBOARD_RECENT_DRIVE_LIMIT = 5
DASHBOARD_ROUTE_SEGMENT_SAMPLE_LIMIT = 2
DASHBOARD_PERSISTED_ROUTE_LIMIT = 5000
DASHBOARD_PERSIST_MIN_ROUTE_AGE_SECONDS = 120
DASHBOARD_PERSISTENT_STATS_PARAM = "GalaxyDashboardStats"
DASHBOARD_PARAMS_DIR = Path("/data/params/d")
DASHBOARD_ANALYZER_LOG_PATH = "/tmp/galaxy_dashboard_analyzer.log"
DASHBOARD_TOP_MODEL_LIMIT = 3
DASHBOARD_EVENT_DISTRACTED = "promptDriverDistracted"
@@ -633,14 +634,44 @@ def _params_get_bool(params_obj, key):
return value.strip().lower() in ("1", "true", "yes", "on")
def _dashboard_param_file_path(key):
if key != DASHBOARD_PERSISTENT_STATS_PARAM:
return None
return DASHBOARD_PARAMS_DIR / key
def _read_dashboard_param_file(key):
path = _dashboard_param_file_path(key)
if path is None or not path.is_file():
return None
try:
return path.read_text(encoding="utf-8")
except Exception:
return None
def _write_dashboard_param_file(key, value):
path = _dashboard_param_file_path(key)
if path is None:
return False
try:
path.parent.mkdir(parents=True, exist_ok=True)
tmp_path = path.with_name(f".{path.name}.{os.getpid()}.tmp")
tmp_path.write_text(str(value), encoding="utf-8")
os.replace(tmp_path, path)
return True
except Exception:
return False
def _params_put_text(params_obj, key, value):
if params_obj is None:
return False
return _write_dashboard_param_file(key, value)
try:
params_obj.put(key, value)
return True
except Exception:
return False
return _write_dashboard_param_file(key, value)
def _split_csv(value):
@@ -691,6 +722,19 @@ def _segment_mtime(segment_path):
return 0.0
def _segment_has_dashboard_log(segment_path):
path = Path(segment_path)
if not path.is_dir():
return False
return any((path / candidate).is_file() for candidate in ROUTE_TIME_LOG_CANDIDATES)
def _select_dashboard_segment_candidate(candidates):
if not candidates:
return None
return next((candidate for candidate in candidates if _segment_has_dashboard_log(candidate)), candidates[0])
def _list_dashboard_routes(footage_paths, limit=DASHBOARD_ROUTE_SCAN_LIMIT):
routes = {}
@@ -726,7 +770,9 @@ def _list_dashboard_routes(footage_paths, limit=DASHBOARD_ROUTE_SCAN_LIMIT):
for route in routes.values():
segments = []
for segment_num, candidates in sorted(route["segments_by_num"].items()):
segments.append({"num": segment_num, "path": candidates[0]})
selected = _select_dashboard_segment_candidate(candidates)
if selected is not None:
segments.append({"num": segment_num, "path": selected})
if not segments:
continue
@@ -993,6 +1039,7 @@ def _analyze_route_messages(messages, route_info, model_names, is_metric, deadli
"unresponsiveMoments": unresponsive_moments,
"routeModifiedAt": _safe_float(route_info.get("modifiedAt", 0.0), 0.0),
"attentionKnown": True,
"analysisComplete": analysis_segment_count >= segment_count,
}
@@ -1030,6 +1077,8 @@ def _empty_drive(is_metric):
"segmentCount": 0,
"distractedMoments": 0,
"unresponsiveMoments": 0,
"attentionKnown": True,
"analysisComplete": False,
"distanceUnit": "kilometers" if is_metric else "miles",
"speedUnit": "kph" if is_metric else "mph",
}
@@ -1076,6 +1125,7 @@ def _route_shell_drive(route_info, params_obj, model_names, is_metric):
"unresponsiveMoments": 0,
"routeModifiedAt": _safe_float(route_info.get("modifiedAt", 0.0), 0.0),
"attentionKnown": False,
"analysisComplete": False,
}
@@ -1100,6 +1150,7 @@ def _drive_from_persistent_route(route_name, entry, is_metric):
"unresponsiveMoments": max(0, _safe_int(entry.get("unresponsiveMoments", 0), 0)),
"routeModifiedAt": _safe_float(entry.get("modifiedAt", 0.0), 0.0),
"attentionKnown": bool(entry.get("attentionKnown", True)),
"analysisComplete": bool(entry.get("analysisComplete", False)),
}
@@ -1147,7 +1198,7 @@ def _analysis_candidates(route_infos, persistent_stats):
return True
if _safe_float(entry.get("modifiedAt", 0.0), 0.0) < _safe_float(route_info.get("modifiedAt", 0.0), 0.0):
return True
return not bool(entry.get("attentionKnown", True))
return not bool(entry.get("attentionKnown", True)) or not bool(entry.get("analysisComplete", False))
missing = [route_info for route_info in route_infos if needs_analysis(route_info)]
return missing
@@ -1180,9 +1231,10 @@ def warm_dashboard_stats(footage_paths=None):
persistent_stats = _load_dashboard_persistent_stats(params_obj)
candidates = _analysis_candidates(route_infos, persistent_stats)[:DASHBOARD_BACKGROUND_ROUTE_ANALYSIS_LIMIT]
for route_info in candidates:
sampled_route_info = _sample_route_info(route_info)
messages = _iter_route_log_messages(sampled_route_info)
drive = _analyze_route_messages(messages, sampled_route_info, model_names, is_metric)
full_route_info = dict(route_info)
full_route_info["analysisSegmentCount"] = max(0, _safe_int(route_info.get("segmentCount", 0), 0))
messages = _iter_route_log_messages(full_route_info)
drive = _analyze_route_messages(messages, full_route_info, model_names, is_metric)
_update_dashboard_persistent_stats(params_obj, [drive], time.time())
@@ -1541,12 +1593,16 @@ def _normalize_persistent_routes(raw_routes):
"segmentCount": max(0, _safe_int(entry.get("segmentCount", 0), 0)),
"modifiedAt": _safe_float(entry.get("modifiedAt", 0.0), 0.0),
"attentionKnown": bool(entry.get("attentionKnown", True)),
"analysisComplete": bool(entry.get("analysisComplete", False)),
}
return routes
def _load_dashboard_persistent_stats(params_obj):
data = _decode_json_param(_params_get_value(params_obj, DASHBOARD_PERSISTENT_STATS_PARAM, None), {})
raw_data = _read_dashboard_param_file(DASHBOARD_PERSISTENT_STATS_PARAM)
if raw_data is None:
raw_data = _params_get_value(params_obj, DASHBOARD_PERSISTENT_STATS_PARAM, None)
data = _decode_json_param(raw_data, {})
if not isinstance(data, dict):
data = {}
data["version"] = 1
@@ -1771,6 +1827,7 @@ def _update_dashboard_persistent_stats(params_obj, drives, wall_now):
"segmentCount": max(0, _safe_int(drive.get("segmentCount", 0), 0)),
"modifiedAt": _safe_float(drive.get("routeModifiedAt", 0.0), 0.0),
"attentionKnown": attention_known,
"analysisComplete": bool(drive.get("analysisComplete", False)),
}
existing_entry = routes.get(route_name)
if isinstance(existing_entry, dict):
@@ -1782,12 +1839,15 @@ def _update_dashboard_persistent_stats(params_obj, drives, wall_now):
next_entry["clean"] = bool(existing_entry.get("clean", False))
next_entry["undistracted"] = bool(existing_entry.get("undistracted", existing_entry.get("clean", False)))
next_entry["attentionKnown"] = True
next_entry["analysisComplete"] = bool(existing_entry.get("analysisComplete", False))
if not attention_known and existing_current and existing_distance >= next_distance:
next_entry["distanceMeters"] = existing_distance
next_entry["duration"] = max(_safe_int(existing_entry.get("duration", 0), 0), next_entry["duration"])
existing_duration = _safe_int(existing_entry.get("duration", 0), 0)
next_entry["duration"] = existing_duration if existing_attention_known else max(existing_duration, next_entry["duration"])
next_entry["engagedSeconds"] = max(0.0, _safe_float(existing_entry.get("engagedSeconds", 0.0), 0.0))
next_entry["distractedMoments"] = max(0, _safe_int(existing_entry.get("distractedMoments", 0), 0))
next_entry["unresponsiveMoments"] = max(0, _safe_int(existing_entry.get("unresponsiveMoments", 0), 0))
next_entry["analysisComplete"] = bool(existing_entry.get("analysisComplete", False))
if (not model_name or model_name == "Unknown model") and _clean_model_label(existing_entry.get("model", "")):
next_entry["model"] = _clean_model_label(existing_entry.get("model", ""))
next_entry["modelKey"] = canonical_model_key(existing_entry.get("modelKey", "")) or _model_usage_key(next_entry["model"])