pull/36300/head
Shane Smiskol 4 days ago
parent 5feed930bf
commit f076fe893c
  1. 126
      selfdrive/ui/layouts/settings/device.py
  2. 13
      system/ui/widgets/list_view.py

@ -1,8 +1,11 @@
import os import os
import json import json
import math
from cereal import messaging, log
from openpilot.common.basedir import BASEDIR from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params from openpilot.common.params import Params
from openpilot.common.swaglog import cloudlog
from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog from openpilot.selfdrive.ui.onroad.driver_camera_dialog import DriverCameraDialog
from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.ui_state import ui_state
from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide from openpilot.selfdrive.ui.layouts.onboarding import TrainingGuide
@ -20,11 +23,7 @@ from openpilot.system.ui.widgets.scroller import Scroller
DESCRIPTIONS = { DESCRIPTIONS = {
'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.", 'pair_device': "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.",
'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)", 'driver_camera': "Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)",
'reset_calibration': ( 'reset_calibration': "openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down.",
"openpilot requires the device to be mounted within 4° left or right and within 5° " +
"up or 9° down. openpilot is continuously calibrating, resetting is rarely required. " +
"Resetting calibration will restart openpilot if the car is powered on."
),
'review_guide': "Review the rules, features, and limitations of openpilot", 'review_guide': "Review the rules, features, and limitations of openpilot",
} }
@ -50,12 +49,15 @@ class DeviceLayout(Widget):
self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device) self._pair_device_btn = button_item("Pair Device", "PAIR", DESCRIPTIONS['pair_device'], callback=self._pair_device)
self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired()) self._pair_device_btn.set_visible(lambda: not ui_state.prime_state.is_paired())
self._reset_calib_btn = button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt,
description_opened_callback=self._update_calib_description)
items = [ items = [
text_item("Dongle ID", dongle_id), text_item("Dongle ID", dongle_id),
text_item("Serial", serial), text_item("Serial", serial),
self._pair_device_btn, self._pair_device_btn,
button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad), button_item("Driver Camera", "PREVIEW", DESCRIPTIONS['driver_camera'], callback=self._show_driver_camera, enabled=ui_state.is_offroad),
button_item("Reset Calibration", "RESET", DESCRIPTIONS['reset_calibration'], callback=self._reset_calibration_prompt), self._reset_calib_btn,
button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide), button_item("Review Training Guide", "REVIEW", DESCRIPTIONS['review_guide'], self._on_review_training_guide),
regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory), regulatory_btn := button_item("Regulatory", "VIEW", callback=self._on_regulatory),
button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad), button_item("Change Language", "CHANGE", callback=self._show_language_selection, enabled=ui_state.is_offroad),
@ -107,11 +109,121 @@ class DeviceLayout(Widget):
self._params.remove("LiveParametersV2") self._params.remove("LiveParametersV2")
self._params.remove("LiveDelay") self._params.remove("LiveDelay")
self._params.put_bool("OnroadCycleRequested", True) self._params.put_bool("OnroadCycleRequested", True)
# self._update_calib_description() self._update_calib_description()
dialog = ConfirmDialog("Are you sure you want to reset calibration?", "Reset") dialog = ConfirmDialog("Are you sure you want to reset calibration?", "Reset")
gui_app.set_modal_overlay(dialog, callback=reset_calibration) gui_app.set_modal_overlay(dialog, callback=reset_calibration)
def _update_calib_description(self):
"""
QString desc = tr("openpilot requires the device to be mounted within 4° left or right and within 5° up or 9° down.");
std::string calib_bytes = params.get("CalibrationParams");
if (!calib_bytes.empty()) {
try {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(calib_bytes.data(), calib_bytes.size()));
auto calib = cmsg.getRoot<cereal::Event>().getLiveCalibration();
if (calib.getCalStatus() != cereal::LiveCalibrationData::Status::UNCALIBRATED) {
double pitch = calib.getRpyCalib()[1] * (180 / M_PI);
double yaw = calib.getRpyCalib()[2] * (180 / M_PI);
desc += tr(" Your device is pointed %%2 and %%4.")
.arg(QString::number(std::abs(pitch), 'g', 1), pitch > 0 ? tr("down") : tr("up"),
QString::number(std::abs(yaw), 'g', 1), yaw > 0 ? tr("left") : tr("right"));
}
} catch (kj::Exception) {
qInfo() << "invalid CalibrationParams";
}
}
int lag_perc = 0;
std::string lag_bytes = params.get("LiveDelay");
if (!lag_bytes.empty()) {
try {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(lag_bytes.data(), lag_bytes.size()));
lag_perc = cmsg.getRoot<cereal::Event>().getLiveDelay().getCalPerc();
} catch (kj::Exception) {
qInfo() << "invalid LiveDelay";
}
}
if (lag_perc < 100) {
desc += tr("\n\nSteering lag calibration is %1% complete.").arg(lag_perc);
} else {
desc += tr("\n\nSteering lag calibration is complete.");
}
std::string torque_bytes = params.get("LiveTorqueParameters");
if (!torque_bytes.empty()) {
try {
AlignedBuffer aligned_buf;
capnp::FlatArrayMessageReader cmsg(aligned_buf.align(torque_bytes.data(), torque_bytes.size()));
auto torque = cmsg.getRoot<cereal::Event>().getLiveTorqueParameters();
// don't add for non-torque cars
if (torque.getUseParams()) {
int torque_perc = torque.getCalPerc();
if (torque_perc < 100) {
desc += tr(" Steering torque response calibration is %1% complete.").arg(torque_perc);
} else {
desc += tr(" Steering torque response calibration is complete.");
}
}
} catch (kj::Exception) {
qInfo() << "invalid LiveTorqueParameters";
}
}
desc += "\n\n";
desc += tr("openpilot is continuously calibrating, resetting is rarely required. "
"Resetting calibration will restart openpilot if the car is powered on.");
resetCalibBtn->setDescription(desc);
"""
desc = DESCRIPTIONS['reset_calibration']
calib_bytes = self._params.get("CalibrationParams")
if calib_bytes:
try:
calib = messaging.log_from_bytes(calib_bytes, log.Event).liveCalibration
if calib.calStatus != log.LiveCalibrationData.Status.uncalibrated:
pitch = math.degrees(calib.rpyCalib[1])
yaw = math.degrees(calib.rpyCalib[2])
desc += f" Your device is pointed {abs(pitch):.1f}° {'down' if pitch > 0 else 'up'} and {abs(yaw):.1f}° {'left' if yaw > 0 else 'right'}."
except Exception:
cloudlog.exception("invalid CalibrationParams")
lag_perc = 0
lag_bytes = self._params.get("LiveDelay")
if lag_bytes:
try:
lag_perc = messaging.log_from_bytes(lag_bytes, log.Event).liveDelay.calPerc
except Exception:
cloudlog.exception("invalid LiveDelay")
if lag_perc < 100:
desc += f"<br><br>Steering lag calibration is {lag_perc}% complete."
else:
desc += "<br><br>Steering lag calibration is complete."
torque_bytes = self._params.get("LiveTorqueParameters")
if torque_bytes:
try:
torque = messaging.log_from_bytes(torque_bytes, log.Event).liveTorqueParameters
# don't add for non-torque cars
if torque.useParams:
torque_perc = torque.calPerc
if torque_perc < 100:
desc += f" Steering torque response calibration is {torque_perc}% complete."
else:
desc += " Steering torque response calibration is complete."
except Exception:
cloudlog.exception("invalid LiveTorqueParameters")
desc += "<br><br>"
desc += ("openpilot is continuously calibrating, resetting is rarely required. " +
"Resetting calibration will restart openpilot if the car is powered on.")
self._reset_calib_btn.set_description(desc)
def _reboot_prompt(self): def _reboot_prompt(self):
if ui_state.engaged: if ui_state.engaged:
gui_app.set_modal_overlay(alert_dialog("Disengage to Reboot")) gui_app.set_modal_overlay(alert_dialog("Disengage to Reboot"))

