Merge remote-tracking branch 'upstream/master' into hkg-btn-msgs

pull/29663/head
Shane Smiskol 2 years ago
commit 2600178e4e
  1. 1
      .github/workflows/selfdrive_tests.yaml
  2. 1
      common/params.cc
  3. 2
      docs/CARS.md
  4. 8
      docs/docker/Dockerfile
  5. 45
      poetry.lock
  6. 6
      pyproject.toml
  7. 2
      release/files_common
  8. 4
      selfdrive/car/docs_definitions.py
  9. 2
      selfdrive/ui/SConscript
  10. 142
      selfdrive/ui/qt/maps/map_settings.cc
  11. 22
      selfdrive/ui/qt/maps/map_settings.h
  12. 2
      selfdrive/ui/qt/network/networking.cc
  13. 2
      selfdrive/ui/qt/network/networking.h
  14. 0
      selfdrive/ui/qt/network/networkmanager.h
  15. 2
      selfdrive/ui/qt/network/wifi_manager.cc
  16. 2
      selfdrive/ui/qt/network/wifi_manager.h
  17. 4
      selfdrive/ui/qt/offroad/driverview.cc
  18. 1
      selfdrive/ui/qt/offroad/driverview.h
  19. 2
      selfdrive/ui/qt/offroad/settings.cc
  20. 2
      selfdrive/ui/qt/setup/setup.cc
  21. 2
      selfdrive/ui/qt/setup/updater.cc
  22. 14
      selfdrive/ui/qt/widgets/input.cc
  23. 10
      selfdrive/ui/qt/widgets/input.h
  24. 2
      selfdrive/ui/qt/widgets/prime.cc
  25. 2
      selfdrive/ui/qt/widgets/prime.h
  26. 6
      selfdrive/ui/translations/main_fr.ts
  27. 4
      system/loggerd/tests/test_uploader.py
  28. 9
      tools/cabana/messageswidget.cc
  29. 5
      tools/plotjuggler/test_plotjuggler.py
  30. 45
      tools/sim/Dockerfile.sim

