diff --git a/.github/workflows/selfdrive_tests.yaml b/.github/workflows/selfdrive_tests.yaml index f5edfe5929..0ff0092b02 100644 --- a/.github/workflows/selfdrive_tests.yaml +++ b/.github/workflows/selfdrive_tests.yaml @@ -307,6 +307,7 @@ jobs: $UNIT_TEST tools/lib/tests && \ ./selfdrive/ui/tests/create_test_translations.sh && \ QT_QPA_PLATFORM=offscreen ./selfdrive/ui/tests/test_translations && \ + ./selfdrive/ui/tests/test_translations.py && \ ./common/tests/test_util && \ ./common/tests/test_swaglog && \ ./selfdrive/boardd/tests/test_boardd_usbprotocol && \ diff --git a/common/swaglog.cc b/common/swaglog.cc index 6b0028326a..22682dc54c 100644 --- a/common/swaglog.cc +++ b/common/swaglog.cc @@ -66,8 +66,9 @@ static void log(int levelnum, const char* filename, int lineno, const char* func char levelnum_c = levelnum; zmq_send(s.sock, (levelnum_c + log_s).c_str(), log_s.length() + 1, ZMQ_NOBLOCK); } + static void cloudlog_common(int levelnum, const char* filename, int lineno, const char* func, - char* msg_buf, json11::Json::object msg_j={}) { + char* msg_buf, const json11::Json::object &msg_j={}) { std::lock_guard lk(s.lock); if (!s.initialized) s.initialize(); diff --git a/release/files_common b/release/files_common index 7e8dbd37a6..acf74e2137 100644 --- a/release/files_common +++ b/release/files_common @@ -60,6 +60,8 @@ release/* tools/lib/* tools/joystick/* +tools/replay/*.cc +tools/replay/*.h selfdrive/__init__.py selfdrive/sentry.py @@ -287,6 +289,7 @@ selfdrive/ui/soundd/*.cc selfdrive/ui/soundd/*.h selfdrive/ui/soundd/soundd selfdrive/ui/soundd/.gitignore +selfdrive/ui/translations/* selfdrive/ui/qt/*.cc selfdrive/ui/qt/*.h @@ -295,10 +298,6 @@ selfdrive/ui/qt/offroad/*.h selfdrive/ui/qt/offroad/*.qml selfdrive/ui/qt/widgets/*.cc selfdrive/ui/qt/widgets/*.h - -tools/replay/*.cc -tools/replay/*.h - selfdrive/ui/qt/maps/*.cc selfdrive/ui/qt/maps/*.h diff --git a/selfdrive/car/honda/carcontroller.py b/selfdrive/car/honda/carcontroller.py index dcf7b05a04..d47caaa9ad 100644 --- a/selfdrive/car/honda/carcontroller.py +++ b/selfdrive/car/honda/carcontroller.py @@ -241,7 +241,7 @@ class CarController: idx = (self.frame // 10) % 4 hud = HUDData(int(pcm_accel), int(round(hud_v_cruise)), hud_control.leadVisible, hud_control.lanesVisible, fcw_display, acc_alert, steer_required) - can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, idx, CS.stock_hud, self.frame)) + can_sends.extend(hondacan.create_ui_commands(self.packer, self.CP, CC.enabled, pcm_speed, hud, CS.is_metric, idx, CS.stock_hud)) if self.CP.openpilotLongitudinalControl and self.CP.carFingerprint not in HONDA_BOSCH: self.speed = pcm_speed diff --git a/selfdrive/car/honda/hondacan.py b/selfdrive/car/honda/hondacan.py index 5de29b4f37..7246b98686 100644 --- a/selfdrive/car/honda/hondacan.py +++ b/selfdrive/car/honda/hondacan.py @@ -101,7 +101,7 @@ def create_bosch_supplemental_1(packer, car_fingerprint, idx): return packer.make_can_msg("BOSCH_SUPPLEMENTAL_1", bus, values, idx) -def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, idx, stock_hud, frame): +def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, idx, stock_hud): commands = [] bus_pt = get_pt_bus(CP.carFingerprint) radar_disabled = CP.carFingerprint in HONDA_BOSCH and CP.openpilotLongitudinalControl @@ -141,8 +141,6 @@ def create_ui_commands(packer, CP, enabled, pcm_speed, hud, is_metric, idx, stoc if CP.carFingerprint in HONDA_BOSCH_RADARLESS: lkas_hud_values['LANE_LINES'] = 3 lkas_hud_values['DASHED_LANES'] = hud.lanes_visible - # TODO: understand this better, does car need to see it fall after start up? - lkas_hud_values['LKAS_PROBLEM'] = 0 if frame > 200 else 1 if not (CP.flags & HondaFlags.BOSCH_EXT_HUD): lkas_hud_values['SET_ME_X48'] = 0x48 diff --git a/selfdrive/common/tests/.gitignore b/selfdrive/common/tests/.gitignore deleted file mode 100644 index 1350b3b825..0000000000 --- a/selfdrive/common/tests/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -test_util -test_swaglog diff --git a/selfdrive/locationd/laikad.py b/selfdrive/locationd/laikad.py index 5098b25d38..d51ac10816 100755 --- a/selfdrive/locationd/laikad.py +++ b/selfdrive/locationd/laikad.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import json +import math import os import time from collections import defaultdict @@ -29,11 +30,12 @@ CACHE_VERSION = 0.1 class Laikad: - def __init__(self, valid_const=("GPS", "GLONASS"), auto_update=False, valid_ephem_types=(EphemerisType.ULTRA_RAPID_ORBIT, EphemerisType.NAV), + def __init__(self, valid_const=("GPS", "GLONASS"), auto_fetch_orbits=True, auto_update=False, valid_ephem_types=(EphemerisType.ULTRA_RAPID_ORBIT, EphemerisType.NAV), save_ephemeris=False, last_known_position=None): self.astro_dog = AstroDog(valid_const=valid_const, auto_update=auto_update, valid_ephem_types=valid_ephem_types, clear_old_ephemeris=True) self.gnss_kf = GNSSKalman(GENERATED_DIR, cython=True) + self.auto_fetch_orbits = auto_fetch_orbits self.orbit_fetch_executor: Optional[ProcessPoolExecutor] = None self.orbit_fetch_future: Optional[Future] = None @@ -41,6 +43,7 @@ class Laikad: self.last_cached_t = None self.save_ephemeris = save_ephemeris self.load_cache() + self.posfix_functions = {constellation: get_posfix_sympy_fun(constellation) for constellation in (ConstellationId.GPS, ConstellationId.GLONASS)} self.last_pos_fix = last_known_position if last_known_position is not None else [] self.last_pos_residual = [] @@ -85,7 +88,8 @@ class Laikad: report = ublox_msg.measurementReport if report.gpsWeek > 0: latest_msg_t = GPSTime(report.gpsWeek, report.rcvTow) - self.fetch_orbits(latest_msg_t + SECS_IN_MIN, block) + if self.auto_fetch_orbits: + self.fetch_orbits(latest_msg_t + SECS_IN_MIN, block) new_meas = read_raw_ublox(report) processed_measurements = process_measurements(new_meas, self.astro_dog) @@ -146,8 +150,8 @@ class Laikad: def kf_valid(self, t: float) -> List[bool]: filter_time = self.gnss_kf.filter.get_filter_time() - return [filter_time is not None, - filter_time is not None and abs(t - filter_time) < MAX_TIME_GAP, + return [not math.isnan(filter_time), + abs(t - filter_time) < MAX_TIME_GAP, all(np.isfinite(self.gnss_kf.x[GStates.ECEF_POS]))] def init_gnss_localizer(self, est_pos): @@ -275,7 +279,8 @@ def main(sm=None, pm=None): replay = "REPLAY" in os.environ # todo get last_known_position - laikad = Laikad(save_ephemeris=not replay) + use_internet = "LAIKAD_NO_INTERNET" not in os.environ + laikad = Laikad(save_ephemeris=not replay, auto_fetch_orbits=use_internet) while True: sm.update() diff --git a/selfdrive/test/process_replay/process_replay.py b/selfdrive/test/process_replay/process_replay.py index def61a10a1..fc83b4b61f 100755 --- a/selfdrive/test/process_replay/process_replay.py +++ b/selfdrive/test/process_replay/process_replay.py @@ -27,14 +27,14 @@ TIMEOUT = 15 PROC_REPLAY_DIR = os.path.dirname(os.path.abspath(__file__)) FAKEDATA = os.path.join(PROC_REPLAY_DIR, "fakedata/") -ProcessConfig = namedtuple('ProcessConfig', ['proc_name', 'pub_sub', 'ignore', 'init_callback', 'should_recv_callback', 'tolerance', 'fake_pubsubmaster', 'submaster_config'], defaults=({},)) +ProcessConfig = namedtuple('ProcessConfig', ['proc_name', 'pub_sub', 'ignore', 'init_callback', 'should_recv_callback', 'tolerance', 'fake_pubsubmaster', 'submaster_config', 'environ', 'subtest_name'], defaults=({}, {}, "")) def wait_for_event(evt): if not evt.wait(TIMEOUT): if threading.currentThread().getName() == "MainThread": # tested process likely died. don't let test just hang - raise Exception("Timeout reached. Tested process likely crashed.") + raise Exception(f"Timeout reached. Tested process {os.environ['PROC_NAME']} likely crashed.") else: # done testing this process, let it die sys.exit(0) @@ -190,6 +190,7 @@ def get_car_params(msgs, fsm, can_sock, fingerprint): _, CP = get_car(can, sendcan) Params().put("CarParams", CP.to_bytes()) + def controlsd_rcv_callback(msg, CP, cfg, fsm): # no sendcan until controlsd is initialized socks = [s for s in cfg.pub_sub[msg.which()] if @@ -198,6 +199,7 @@ def controlsd_rcv_callback(msg, CP, cfg, fsm): socks.remove("sendcan") return socks, len(socks) > 0 + def radar_rcv_callback(msg, CP, cfg, fsm): if msg.which() != "can": return [], False @@ -240,7 +242,7 @@ def laika_rcv_callback(msg, CP, cfg, fsm): if msg.ubloxGnss.which() == "measurementReport": return ["gnssMeasurements"], True else: - return [], False + return [], True CONFIGS = [ @@ -345,6 +347,19 @@ CONFIGS = [ tolerance=None, fake_pubsubmaster=False, ), + ProcessConfig( + proc_name="laikad", + subtest_name="Offline", + pub_sub={ + "ubloxGnss": ["gnssMeasurements"], + }, + ignore=["logMonoTime"], + init_callback=get_car_params, + should_recv_callback=laika_rcv_callback, + tolerance=NUMPY_TOLERANCE, + fake_pubsubmaster=True, + environ={"LAIKAD_NO_INTERNET": "1"}, + ), ProcessConfig( proc_name="laikad", pub_sub={ @@ -366,7 +381,8 @@ def replay_process(cfg, lr, fingerprint=None): else: return cpp_replay_process(cfg, lr, fingerprint) -def setup_env(simulation=False, CP=None): + +def setup_env(simulation=False, CP=None, cfg=None): params = Params() params.clear_all() params.put_bool("OpenpilotEnabledToggle", True) @@ -380,6 +396,16 @@ def setup_env(simulation=False, CP=None): os.environ['SKIP_FW_QUERY'] = "" os.environ['FINGERPRINT'] = "" + if cfg is not None: + # Clear all custom processConfig environment variables + for cfg in CONFIGS: + for k, _ in cfg.environ.items(): + if k in os.environ: + del os.environ[k] + + os.environ.update(cfg.environ) + os.environ['PROC_NAME'] = cfg.proc_name + if simulation: os.environ["SIMULATION"] = "1" elif "SIMULATION" in os.environ: @@ -396,6 +422,7 @@ def setup_env(simulation=False, CP=None): os.environ['SKIP_FW_QUERY'] = "1" os.environ['FINGERPRINT'] = CP.carFingerprint + def python_replay_process(cfg, lr, fingerprint=None): sub_sockets = [s for _, sub in cfg.pub_sub.items() for s in sub] pub_sockets = [s for s in cfg.pub_sub.keys() if s != 'can'] @@ -413,10 +440,10 @@ def python_replay_process(cfg, lr, fingerprint=None): if fingerprint is not None: os.environ['SKIP_FW_QUERY'] = "1" os.environ['FINGERPRINT'] = fingerprint - setup_env() + setup_env(cfg=cfg) else: CP = [m for m in lr if m.which() == 'carParams'][0].carParams - setup_env(CP=CP) + setup_env(CP=CP, cfg=cfg) assert(type(managed_processes[cfg.proc_name]) is PythonProcess) managed_processes[cfg.proc_name].prepare() @@ -477,7 +504,7 @@ def cpp_replay_process(cfg, lr, fingerprint=None): log_msgs = [] # We need to fake SubMaster alive since we can't inject a fake clock - setup_env(simulation=True) + setup_env(simulation=True, cfg=cfg) managed_processes[cfg.proc_name].prepare() managed_processes[cfg.proc_name].start() diff --git a/selfdrive/test/process_replay/ref_commit b/selfdrive/test/process_replay/ref_commit index 946ddce19e..c3e8eca42d 100644 --- a/selfdrive/test/process_replay/ref_commit +++ b/selfdrive/test/process_replay/ref_commit @@ -1 +1 @@ -a0b5ce7b2e0b9c073e51ac8908402d53e1d99722 \ No newline at end of file +a9adebff7ce27d6233d443217a30337b761898ee \ No newline at end of file diff --git a/selfdrive/test/process_replay/test_processes.py b/selfdrive/test/process_replay/test_processes.py index 25fbd210cc..4e7ba4a6dd 100755 --- a/selfdrive/test/process_replay/test_processes.py +++ b/selfdrive/test/process_replay/test_processes.py @@ -200,11 +200,11 @@ if __name__ == "__main__": if cfg.proc_name not in tested_procs: continue - cur_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{cur_commit}.bz2") + cur_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}{cfg.subtest_name}_{cur_commit}.bz2") if args.update_refs: # reference logs will not exist if routes were just regenerated ref_log_path = get_url(*segment.rsplit("--", 1)) else: - ref_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}_{ref_commit}.bz2") + ref_log_fn = os.path.join(FAKEDATA, f"{segment}_{cfg.proc_name}{cfg.subtest_name}_{ref_commit}.bz2") ref_log_path = ref_log_fn if os.path.exists(ref_log_fn) else BASE_URL + os.path.basename(ref_log_fn) dat = None if args.upload_only else log_data[segment] diff --git a/selfdrive/test/test_onroad.py b/selfdrive/test/test_onroad.py index f10c79313b..a7ab55fe2a 100755 --- a/selfdrive/test/test_onroad.py +++ b/selfdrive/test/test_onroad.py @@ -24,7 +24,7 @@ PROCS = { "selfdrive.controls.controlsd": 35.0, "./loggerd": 10.0, "./encoderd": 12.5, - "./camerad": 16.5, + "./camerad": 14.5, "./locationd": 9.1, "selfdrive.controls.plannerd": 11.7, "./_ui": 19.2, diff --git a/selfdrive/ui/qt/offroad/onboarding.cc b/selfdrive/ui/qt/offroad/onboarding.cc index 77e84293b2..f3e50b572b 100644 --- a/selfdrive/ui/qt/offroad/onboarding.cc +++ b/selfdrive/ui/qt/offroad/onboarding.cc @@ -151,7 +151,7 @@ void DeclinePage::showEvent(QShowEvent *event) { QObject::connect(back_btn, &QPushButton::clicked, this, &DeclinePage::getBack); - QPushButton *uninstall_btn = new QPushButton(QString(tr("Decline, uninstall %1")).arg(getBrand())); + QPushButton *uninstall_btn = new QPushButton(tr("Decline, uninstall %1").arg(getBrand())); uninstall_btn->setStyleSheet("background-color: #B73D3D"); buttons->addWidget(uninstall_btn); QObject::connect(uninstall_btn, &QPushButton::clicked, [=]() { diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index 392287d65d..547ad168f1 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -179,7 +179,7 @@ void DevicePanel::updateCalibDescription() { if (calib.getCalStatus() != 0) { double pitch = calib.getRpyCalib()[1] * (180 / M_PI); double yaw = calib.getRpyCalib()[2] * (180 / M_PI); - desc += QString(tr(" Your device is pointed %1° %2 and %3° %4.")) + desc += tr(" Your device is pointed %1° %2 and %3° %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")); } diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 7032fc7518..604d3c09a9 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -430,13 +430,13 @@ void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) { // lanelines for (int i = 0; i < std::size(scene.lane_line_vertices); ++i) { painter.setBrush(QColor::fromRgbF(1.0, 1.0, 1.0, std::clamp(scene.lane_line_probs[i], 0.0, 0.7))); - painter.drawPolygon(scene.lane_line_vertices[i].v, scene.lane_line_vertices[i].cnt); + painter.drawPolygon(scene.lane_line_vertices[i]); } // road edges for (int i = 0; i < std::size(scene.road_edge_vertices); ++i) { painter.setBrush(QColor::fromRgbF(1.0, 0, 0, std::clamp(1.0 - scene.road_edge_stds[i], 0.0, 1.0))); - painter.drawPolygon(scene.road_edge_vertices[i].v, scene.road_edge_vertices[i].cnt); + painter.drawPolygon(scene.road_edge_vertices[i]); } // paint path @@ -455,7 +455,7 @@ void NvgWindow::drawLaneLines(QPainter &painter, const UIState *s) { bg.setColorAt(0.75 / 1.5, QColor::fromHslF(curve_hue / 360., 1.0, 0.68, 0.35)); bg.setColorAt(1.0, QColor::fromHslF(curve_hue / 360., 1.0, 0.68, 0.0)); painter.setBrush(bg); - painter.drawPolygon(scene.track_vertices.v, scene.track_vertices.cnt); + painter.drawPolygon(scene.track_vertices); painter.restore(); } diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc index f82d9bf496..cab7299cd6 100644 --- a/selfdrive/ui/qt/util.cc +++ b/selfdrive/ui/qt/util.cc @@ -63,13 +63,13 @@ QString timeAgo(const QDateTime &date) { s = "now"; } else if (diff < 60 * 60) { int minutes = diff / 60; - s = QString(QObject::tr("%1 minute%2 ago")).arg(minutes).arg(minutes > 1 ? "s" : ""); + s = QObject::tr("%1 minute%2 ago").arg(minutes).arg(minutes > 1 ? "s" : ""); } else if (diff < 60 * 60 * 24) { int hours = diff / (60 * 60); - s = QString(QObject::tr("%1 hour%2 ago")).arg(hours).arg(hours > 1 ? "s" : ""); + s = QObject::tr("%1 hour%2 ago").arg(hours).arg(hours > 1 ? "s" : ""); } else if (diff < 3600 * 24 * 7) { int days = diff / (60 * 60 * 24); - s = QString(QObject::tr("%1 day%2 ago")).arg(days).arg(days > 1 ? "s" : ""); + s = QObject::tr("%1 day%2 ago").arg(days).arg(days > 1 ? "s" : ""); } else { s = date.date().toString(); } diff --git a/selfdrive/ui/qt/widgets/cameraview.cc b/selfdrive/ui/qt/widgets/cameraview.cc index 0bc90b5307..63d15660a0 100644 --- a/selfdrive/ui/qt/widgets/cameraview.cc +++ b/selfdrive/ui/qt/widgets/cameraview.cc @@ -243,13 +243,12 @@ void CameraViewWidget::paintGL() { // } // Log duplicate/dropped frames - static int prev_id = 0; - if (frames[frame_idx].first == prev_id) { - qInfo() << "Drawing same frame twice" << frames[frame_idx].first; - } else if (frames[frame_idx].first != prev_id + 1) { - qInfo() << "Skipped frame" << frames[frame_idx].first; + if (frames[frame_idx].first == prev_frame_id) { + qDebug() << "Drawing same frame twice" << frames[frame_idx].first; + } else if (frames[frame_idx].first != prev_frame_id + 1) { + qDebug() << "Skipped frame" << frames[frame_idx].first; } - prev_id = frames[frame_idx].first; + prev_frame_id = frames[frame_idx].first; glViewport(0, 0, width(), height()); glBindVertexArray(frame_vao); diff --git a/selfdrive/ui/qt/widgets/cameraview.h b/selfdrive/ui/qt/widgets/cameraview.h index cc11ec2c27..016522b05c 100644 --- a/selfdrive/ui/qt/widgets/cameraview.h +++ b/selfdrive/ui/qt/widgets/cameraview.h @@ -78,6 +78,7 @@ protected: std::deque> frames; uint32_t draw_frame_id = 0; + int prev_frame_id = 0; protected slots: void vipcConnected(VisionIpcClient *vipc_client); diff --git a/selfdrive/ui/qt/widgets/ssh_keys.cc b/selfdrive/ui/qt/widgets/ssh_keys.cc index 1f48c72735..f17604b3e5 100644 --- a/selfdrive/ui/qt/widgets/ssh_keys.cc +++ b/selfdrive/ui/qt/widgets/ssh_keys.cc @@ -47,13 +47,13 @@ void SshControl::getUserKeys(const QString &username) { params.put("GithubUsername", username.toStdString()); params.put("GithubSshKeys", resp.toStdString()); } else { - ConfirmationDialog::alert(QString(tr("Username '%1' has no keys on GitHub")).arg(username), this); + ConfirmationDialog::alert(tr("Username '%1' has no keys on GitHub").arg(username), this); } } else { if (request->timeout()) { ConfirmationDialog::alert(tr("Request timed out"), this); } else { - ConfirmationDialog::alert(QString(tr("Username '%1' doesn't exist on GitHub")).arg(username), this); + ConfirmationDialog::alert(tr("Username '%1' doesn't exist on GitHub").arg(username), this); } } diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py new file mode 100755 index 0000000000..ccea748e24 --- /dev/null +++ b/selfdrive/ui/tests/test_translations.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +import json +import os +import unittest + +from selfdrive.ui.update_translations import TRANSLATIONS_DIR, LANGUAGES_FILE, update_translations + + +class TestTranslations(unittest.TestCase): + @classmethod + def setUpClass(cls): + with open(LANGUAGES_FILE, "r") as f: + cls.translation_files = json.load(f) + + def test_missing_translation_files(self): + for name, file in self.translation_files.items(): + with self.subTest(name=name, file=file): + if not len(file): + self.skipTest(f"{name} translation has no file") + + self.assertTrue(os.path.exists(os.path.join(TRANSLATIONS_DIR, f"{file}.ts")), + f"{name} has no XML translation file, run selfdrive/ui/update_translations.py") + self.assertTrue(os.path.exists(os.path.join(TRANSLATIONS_DIR, f"{file}.qm")), + f"{name} has no compiled QM translation file, run selfdrive/ui/update_translations.py --release") + + def test_translations_updated(self): + suffix = "_test" + update_translations(suffix=suffix) + + for name, file in self.translation_files.items(): + with self.subTest(name=name, file=file): + cur_tr_file = os.path.join(TRANSLATIONS_DIR, f"{file}.ts") + new_tr_file = os.path.join(TRANSLATIONS_DIR, f"{file}{suffix}.ts") + + if not len(file): + self.skipTest(f"{name} translation has no file") + elif not os.path.exists(cur_tr_file): + self.skipTest(f"{name} missing translation file") # caught by test_missing_translation_files + + with open(cur_tr_file, "r") as f: + cur_translations = f.read() + with open(new_tr_file, "r") as f: + new_translations = f.read() + + self.assertEqual(cur_translations, new_translations, + f"{name} translation file out of date. Run selfdrive/ui/update_translations.py to update the translation files") + + +if __name__ == "__main__": + unittest.main() diff --git a/selfdrive/ui/translations/languages.json b/selfdrive/ui/translations/languages.json new file mode 100644 index 0000000000..f2f9400d64 --- /dev/null +++ b/selfdrive/ui/translations/languages.json @@ -0,0 +1,3 @@ +{ + "English": "" +} diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index c8fc645cf2..f6193f97a6 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -55,10 +55,13 @@ static void update_leads(UIState *s, const cereal::RadarState::Reader &radar_sta } static void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTData::Reader &line, - float y_off, float z_off, line_vertices_data *pvd, int max_idx, bool allow_invert=true) { + float y_off, float z_off, QPolygonF *pvd, int max_idx, bool allow_invert=true) { const auto line_x = line.getX(), line_y = line.getY(), line_z = line.getZ(); - std::vector left_points, right_points; + QPolygonF left_points, right_points; + left_points.reserve(max_idx + 1); + right_points.reserve(max_idx + 1); + for (int i = 0; i <= max_idx; i++) { QPointF left, right; bool l = calib_frame_to_full_frame(s, line_x[i], line_y[i] - y_off, line_z[i] + z_off, &left); @@ -69,19 +72,10 @@ static void update_line_data(const UIState *s, const cereal::ModelDataV2::XYZTDa continue; } left_points.push_back(left); - right_points.push_back(right); + right_points.push_front(right); } } - - pvd->cnt = 2 * left_points.size(); - assert(left_points.size() == right_points.size()); - assert(pvd->cnt <= std::size(pvd->v)); - - for (int left_idx = 0; left_idx < left_points.size(); left_idx++){ - int right_idx = 2 * left_points.size() - left_idx - 1; - pvd->v[left_idx] = left_points[left_idx]; - pvd->v[right_idx] = right_points[left_idx]; - } + *pvd = left_points + right_points; } static void update_model(UIState *s, const cereal::ModelDataV2::Reader &model) { diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 7364b81a40..1aee3df9a1 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "cereal/messaging/messaging.h" @@ -84,11 +85,6 @@ const QColor bg_colors [] = { [STATUS_ALERT] = QColor(0xC9, 0x22, 0x31, 0xf1), }; -typedef struct { - QPointF v[TRAJECTORY_SIZE * 2]; - int cnt; -} line_vertices_data; - typedef struct UIScene { bool calibration_valid = false; mat3 view_from_calib = DEFAULT_CALIBRATION; @@ -97,9 +93,9 @@ typedef struct UIScene { // modelV2 float lane_line_probs[4]; float road_edge_stds[2]; - line_vertices_data track_vertices; - line_vertices_data lane_line_vertices[4]; - line_vertices_data road_edge_vertices[2]; + QPolygonF track_vertices; + QPolygonF lane_line_vertices[4]; + QPolygonF road_edge_vertices[2]; // lead QPointF lead_vertices[2]; diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py new file mode 100755 index 0000000000..5d57fa39d2 --- /dev/null +++ b/selfdrive/ui/update_translations.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +import argparse +import os +import json + +from common.basedir import BASEDIR + +UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") +TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") +LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json") + + +def update_translations(release=False, suffix=""): + with open(LANGUAGES_FILE, "r") as f: + translation_files = json.load(f) + + for name, file in translation_files.items(): + if not len(file): + print(f"{name} has no translation file, skipping...") + continue + + tr_file = os.path.join(TRANSLATIONS_DIR, f"{file}{suffix}.ts") + ret = os.system(f"lupdate -recursive {UI_DIR} -ts {tr_file}") + assert ret == 0 + + if release: + ret = os.system(f"lrelease {tr_file}") + assert ret == 0 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Update translation files for UI", + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument("--release", action="store_true", help="Create compiled QM translation files used by UI") + args = parser.parse_args() + + update_translations(args.release) diff --git a/system/camerad/cameras/camera_common.cc b/system/camerad/cameras/camera_common.cc index 34dde9389c..04f3136485 100644 --- a/system/camerad/cameras/camera_common.cc +++ b/system/camerad/cameras/camera_common.cc @@ -66,11 +66,10 @@ private: bool hdr_; }; -void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType init_rgb_type, VisionStreamType init_yuv_type, release_cb init_release_callback) { +void CameraBuf::init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType init_rgb_type, VisionStreamType init_yuv_type) { vipc_server = v; this->rgb_type = init_rgb_type; this->yuv_type = init_yuv_type; - this->release_callback = init_release_callback; const CameraInfo *ci = &s->ci; camera_state = s; @@ -127,7 +126,7 @@ CameraBuf::~CameraBuf() { } bool CameraBuf::acquire() { - if (!safe_queue.try_pop(cur_buf_idx, 1)) return false; + if (!safe_queue.try_pop(cur_buf_idx, 50)) return false; if (camera_bufs_metadata[cur_buf_idx].frame_id == -1) { LOGE("no frame data? wtf"); @@ -169,9 +168,7 @@ bool CameraBuf::acquire() { } void CameraBuf::release() { - if (release_callback) { - release_callback((void*)camera_state, cur_buf_idx); - } + // Empty } void CameraBuf::queue(size_t buf_idx) { diff --git a/system/camerad/cameras/camera_common.h b/system/camerad/cameras/camera_common.h index 6b483372bb..4695d4e2c9 100644 --- a/system/camerad/cameras/camera_common.h +++ b/system/camerad/cameras/camera_common.h @@ -48,8 +48,6 @@ const bool env_disable_driver = getenv("DISABLE_DRIVER") != NULL; const bool env_debug_frames = getenv("DEBUG_FRAMES") != NULL; const bool env_log_raw_frames = getenv("LOG_RAW_FRAMES") != NULL; -typedef void (*release_cb)(void *cookie, int buf_idx); - typedef struct CameraInfo { uint32_t frame_width, frame_height; uint32_t frame_stride; @@ -85,11 +83,6 @@ typedef struct FrameMetadata { float processing_time; } FrameMetadata; -typedef struct CameraExpInfo { - int op_id; - float grey_frac; -} CameraExpInfo; - struct MultiCameraState; struct CameraState; class Debayer; @@ -108,7 +101,6 @@ private: SafeQueue safe_queue; int frame_buf_count; - release_cb release_callback; public: cl_command_queue q; @@ -124,7 +116,7 @@ public: CameraBuf() = default; ~CameraBuf(); - void init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType rgb_type, VisionStreamType yuv_type, release_cb release_callback=nullptr); + void init(cl_device_id device_id, cl_context context, CameraState *s, VisionIpcServer * v, int frame_cnt, VisionStreamType rgb_type, VisionStreamType yuv_type); bool acquire(); void release(); void queue(size_t buf_idx); diff --git a/tools/ssh/README.md b/tools/ssh/README.md index 66f030de52..29d3349328 100644 --- a/tools/ssh/README.md +++ b/tools/ssh/README.md @@ -22,3 +22,52 @@ The public keys are only fetched from your GitHub account once. In order to upda The `id_rsa` key in this directory only works while your device is in the setup state with no software installed. After installation, that default key will be removed. See the [community wiki](https://github.com/commaai/openpilot/wiki/SSH) for more detailed instructions and information. + +# Connecting to ssh.comma.ai +SSH into your comma device from anywhere with `ssh.comma.ai`. + +## Setup + +With software version 0.6.1 or newer, enter your GitHub username on your device under Developer Settings. Your GitHub authorized public keys will become your authorized SSH keys for `ssh.comma.ai`. You can add any additional keys in `/system/comma/home/.ssh/authorized_keys.persist`. + +Requires [comma SIM with comma prime](https://comma.ai/shop) activated with comma connect, available on iOS and Android. comma two and EON ship with a pre-inserted comma SIM. + +## Recommended .ssh/config + +With the below ssh configuration, you can type `ssh comma-{dongleid}` to connect to your device through `ssh.comma.ai`. For example, `ssh comma-ffffffffffffffff`. + +``` +Host comma-* + Port 22 + User comma + IdentityFile ~/.ssh/my_github_key + ProxyCommand ssh %h@ssh.comma.ai -W %h:%p +Host ssh.comma.ai + Hostname ssh.comma.ai + Port 22 + IdentityFile ~/.ssh/my_github_key +``` + +## One-off connection + +``` +ssh -i ~/.ssh/my_github_key -o ProxyCommand="ssh -i ~/.ssh/my_github_key -W %h:%p -p %p %h@ssh.comma.ai" comma@ffffffffffffffff +``` +(Replace `ffffffffffffffff` with your dongle_id) + +## ssh.comma.ai host key fingerprint + +``` +Host key fingerprint is SHA256:X22GOmfjGb9J04IA2+egtdaJ7vW9Fbtmpz9/x8/W1X4 ++---[RSA 4096]----+ +| | +| | +| . | +| + o | +| S = + +..| +| + @ = .=| +| . B @ ++=| +| o * B XE| +| .o o OB/| ++----[SHA256]-----+ +```