@ -260,7 +260,7 @@ class MultipleButtonAction(ItemAction):
class ListItem(Widget): class ListItem(Widget):
def __init__(self, title: str = "", icon: str | None = None, description: str | Callable[[], str] | None = None, def __init__(self, title: str = "", icon: str | None = None, description: str | Callable[[], str] | None = None,
description_visible: bool = False, callback: Callable | None = None, description_visible: bool = False, callback: Callable | None = None, description_opened_callback: Callable | None = None,
action_item: ItemAction | None = None): action_item: ItemAction | None = None):
super().__init__() super().__init__()
self.title = title self.title = title
@ -268,6 +268,7 @@ class ListItem(Widget):
self._description = description self._description = description
self.description_visible = description_visible self.description_visible = description_visible
self.callback = callback self.callback = callback
self.description_opened_callback = description_opened_callback
self.action_item = action_item self.action_item = action_item
self.set_rect(rl.Rectangle(0, 0, ITEM_BASE_WIDTH, ITEM_BASE_HEIGHT)) self.set_rect(rl.Rectangle(0, 0, ITEM_BASE_WIDTH, ITEM_BASE_HEIGHT))
@ -302,6 +303,10 @@ class ListItem(Widget):
if self.description: if self.description:
self.description_visible = not self.description_visible self.description_visible = not self.description_visible
# do callback first in case consumer changes description
if self.description_visible and self.description_opened_callback:
self.description_opened_callback()
content_width = int(self._rect.width - ITEM_PADDING * 2) content_width = int(self._rect.width - ITEM_PADDING * 2)
self._rect.height = self.get_item_height(self._font, content_width) self._rect.height = self.get_item_height(self._font, content_width)
@ -411,9 +416,11 @@ def toggle_item(title: str, description: str | Callable[[], str] | None = None,
def button_item(title: str, button_text: str | Callable[[], str], description: str | Callable[[], str] | None = None, def button_item(title: str, button_text: str | Callable[[], str], description: str | Callable[[], str] | None = None,
callback: Callable | None = None, enabled: bool | Callable[[], bool] = True) -> ListItem: callback: Callable | None = None, description_opened_callback: Callable | None = None,
enabled: bool | Callable[[], bool] = True) -> ListItem:
action = ButtonAction(text=button_text, enabled=enabled) action = ButtonAction(text=button_text, enabled=enabled)
return ListItem(title=title, description=description, action_item=action, callback=callback) return ListItem(title=title, description=description, action_item=action, callback=callback,
description_opened_callback=description_opened_callback)
def text_item(title: str, value: str | Callable[[], str], description: str | Callable[[], str] | None = None, def text_item(title: str, value: str | Callable[[], str], description: str | Callable[[], str] | None = None,

Loading…
Cancel
Save