@ -301,6 +301,7 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
run: | run: |
${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \ ${{ env.RUN }} "CI=1 coverage run selfdrive/test/process_replay/test_processes.py -j$(nproc) && \
chmod -R 777 /tmp/comma_download_cache && \
coverage xml" coverage xml"
- name: Print diff - name: Print diff
id: print-diff id: print-diff

@ -158,6 +158,7 @@ std::unordered_map<std::string, uint32_t> keys = {
{"LongitudinalPersonality", PERSISTENT}, {"LongitudinalPersonality", PERSISTENT},
{"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"NavDestination", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION}, {"NavDestinationWaypoints", CLEAR_ON_MANAGER_START | CLEAR_ON_OFFROAD_TRANSITION},
{"NavPastDestinations", PERSISTENT},
{"NavSettingLeftSide", PERSISTENT}, {"NavSettingLeftSide", PERSISTENT},
{"NavSettingTime24h", PERSISTENT}, {"NavSettingTime24h", PERSISTENT},
{"NavdRender", PERSISTENT}, {"NavdRender", PERSISTENT},

@ -268,7 +268,7 @@ A supported vehicle is one that just works when you install a comma three. All s
|Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>|| |Volkswagen|Touran 2016-23|Adaptive Cruise Control (ACC) & Lane Assist|openpilot available[<sup>1,12</sup>](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|<details><summary>Parts</summary><sub>- 1 J533 connector<br>- 1 USB-C coupler<br>- 1 comma three<br>- 1 harness box<br>- 1 long OBD-C cable<br>- 1 mount<br>- 1 right angle OBD-C cable (1.5 ft)<br><a href="https://comma.ai/shop/comma-3x.html?make=Volkswagen&model=Touran 2016-23">Buy Here</a></sub></details>||
### Footnotes ### Footnotes
<sup>1</sup>Experimental openpilot longitudinal control is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br /> <sup>1</sup>openpilot Longitudinal Control (Alpha) is available behind a toggle; the toggle is only available in non-release branches such as `devel` or `master-ci`. <br />
<sup>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br /> <sup>2</sup>By default, this car will use the stock Adaptive Cruise Control (ACC) for longitudinal control. If the Driver Support Unit (DSU) is disconnected, openpilot ACC will replace stock ACC. <b><i>NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).</i></b> <br />
<sup>3</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br /> <sup>3</sup>Refers only to the Focus Mk4 (C519) available in Europe/China/Taiwan/Australasia, not the Focus Mk3 (C346) in North and South America/Southeast Asia. <br />
<sup>4</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br /> <sup>4</sup>Requires a <a href="https://github.com/commaai/openpilot/wiki/GM#hardware" target="_blank">community built ASCM harness</a>. <b><i>NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).</i></b> <br />

@ -2,7 +2,7 @@ FROM ghcr.io/commaai/openpilot-base:latest
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
ENV OPENPILOT_PATH /home/batman/openpilot/ ENV OPENPILOT_PATH /tmp/openpilot
ENV PYTHONPATH ${OPENPILOT_PATH}:${PYTHONPATH} ENV PYTHONPATH ${OPENPILOT_PATH}:${PYTHONPATH}
ENV POETRY_VIRUALENVS_CREATE false ENV POETRY_VIRUALENVS_CREATE false
@ -15,8 +15,8 @@ COPY ./openpilot ${OPENPILOT_PATH}/openpilot
COPY ./body ${OPENPILOT_PATH}/body COPY ./body ${OPENPILOT_PATH}/body
COPY ./third_party ${OPENPILOT_PATH}/third_party COPY ./third_party ${OPENPILOT_PATH}/third_party
COPY ./site_scons ${OPENPILOT_PATH}/site_scons COPY ./site_scons ${OPENPILOT_PATH}/site_scons
COPY ./laika ${OPENPILOT_PATH}/laika
COPY ./laika_repo ${OPENPILOT_PATH}/laika_repo COPY ./laika_repo ${OPENPILOT_PATH}/laika_repo
RUN ln -s ${OPENPILOT_PATH}/laika_repo ${OPENPILOT_PATH}/laika
COPY ./rednose ${OPENPILOT_PATH}/rednose COPY ./rednose ${OPENPILOT_PATH}/rednose
COPY ./rednose_repo ${OPENPILOT_PATH}/rednose_repo COPY ./rednose_repo ${OPENPILOT_PATH}/rednose_repo
COPY ./tools ${OPENPILOT_PATH}/tools COPY ./tools ${OPENPILOT_PATH}/tools
@ -29,7 +29,7 @@ COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive
COPY ./system ${OPENPILOT_PATH}/system COPY ./system ${OPENPILOT_PATH}/system
COPY ./*.md ${OPENPILOT_PATH}/ COPY ./*.md ${OPENPILOT_PATH}/
RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) --cache-readonly
RUN apt update && apt install doxygen -y RUN apt update && apt install doxygen -y
COPY ./docs ${OPENPILOT_PATH}/docs COPY ./docs ${OPENPILOT_PATH}/docs
@ -38,5 +38,5 @@ WORKDIR ${OPENPILOT_PATH}/docs
RUN make html RUN make html
FROM nginx:1.21 FROM nginx:1.21
COPY --from=0 /home/batman/openpilot/build/docs/html /usr/share/nginx/html COPY --from=0 /tmp/openpilot/build/docs/html /usr/share/nginx/html
COPY ./docs/docker/nginx.conf /etc/nginx/conf.d/default.conf COPY ./docs/docker/nginx.conf /etc/nginx/conf.d/default.conf

45
poetry.lock generated

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. # This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]] [[package]]
name = "aiohttp" name = "aiohttp"
@ -2383,14 +2383,7 @@ files = [
] ]
[package.dependencies] [package.dependencies]
numpy = [ numpy = {version = ">=1.23.5", markers = "python_version >= \"3.11\""}
{version = ">=1.21.2", markers = "python_version >= \"3.10\""},
{version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\""},
{version = ">=1.23.5", markers = "python_version >= \"3.11\""},
{version = ">=1.19.3", markers = "python_version >= \"3.6\" and platform_system == \"Linux\" and platform_machine == \"aarch64\" or python_version >= \"3.9\""},
{version = ">=1.17.0", markers = "python_version >= \"3.7\""},
{version = ">=1.17.3", markers = "python_version >= \"3.8\""},
]
[[package]] [[package]]
name = "packaging" name = "packaging"
@ -2438,10 +2431,7 @@ files = [
] ]
[package.dependencies] [package.dependencies]
numpy = [ numpy = {version = ">=1.23.2", markers = "python_version >= \"3.11\""}
{version = ">=1.21.0", markers = "python_version >= \"3.10\""},
{version = ">=1.23.2", markers = "python_version >= \"3.11\""},
]
python-dateutil = ">=2.8.2" python-dateutil = ">=2.8.2"
pytz = ">=2020.1" pytz = ">=2020.1"
tzdata = ">=2022.1" tzdata = ">=2022.1"
@ -3243,6 +3233,33 @@ files = [
attrs = ">=19.2.0" attrs = ">=19.2.0"
pytest = ">=7.0" pytest = ">=7.0"
[[package]]
name = "pytest-timeout"
version = "2.1.0"
description = "pytest plugin to abort hanging tests"
optional = false
python-versions = ">=3.6"
files = [
{file = "pytest-timeout-2.1.0.tar.gz", hash = "sha256:c07ca07404c612f8abbe22294b23c368e2e5104b521c1790195561f37e1ac3d9"},
{file = "pytest_timeout-2.1.0-py3-none-any.whl", hash = "sha256:f6f50101443ce70ad325ceb4473c4255e9d74e3c7cd0ef827309dfa4c0d975c6"},
]
[package.dependencies]
pytest = ">=5.0.0"
[[package]]
name = "pytest-timeouts"
version = "1.2.1"
description = "Linux-only Pytest plugin to control durations of various test case execution phases"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "pytest-timeouts-1.2.1.tar.gz", hash = "sha256:390351afc7ecb422ea0ec38081e0acd91cad416b383944a9a3358087de50c2fb"},
]
[package.dependencies]
pytest = ">=3.1"
[[package]] [[package]]
name = "pytest-xdist" name = "pytest-xdist"
version = "3.3.1" version = "3.3.1"
@ -4322,4 +4339,4 @@ multidict = ">=4.0"
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "~3.11" python-versions = "~3.11"
content-hash = "7e2bfde2719e7d7bb4b1627b0657e9ab4a9f4e1637d8b8ae6d5c80c7861e2052" content-hash = "799e03aa1f3098c94a383f17b56275cd4140179bc457ff837b793fbcf7eb4b1e"

@ -1,8 +1,8 @@
[tool.pytest.ini_options] [tool.pytest.ini_options]
minversion = "6.0" minversion = "6.0"
addopts = "--ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/" addopts = "--ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=laika_repo/ -Werror --strict-config --strict-markers"
python_files = "test_*.py" python_files = "test_*.py"
timeout = "30" # you get this long by default #timeout = "30" # you get this long by default
[tool.mypy] [tool.mypy]
python_version = "3.11" python_version = "3.11"
@ -124,6 +124,8 @@ pytest = "*"
pytest-cov = "*" pytest-cov = "*"
pytest-subtests = "*" pytest-subtests = "*"
pytest-xdist = "*" pytest-xdist = "*"
pytest-timeout = "*"
pytest-timeouts = "*"
scipy = "*" scipy = "*"
sphinx = "*" sphinx = "*"
sphinx-rtd-theme = "*" sphinx-rtd-theme = "*"

@ -318,6 +318,8 @@ selfdrive/ui/tests/test_translations.py
selfdrive/ui/qt/*.cc selfdrive/ui/qt/*.cc
selfdrive/ui/qt/*.h selfdrive/ui/qt/*.h
selfdrive/ui/qt/network/*.cc
selfdrive/ui/qt/network/*.h
selfdrive/ui/qt/offroad/*.cc selfdrive/ui/qt/offroad/*.cc
selfdrive/ui/qt/offroad/*.h selfdrive/ui/qt/offroad/*.h
selfdrive/ui/qt/offroad/*.qml selfdrive/ui/qt/offroad/*.qml

@ -85,6 +85,7 @@ class CarHarness(EnumBase):
bosch_a = BaseCarHarness("Honda Bosch A connector") bosch_a = BaseCarHarness("Honda Bosch A connector")
bosch_b = BaseCarHarness("Honda Bosch B connector") bosch_b = BaseCarHarness("Honda Bosch B connector")
toyota_a = BaseCarHarness("Toyota A connector") toyota_a = BaseCarHarness("Toyota A connector")
toyota_b = BaseCarHarness("Toyota B connector")
subaru_a = BaseCarHarness("Subaru A connector") subaru_a = BaseCarHarness("Subaru A connector")
subaru_b = BaseCarHarness("Subaru B connector") subaru_b = BaseCarHarness("Subaru B connector")
subaru_c = BaseCarHarness("Subaru C connector") subaru_c = BaseCarHarness("Subaru C connector")
@ -110,6 +111,7 @@ class CarHarness(EnumBase):
hyundai_o = BaseCarHarness("Hyundai O connector") hyundai_o = BaseCarHarness("Hyundai O connector")
hyundai_p = BaseCarHarness("Hyundai P connector") hyundai_p = BaseCarHarness("Hyundai P connector")
hyundai_q = BaseCarHarness("Hyundai Q connector") hyundai_q = BaseCarHarness("Hyundai Q connector")
hyundai_r = BaseCarHarness("Hyundai R connector")
custom = BaseCarHarness("Developer connector") custom = BaseCarHarness("Developer connector")
obd_ii = BaseCarHarness("OBD-II connector", parts=[Cable.long_obdc_cable, Cable.long_obdc_cable], has_connector=False) obd_ii = BaseCarHarness("OBD-II connector", parts=[Cable.long_obdc_cable, Cable.long_obdc_cable], has_connector=False)
gm = BaseCarHarness("GM connector") gm = BaseCarHarness("GM connector")
@ -174,7 +176,7 @@ CarFootnote = namedtuple("CarFootnote", ["text", "column", "docs_only", "shop_fo
class CommonFootnote(Enum): class CommonFootnote(Enum):
EXP_LONG_AVAIL = CarFootnote( EXP_LONG_AVAIL = CarFootnote(
"Experimental openpilot longitudinal control is available behind a toggle; " + "openpilot Longitudinal Control (Alpha) is available behind a toggle; " +
"the toggle is only available in non-release branches such as `devel` or `master-ci`.", "the toggle is only available in non-release branches such as `devel` or `master-ci`.",
Column.LONGITUDINAL, docs_only=True) Column.LONGITUDINAL, docs_only=True)
EXP_LONG_DSU = CarFootnote( EXP_LONG_DSU = CarFootnote(

@ -24,7 +24,7 @@ widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/
"qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc", "qt/widgets/ssh_keys.cc", "qt/widgets/toggle.cc", "qt/widgets/controls.cc",
"qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc", "qt/widgets/offroad_alerts.cc", "qt/widgets/prime.cc", "qt/widgets/keyboard.cc",
"qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc", "qt/widgets/scrollview.cc", "qt/widgets/cameraview.cc", "#third_party/qrcode/QrCode.cc",
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/offroad/networking.cc", "qt/offroad/wifiManager.cc"] "qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc"]
qt_env['CPPDEFINES'] = [] qt_env['CPPDEFINES'] = []
if maps: if maps:

@ -9,6 +9,17 @@
#include "selfdrive/ui/qt/request_repeater.h" #include "selfdrive/ui/qt/request_repeater.h"
#include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/widgets/scrollview.h"
static void swap(QJsonValueRef v1, QJsonValueRef v2) { std::swap(v1, v2); }
static bool locationEqual(const QJsonValue &v1, const QJsonValue &v2) {
return v1["latitude"] == v2["latitude"] && v1["longitude"] == v2["longitude"];
}
static qint64 convertTimestampToEpoch(const QString &timestamp) {
QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate);
return dt.isValid() ? dt.toSecsSinceEpoch() : 0;
}
MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) { MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
setContentsMargins(0, 0, 0, 0); setContentsMargins(0, 0, 0, 0);
setAttribute(Qt::WA_NoMousePropagation); setAttribute(Qt::WA_NoMousePropagation);
@ -61,11 +72,8 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
frame->addSpacing(32); frame->addSpacing(32);
current_widget = new DestinationWidget(this); current_widget = new DestinationWidget(this);
QObject::connect(current_widget, &DestinationWidget::actionClicked, [=]() { QObject::connect(current_widget, &DestinationWidget::actionClicked,
if (current_destination.empty()) return; []() { NavManager::instance()->setCurrentDestination({}); });
params.remove("NavDestination");
updateCurrentRoute();
});
frame->addWidget(current_widget); frame->addWidget(current_widget);
frame->addSpacing(32); frame->addSpacing(32);
@ -84,40 +92,16 @@ MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
frame->addWidget(destinations_scroller); frame->addWidget(destinations_scroller);
setStyleSheet("MapSettings { background-color: #333333; }"); setStyleSheet("MapSettings { background-color: #333333; }");
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
QObject::connect(NavigationRequest::instance(), &NavigationRequest::locationsUpdated, this, &MapSettings::updateLocations);
QObject::connect(NavigationRequest::instance(), &NavigationRequest::nextDestinationUpdated, this, &MapSettings::updateCurrentRoute);
current_locations = NavigationRequest::instance()->currentLocations();
} }
void MapSettings::showEvent(QShowEvent *event) { void MapSettings::showEvent(QShowEvent *event) {
updateCurrentRoute();
}
void MapSettings::updateCurrentRoute() {
auto dest = QString::fromStdString(params.get("NavDestination"));
if (dest.size()) {
QJsonDocument doc = QJsonDocument::fromJson(dest.trimmed().toUtf8());
if (doc.isNull()) {
qWarning() << "JSON Parse failed on NavDestination" << dest;
return;
}
current_destination = doc.object();
current_widget->set(current_destination, true);
} else {
current_destination = {};
current_widget->unset("", true);
}
if (isVisible()) refresh();
}
void MapSettings::updateLocations(const QJsonArray &locations) {
current_locations = locations;
refresh(); refresh();
} }
void MapSettings::refresh() { void MapSettings::refresh() {
if (!isVisible()) return;
setUpdatesEnabled(false); setUpdatesEnabled(false);
auto get_w = [this](int i) { auto get_w = [this](int i) {
@ -129,11 +113,17 @@ void MapSettings::refresh() {
return w; return w;
}; };
const auto current_dest = NavManager::instance()->currentDestination();
if (!current_dest.isEmpty()) {
current_widget->set(current_dest, true);
} else {
current_widget->unset("", true);
}
home_widget->unset(NAV_FAVORITE_LABEL_HOME); home_widget->unset(NAV_FAVORITE_LABEL_HOME);
work_widget->unset(NAV_FAVORITE_LABEL_WORK); work_widget->unset(NAV_FAVORITE_LABEL_WORK);
int n = 0; int n = 0;
for (auto location : current_locations) { for (auto location : NavManager::instance()->currentLocations()) {
DestinationWidget *w = nullptr; DestinationWidget *w = nullptr;
auto dest = location.toObject(); auto dest = location.toObject();
if (dest["save_type"].toString() == NAV_TYPE_FAVORITE) { if (dest["save_type"].toString() == NAV_TYPE_FAVORITE) {
@ -143,7 +133,7 @@ void MapSettings::refresh() {
} }
w = w ? w : get_w(n++); w = w ? w : get_w(n++);
w->set(dest, false); w->set(dest, false);
w->setVisible(dest != current_destination); w->setVisible(!locationEqual(dest, current_dest));
} }
for (; n < widgets.size(); ++n) widgets[n]->setVisible(false); for (; n < widgets.size(); ++n) widgets[n]->setVisible(false);
@ -151,9 +141,7 @@ void MapSettings::refresh() {
} }
void MapSettings::navigateTo(const QJsonObject &place) { void MapSettings::navigateTo(const QJsonObject &place) {
QJsonDocument doc(place); NavManager::instance()->setCurrentDestination(place);
params.put("NavDestination", doc.toJson().toStdString());
updateCurrentRoute();
emit closeSettings(); emit closeSettings();
} }
@ -279,24 +267,26 @@ void DestinationWidget::unset(const QString &label, bool current) {
setVisible(true); setVisible(true);
} }
// singleton NavigationRequest // singleton NavManager
NavigationRequest *NavigationRequest::instance() { NavManager *NavManager::instance() {
static NavigationRequest *request = new NavigationRequest(qApp); static NavManager *request = new NavManager(qApp);
return request; return request;
} }
NavigationRequest::NavigationRequest(QObject *parent) : QObject(parent) { NavManager::NavManager(QObject *parent) : QObject(parent) {
locations = QJsonDocument::fromJson(params.get("NavPastDestinations").c_str()).array();
current_dest = QJsonDocument::fromJson(params.get("NavDestination").c_str()).object();
if (auto dongle_id = getDongleId()) { if (auto dongle_id = getDongleId()) {
{ {
// Fetch favorite and recent locations // Fetch favorite and recent locations
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations"; QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations";
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true); RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true);
QObject::connect(repeater, &RequestRepeater::requestDone, this, &NavigationRequest::parseLocationsResponse); QObject::connect(repeater, &RequestRepeater::requestDone, this, &NavManager::parseLocationsResponse);
} }
{ {
auto param_watcher = new ParamWatcher(this); auto param_watcher = new ParamWatcher(this);
QObject::connect(param_watcher, &ParamWatcher::paramChanged, this, &NavigationRequest::nextDestinationUpdated); QObject::connect(param_watcher, &ParamWatcher::paramChanged, this, &NavManager::updated);
// Destination set while offline // Destination set while offline
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/next"; QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/next";
@ -316,14 +306,14 @@ NavigationRequest::NavigationRequest(QObject *parent) : QObject(parent) {
// athena can set destination at any time // athena can set destination at any time
param_watcher->addParam("NavDestination"); param_watcher->addParam("NavDestination");
current_dest = QJsonDocument::fromJson(params.get("NavDestination").c_str()).object();
emit updated();
}); });
} }
} }
} }
static void swap(QJsonValueRef v1, QJsonValueRef v2) { std::swap(v1, v2); } void NavManager::parseLocationsResponse(const QString &response, bool success) {
void NavigationRequest::parseLocationsResponse(const QString &response, bool success) {
if (!success || response == prev_response) return; if (!success || response == prev_response) return;
prev_response = response; prev_response = response;
@ -333,13 +323,63 @@ void NavigationRequest::parseLocationsResponse(const QString &response, bool suc
return; return;
} }
// Sort: alphabetical FAVORITES, and then most recent (as returned by API). // set last activity time.
auto remote_locations = doc.array();
for (QJsonValueRef loc : remote_locations) {
auto obj = loc.toObject();
auto serverTime = convertTimestampToEpoch(obj["modified"].toString());
obj.insert("time", qMax(serverTime, getLastActivity(obj)));
loc = obj;
}
locations = remote_locations;
sortLocations();
emit updated();
}
void NavManager::sortLocations() {
// Sort: alphabetical FAVORITES, and then most recent.
// We don't need to care about the ordering of HOME and WORK. DestinationWidget always displays them at the top. // We don't need to care about the ordering of HOME and WORK. DestinationWidget always displays them at the top.
locations = doc.array();
std::stable_sort(locations.begin(), locations.end(), [](const QJsonValue &a, const QJsonValue &b) { std::stable_sort(locations.begin(), locations.end(), [](const QJsonValue &a, const QJsonValue &b) {
bool has_favorite = a["save_type"] == NAV_TYPE_FAVORITE || b["save_type"] == NAV_TYPE_FAVORITE; if (a["save_type"] == NAV_TYPE_FAVORITE || b["save_type"] == NAV_TYPE_FAVORITE) {
return has_favorite && (std::tuple(a["save_type"].toString(), a["place_name"].toString()) < return (std::tuple(a["save_type"].toString(), a["place_name"].toString()) <
std::tuple(b["save_type"].toString(), b["place_name"].toString())); std::tuple(b["save_type"].toString(), b["place_name"].toString()));
} else {
return a["time"].toVariant().toLongLong() > b["time"].toVariant().toLongLong();
}
});
write_param_future = std::async(std::launch::async, [destinations = QJsonArray(locations)]() {
Params().put("NavPastDestinations", QJsonDocument(destinations).toJson().toStdString());
}); });
emit locationsUpdated(locations); }
qint64 NavManager::getLastActivity(const QJsonObject &loc) const {
qint64 last_activity = 0;
auto it = std::find_if(locations.begin(), locations.end(),
[&loc](const QJsonValue &l) { return locationEqual(loc, l); });
if (it != locations.end()) {
auto tm = it->toObject().value("time");
if (!tm.isUndefined() && !tm.isNull()) {
last_activity = tm.toVariant().toLongLong();
}
}
return last_activity;
}
void NavManager::setCurrentDestination(const QJsonObject &loc) {
current_dest = loc;
if (!current_dest.isEmpty()) {
current_dest["time"] = QDateTime::currentSecsSinceEpoch();
auto it = std::find_if(locations.begin(), locations.end(),
[&loc](const QJsonValue &l) { return locationEqual(loc, l); });
if (it != locations.end()) {
*it = current_dest;
sortLocations();
}
params.put("NavDestination", QJsonDocument(current_dest).toJson().toStdString());
} else {
params.remove("NavDestination");
}
emit updated();
} }

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <future>
#include <vector> #include <vector>
#include <QFrame> #include <QFrame>
@ -22,42 +23,41 @@ const QString NAV_FAVORITE_LABEL_WORK = "work";
class DestinationWidget; class DestinationWidget;
class NavigationRequest : public QObject { class NavManager : public QObject {
Q_OBJECT Q_OBJECT
public: public:
static NavigationRequest *instance(); static NavManager *instance();
QJsonArray currentLocations() const { return locations; } QJsonArray currentLocations() const { return locations; }
QJsonObject currentDestination() const { return current_dest; }
void setCurrentDestination(const QJsonObject &loc);
qint64 getLastActivity(const QJsonObject &loc) const;
signals: signals:
void locationsUpdated(const QJsonArray &locations); void updated();
void nextDestinationUpdated();
private: private:
NavigationRequest(QObject *parent); NavManager(QObject *parent);
void parseLocationsResponse(const QString &response, bool success); void parseLocationsResponse(const QString &response, bool success);
void sortLocations();
Params params; Params params;
QString prev_response; QString prev_response;
QJsonArray locations; QJsonArray locations;
QJsonObject current_dest;
std::future<void> write_param_future;
}; };
class MapSettings : public QFrame { class MapSettings : public QFrame {
Q_OBJECT Q_OBJECT
public: public:
explicit MapSettings(bool closeable = false, QWidget *parent = nullptr); explicit MapSettings(bool closeable = false, QWidget *parent = nullptr);
void navigateTo(const QJsonObject &place); void navigateTo(const QJsonObject &place);
void updateLocations(const QJsonArray &locations);
void updateCurrentRoute();
private: private:
void showEvent(QShowEvent *event) override; void showEvent(QShowEvent *event) override;
void refresh(); void refresh();
Params params;
QJsonArray current_locations;
QJsonObject current_destination;
QVBoxLayout *destinations_layout; QVBoxLayout *destinations_layout;
DestinationWidget *current_widget; DestinationWidget *current_widget;
DestinationWidget *home_widget; DestinationWidget *home_widget;

@ -1,4 +1,4 @@
#include "selfdrive/ui/qt/offroad/networking.h" #include "selfdrive/ui/qt/network/networking.h"
#include <algorithm> #include <algorithm>

@ -2,7 +2,7 @@
#include <vector> #include <vector>
#include "selfdrive/ui/qt/offroad/wifiManager.h" #include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"
#include "selfdrive/ui/qt/widgets/ssh_keys.h" #include "selfdrive/ui/qt/widgets/ssh_keys.h"
#include "selfdrive/ui/qt/widgets/toggle.h" #include "selfdrive/ui/qt/widgets/toggle.h"

@ -1,4 +1,4 @@
#include "selfdrive/ui/qt/offroad/wifiManager.h" #include "selfdrive/ui/qt/network/wifi_manager.h"
#include "selfdrive/ui/ui.h" #include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/widgets/prime.h" #include "selfdrive/ui/qt/widgets/prime.h"

@ -4,7 +4,7 @@
#include <QtDBus> #include <QtDBus>
#include <QTimer> #include <QTimer>
#include "selfdrive/ui/qt/offroad/networkmanager.h" #include "selfdrive/ui/qt/network/networkmanager.h"
enum class SecurityType { enum class SecurityType {
OPEN, OPEN,

@ -35,7 +35,7 @@ void DriverViewWindow::mouseReleaseEvent(QMouseEvent* e) {
closeView(); closeView();
} }
DriverViewScene::DriverViewScene(QWidget* parent) : sm({"driverStateV2"}), QWidget(parent) { DriverViewScene::DriverViewScene(QWidget* parent) : QWidget(parent) {
face_img = loadPixmap("../assets/img_driver_face_static.png", {FACE_IMG_SIZE, FACE_IMG_SIZE}); face_img = loadPixmap("../assets/img_driver_face_static.png", {FACE_IMG_SIZE, FACE_IMG_SIZE});
} }
@ -51,7 +51,6 @@ void DriverViewScene::hideEvent(QHideEvent* event) {
void DriverViewScene::frameUpdated() { void DriverViewScene::frameUpdated() {
frame_updated = true; frame_updated = true;
sm.update(0);
update(); update();
} }
@ -67,6 +66,7 @@ void DriverViewScene::paintEvent(QPaintEvent* event) {
return; return;
} }
const auto &sm = *(uiState()->sm);
cereal::DriverStateV2::Reader driver_state = sm["driverStateV2"].getDriverStateV2(); cereal::DriverStateV2::Reader driver_state = sm["driverStateV2"].getDriverStateV2();
cereal::DriverStateV2::DriverData::Reader driver_data; cereal::DriverStateV2::DriverData::Reader driver_data;

@ -20,7 +20,6 @@ protected:
private: private:
Params params; Params params;
SubMaster sm;
QPixmap face_img; QPixmap face_img;
bool is_rhd = false; bool is_rhd = false;
bool frame_updated = false; bool frame_updated = false;

@ -8,7 +8,7 @@
#include <QDebug> #include <QDebug>
#include "selfdrive/ui/qt/offroad/networking.h" #include "selfdrive/ui/qt/network/networking.h"
#include "common/params.h" #include "common/params.h"
#include "common/watchdog.h" #include "common/watchdog.h"

@ -15,7 +15,7 @@
#include "system/hardware/hw.h" #include "system/hardware/hw.h"
#include "selfdrive/ui/qt/api.h" #include "selfdrive/ui/qt/api.h"
#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/offroad/networking.h" #include "selfdrive/ui/qt/network/networking.h"
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/input.h" #include "selfdrive/ui/qt/widgets/input.h"

@ -6,7 +6,7 @@
#include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/qt_window.h" #include "selfdrive/ui/qt/qt_window.h"
#include "selfdrive/ui/qt/setup/updater.h" #include "selfdrive/ui/qt/setup/updater.h"
#include "selfdrive/ui/qt/offroad/networking.h" #include "selfdrive/ui/qt/network/networking.h"
Updater::Updater(const QString &updater_path, const QString &manifest_path, QWidget *parent) Updater::Updater(const QString &updater_path, const QString &manifest_path, QWidget *parent)
: updater(updater_path), manifest(manifest_path), QStackedWidget(parent) { : updater(updater_path), manifest(manifest_path), QStackedWidget(parent) {

@ -9,7 +9,7 @@
#include "selfdrive/ui/qt/widgets/scrollview.h" #include "selfdrive/ui/qt/widgets/scrollview.h"
QDialogBase::QDialogBase(QWidget *parent) : QDialog(parent) { DialogBase::DialogBase(QWidget *parent) : QDialog(parent) {
Q_ASSERT(parent != nullptr); Q_ASSERT(parent != nullptr);
parent->installEventFilter(this); parent->installEventFilter(this);
@ -19,7 +19,7 @@ QDialogBase::QDialogBase(QWidget *parent) : QDialog(parent) {
color: white; color: white;
font-family: Inter; font-family: Inter;
} }
QDialogBase { DialogBase {
background-color: black; background-color: black;
} }
QPushButton { QPushButton {
@ -36,19 +36,19 @@ QDialogBase::QDialogBase(QWidget *parent) : QDialog(parent) {
)"); )");
} }
bool QDialogBase::eventFilter(QObject *o, QEvent *e) { bool DialogBase::eventFilter(QObject *o, QEvent *e) {
if (o == parent() && e->type() == QEvent::Hide) { if (o == parent() && e->type() == QEvent::Hide) {
reject(); reject();
} }
return QDialog::eventFilter(o, e); return QDialog::eventFilter(o, e);
} }
int QDialogBase::exec() { int DialogBase::exec() {
setMainWindow(this); setMainWindow(this);
return QDialog::exec(); return QDialog::exec();
} }
InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &subtitle, bool secret) : QDialogBase(parent) { InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &subtitle, bool secret) : DialogBase(parent) {
main_layout = new QVBoxLayout(this); main_layout = new QVBoxLayout(this);
main_layout->setContentsMargins(50, 55, 50, 50); main_layout->setContentsMargins(50, 55, 50, 50);
main_layout->setSpacing(0); main_layout->setSpacing(0);
@ -188,7 +188,7 @@ void InputDialog::setMinLength(int length) {
// ConfirmationDialog // ConfirmationDialog
ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString &confirm_text, const QString &cancel_text, ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString &confirm_text, const QString &cancel_text,
const bool rich, QWidget *parent) : QDialogBase(parent) { const bool rich, QWidget *parent) : DialogBase(parent) {
QFrame *container = new QFrame(this); QFrame *container = new QFrame(this);
container->setStyleSheet(R"( container->setStyleSheet(R"(
QFrame { background-color: #1B1B1B; color: #C9C9C9; } QFrame { background-color: #1B1B1B; color: #C9C9C9; }
@ -245,7 +245,7 @@ bool ConfirmationDialog::rich(const QString &prompt_text, QWidget *parent) {
// MultiOptionDialog // MultiOptionDialog
MultiOptionDialog::MultiOptionDialog(const QString &prompt_text, const QStringList &l, const QString &current, QWidget *parent) : QDialogBase(parent) { MultiOptionDialog::MultiOptionDialog(const QString &prompt_text, const QStringList &l, const QString &current, QWidget *parent) : DialogBase(parent) {
QFrame *container = new QFrame(this); QFrame *container = new QFrame(this);
container->setStyleSheet(R"( container->setStyleSheet(R"(
QFrame { background-color: #1B1B1B; } QFrame { background-color: #1B1B1B; }

@ -10,18 +10,18 @@
#include "selfdrive/ui/qt/widgets/keyboard.h" #include "selfdrive/ui/qt/widgets/keyboard.h"
class QDialogBase : public QDialog { class DialogBase : public QDialog {
Q_OBJECT Q_OBJECT
protected: protected:
QDialogBase(QWidget *parent); DialogBase(QWidget *parent);
bool eventFilter(QObject *o, QEvent *e) override; bool eventFilter(QObject *o, QEvent *e) override;
public slots: public slots:
int exec() override; int exec() override;
}; };
class InputDialog : public QDialogBase { class InputDialog : public DialogBase {
Q_OBJECT Q_OBJECT
public: public:
@ -50,7 +50,7 @@ signals:
void emitText(const QString &text); void emitText(const QString &text);
}; };
class ConfirmationDialog : public QDialogBase { class ConfirmationDialog : public DialogBase {
Q_OBJECT Q_OBJECT
public: public:
@ -61,7 +61,7 @@ public:
static bool rich(const QString &prompt_text, QWidget *parent); static bool rich(const QString &prompt_text, QWidget *parent);
}; };
class MultiOptionDialog : public QDialogBase { class MultiOptionDialog : public DialogBase {
Q_OBJECT Q_OBJECT
public: public:

@ -68,7 +68,7 @@ void PairingQRWidget::paintEvent(QPaintEvent *e) {
} }
PairingPopup::PairingPopup(QWidget *parent) : QDialogBase(parent) { PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
QHBoxLayout *hlayout = new QHBoxLayout(this); QHBoxLayout *hlayout = new QHBoxLayout(this);
hlayout->setContentsMargins(0, 0, 0, 0); hlayout->setContentsMargins(0, 0, 0, 0);
hlayout->setSpacing(0); hlayout->setSpacing(0);

@ -28,7 +28,7 @@ private slots:
// pairing popup widget // pairing popup widget
class PairingPopup : public QDialogBase { class PairingPopup : public DialogBase {
Q_OBJECT Q_OBJECT
public: public:

@ -517,7 +517,7 @@
<name>PrimeAdWidget</name> <name>PrimeAdWidget</name>
<message> <message>
<source>Upgrade Now</source> <source>Upgrade Now</source>
<translation>Mettre à niveau maintenant</translation> <translation>Mettre à niveau</translation>
</message> </message>
<message> <message>
<source>Become a comma prime member at connect.comma.ai</source> <source>Become a comma prime member at connect.comma.ai</source>
@ -860,7 +860,7 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<name>SoftwarePanel</name> <name>SoftwarePanel</name>
<message> <message>
<source>Updates are only downloaded while the car is off.</source> <source>Updates are only downloaded while the car is off.</source>
<translation>Les mises à jour sont téléchargées uniquement lorsque la voiture est éteinte.</translation> <translation>Les MàJ sont téléchargées uniquement si la voiture est éteinte.</translation>
</message> </message>
<message> <message>
<source>Current Version</source> <source>Current Version</source>
@ -1174,7 +1174,7 @@ Cela peut prendre jusqu&apos;à une minute.</translation>
<name>WiFiPromptWidget</name> <name>WiFiPromptWidget</name>
<message> <message>
<source>Setup Wi-Fi</source> <source>Setup Wi-Fi</source>
<translation>Configurer le Wi-Fi</translation> <translation>Configurer Wi-Fi</translation>
</message> </message>
<message> <message>
<source>Connect to Wi-Fi to upload driving data and help improve openpilot</source> <source>Connect to Wi-Fi to upload driving data and help improve openpilot</source>

@ -14,7 +14,7 @@ from openpilot.system.loggerd.uploader import uploader_fn, UPLOAD_ATTR_NAME, UPL
from openpilot.system.loggerd.tests.loggerd_tests_common import UploaderTestCase from openpilot.system.loggerd.tests.loggerd_tests_common import UploaderTestCase
class TestLogHandler(logging.Handler): class FakeLogHandler(logging.Handler):
def __init__(self): def __init__(self):
logging.Handler.__init__(self) logging.Handler.__init__(self)
self.reset() self.reset()
@ -33,7 +33,7 @@ class TestLogHandler(logging.Handler):
except Exception: except Exception:
pass pass
log_handler = TestLogHandler() log_handler = FakeLogHandler()
cloudlog.addHandler(log_handler) cloudlog.addHandler(log_handler)

@ -281,7 +281,8 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, co
case Column::NAME: { case Column::NAME: {
const auto msg = dbc()->msg(id); const auto msg = dbc()->msg(id);
match = re.match(msg ? msg->name : UNTITLED).hasMatch(); match = re.match(msg ? msg->name : UNTITLED).hasMatch();
match |= msg && std::any_of(msg->sigs.cbegin(), msg->sigs.cend(), [&re](const auto &s) { return re.match(s->name).hasMatch(); }); match = match || (msg && std::any_of(msg->sigs.cbegin(), msg->sigs.cend(),
[&re](const auto &s) { return re.match(s->name).hasMatch(); }));
break; break;
} }
case Column::SOURCE: case Column::SOURCE:
@ -289,7 +290,7 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, co
break; break;
case Column::ADDRESS: { case Column::ADDRESS: {
match = re.match(QString::number(id.address, 16)).hasMatch(); match = re.match(QString::number(id.address, 16)).hasMatch();
match |= parseRange(txt, id.address, 16); match = match || parseRange(txt, id.address, 16);
break; break;
} }
case Column::FREQ: case Column::FREQ:
@ -301,8 +302,8 @@ bool MessageListModel::matchMessage(const MessageId &id, const CanData &data, co
break; break;
case Column::DATA: { case Column::DATA: {
match = QString(data.dat.toHex()).contains(txt, Qt::CaseInsensitive); match = QString(data.dat.toHex()).contains(txt, Qt::CaseInsensitive);
match |= re.match(QString(data.dat.toHex())).hasMatch(); match = match || re.match(QString(data.dat.toHex())).hasMatch();
match |= re.match(QString(data.dat.toHex(' '))).hasMatch(); match = match || re.match(QString(data.dat.toHex(' '))).hasMatch();
break; break;
} }
} }

@ -18,9 +18,8 @@ class TestPlotJuggler(unittest.TestCase):
install() install()
pj = os.path.join(PJ_DIR, "juggle.py") pj = os.path.join(PJ_DIR, "juggle.py")
p = subprocess.Popen(f'QT_QPA_PLATFORM=offscreen {pj} --demo None 1 --qlog', with subprocess.Popen(f'QT_QPA_PLATFORM=offscreen {pj} --demo None 1 --qlog',
stderr=subprocess.PIPE, shell=True, start_new_session=True) stderr=subprocess.PIPE, shell=True, start_new_session=True) as p:
# Wait for "Done reading Rlog data" signal from the plugin # Wait for "Done reading Rlog data" signal from the plugin
output = "\n" output = "\n"
with Timeout(180, error_msg=output): with Timeout(180, error_msg=output):

@ -9,26 +9,29 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN cd $HOME && \ RUN cd $HOME && \
curl -O https://raw.githubusercontent.com/commaai/eon-neos-builder/master/devices/eon/home/.tmux.conf curl -O https://raw.githubusercontent.com/commaai/eon-neos-builder/master/devices/eon/home/.tmux.conf
ENV PYTHONPATH $HOME/openpilot:${PYTHONPATH} ENV OPENPILOT_PATH /tmp/openpilot
RUN mkdir -p $HOME/openpilot ENV PYTHONPATH ${OPENPILOT_PATH}:${PYTHONPATH}
COPY SConstruct $HOME/openpilot/ RUN mkdir -p ${OPENPILOT_PATH}
WORKDIR ${OPENPILOT_PATH}
COPY ./openpilot $HOME/openpilot/openpilot
COPY ./body $HOME/openpilot/body COPY SConstruct ${OPENPILOT_PATH}
COPY ./third_party $HOME/openpilot/third_party
COPY ./site_scons $HOME/openpilot/site_scons COPY ./openpilot ${OPENPILOT_PATH}/openpilot
COPY ./rednose $HOME/openpilot/rednose COPY ./body ${OPENPILOT_PATH}/body
COPY ./laika $HOME/openpilot/laika COPY ./third_party ${OPENPILOT_PATH}/third_party
COPY ./common $HOME/openpilot/common COPY ./site_scons ${OPENPILOT_PATH}/site_scons
COPY ./opendbc $HOME/openpilot/opendbc COPY ./rednose ${OPENPILOT_PATH}/rednose
COPY ./cereal $HOME/openpilot/cereal COPY ./laika_repo ${OPENPILOT_PATH}/laika_repo
COPY ./panda $HOME/openpilot/panda RUN ln -s ${OPENPILOT_PATH}/laika_repo ${OPENPILOT_PATH}/laika
COPY ./selfdrive $HOME/openpilot/selfdrive COPY ./common ${OPENPILOT_PATH}/common
COPY ./system $HOME/openpilot/system COPY ./opendbc ${OPENPILOT_PATH}/opendbc
COPY ./tools $HOME/openpilot/tools COPY ./cereal ${OPENPILOT_PATH}/cereal
COPY ./panda ${OPENPILOT_PATH}/panda
WORKDIR $HOME/openpilot COPY ./selfdrive ${OPENPILOT_PATH}/selfdrive
RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) COPY ./system ${OPENPILOT_PATH}/system
COPY ./tools ${OPENPILOT_PATH}/tools
RUN --mount=type=bind,source=.ci_cache/scons_cache,target=/tmp/scons_cache,rw scons -j$(nproc) --cache-readonly
RUN python -c "from openpilot.selfdrive.test.helpers import set_params_enabled; set_params_enabled()" RUN python -c "from openpilot.selfdrive.test.helpers import set_params_enabled; set_params_enabled()"

Loading…
Cancel
Save