wip
This commit is contained in:
@@ -1,82 +0,0 @@
|
|||||||
# PFEIFER - MAPD - Modified by FrogAi for FrogPilot to automatically update
|
|
||||||
import os
|
|
||||||
import socket
|
|
||||||
import stat
|
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
import urllib.error
|
|
||||||
import urllib.request
|
|
||||||
|
|
||||||
from openpilot.common.realtime import Ratekeeper
|
|
||||||
|
|
||||||
VERSION = 'v1'
|
|
||||||
VERSION_URL = f"https://github.com/FrogAi/FrogPilot-Resources/raw/master/mapd_version_{VERSION}.txt"
|
|
||||||
|
|
||||||
MAPD_PATH = '/data/media/0/osm/mapd'
|
|
||||||
VERSION_PATH = '/data/media/0/osm/mapd_version'
|
|
||||||
|
|
||||||
def github_pinged(url="https://github.com", timeout=5):
|
|
||||||
no_internet = 0
|
|
||||||
while True:
|
|
||||||
if no_internet > 5:
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
urllib.request.urlopen(url, timeout=timeout)
|
|
||||||
return True
|
|
||||||
except (urllib.error.URLError, socket.timeout):
|
|
||||||
no_internet += 1
|
|
||||||
time.sleep(10)
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_latest_version():
|
|
||||||
with urllib.request.urlopen(VERSION_URL) as response:
|
|
||||||
return response.read().decode('utf-8').strip()
|
|
||||||
|
|
||||||
def download(current_version):
|
|
||||||
url = f"https://github.com/pfeiferj/openpilot-mapd/releases/download/{current_version}/mapd"
|
|
||||||
|
|
||||||
mapd_dir = os.path.dirname(MAPD_PATH)
|
|
||||||
if not os.path.exists(mapd_dir):
|
|
||||||
os.makedirs(mapd_dir)
|
|
||||||
with urllib.request.urlopen(url) as f:
|
|
||||||
with open(MAPD_PATH, 'wb') as output:
|
|
||||||
output.write(f.read())
|
|
||||||
os.fsync(output)
|
|
||||||
current_permissions = stat.S_IMODE(os.lstat(MAPD_PATH).st_mode) # <-- preserve permissions
|
|
||||||
os.chmod(MAPD_PATH, current_permissions | stat.S_IEXEC) # <-- preserve permissions
|
|
||||||
with open(VERSION_PATH, 'w') as output:
|
|
||||||
output.write(current_version)
|
|
||||||
os.fsync(output)
|
|
||||||
|
|
||||||
def mapd_thread(sm=None, pm=None):
|
|
||||||
rk = Ratekeeper(0.05, print_delay_threshold=None)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
if github_pinged():
|
|
||||||
current_version = get_latest_version()
|
|
||||||
if not os.path.exists(MAPD_PATH):
|
|
||||||
download(current_version)
|
|
||||||
continue
|
|
||||||
if not os.path.exists(VERSION_PATH):
|
|
||||||
download(current_version)
|
|
||||||
continue
|
|
||||||
with open(VERSION_PATH) as f:
|
|
||||||
content = f.read()
|
|
||||||
if content != current_version:
|
|
||||||
download(current_version)
|
|
||||||
continue
|
|
||||||
|
|
||||||
process = subprocess.Popen(MAPD_PATH)
|
|
||||||
process.wait()
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
|
|
||||||
rk.keep_time()
|
|
||||||
|
|
||||||
|
|
||||||
def main(sm=None, pm=None):
|
|
||||||
mapd_thread(sm, pm)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 637 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 422 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 366 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 654 KiB |
@@ -1,329 +0,0 @@
|
|||||||
/****************************************************************************
|
|
||||||
** Meta object code from reading C++ file 'navigation_settings.h'
|
|
||||||
**
|
|
||||||
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.12.8)
|
|
||||||
**
|
|
||||||
** WARNING! All changes made in this file will be lost!
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "navigation_settings.h"
|
|
||||||
#include <QtCore/qbytearray.h>
|
|
||||||
#include <QtCore/qmetatype.h>
|
|
||||||
#if !defined(Q_MOC_OUTPUT_REVISION)
|
|
||||||
#error "The header file 'navigation_settings.h' doesn't include <QObject>."
|
|
||||||
#elif Q_MOC_OUTPUT_REVISION != 67
|
|
||||||
#error "This file was generated using the moc from 5.12.8. It"
|
|
||||||
#error "cannot be used with the include files from this version of Qt."
|
|
||||||
#error "(The moc has changed too much.)"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
QT_BEGIN_MOC_NAMESPACE
|
|
||||||
QT_WARNING_PUSH
|
|
||||||
QT_WARNING_DISABLE_DEPRECATED
|
|
||||||
struct qt_meta_stringdata_Primeless_t {
|
|
||||||
QByteArrayData data[3];
|
|
||||||
char stringdata0[21];
|
|
||||||
};
|
|
||||||
#define QT_MOC_LITERAL(idx, ofs, len) \
|
|
||||||
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
|
|
||||||
qptrdiff(offsetof(qt_meta_stringdata_Primeless_t, stringdata0) + ofs \
|
|
||||||
- idx * sizeof(QByteArrayData)) \
|
|
||||||
)
|
|
||||||
static const qt_meta_stringdata_Primeless_t qt_meta_stringdata_Primeless = {
|
|
||||||
{
|
|
||||||
QT_MOC_LITERAL(0, 0, 9), // "Primeless"
|
|
||||||
QT_MOC_LITERAL(1, 10, 9), // "backPress"
|
|
||||||
QT_MOC_LITERAL(2, 20, 0) // ""
|
|
||||||
|
|
||||||
},
|
|
||||||
"Primeless\0backPress\0"
|
|
||||||
};
|
|
||||||
#undef QT_MOC_LITERAL
|
|
||||||
|
|
||||||
static const uint qt_meta_data_Primeless[] = {
|
|
||||||
|
|
||||||
// content:
|
|
||||||
8, // revision
|
|
||||||
0, // classname
|
|
||||||
0, 0, // classinfo
|
|
||||||
1, 14, // methods
|
|
||||||
0, 0, // properties
|
|
||||||
0, 0, // enums/sets
|
|
||||||
0, 0, // constructors
|
|
||||||
0, // flags
|
|
||||||
1, // signalCount
|
|
||||||
|
|
||||||
// signals: name, argc, parameters, tag, flags
|
|
||||||
1, 0, 19, 2, 0x06 /* Public */,
|
|
||||||
|
|
||||||
// signals: parameters
|
|
||||||
QMetaType::Void,
|
|
||||||
|
|
||||||
0 // eod
|
|
||||||
};
|
|
||||||
|
|
||||||
void Primeless::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
|
|
||||||
{
|
|
||||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
|
||||||
auto *_t = static_cast<Primeless *>(_o);
|
|
||||||
Q_UNUSED(_t)
|
|
||||||
switch (_id) {
|
|
||||||
case 0: _t->backPress(); break;
|
|
||||||
default: ;
|
|
||||||
}
|
|
||||||
} else if (_c == QMetaObject::IndexOfMethod) {
|
|
||||||
int *result = reinterpret_cast<int *>(_a[0]);
|
|
||||||
{
|
|
||||||
using _t = void (Primeless::*)();
|
|
||||||
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Primeless::backPress)) {
|
|
||||||
*result = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Q_UNUSED(_a);
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_INIT_METAOBJECT const QMetaObject Primeless::staticMetaObject = { {
|
|
||||||
&QWidget::staticMetaObject,
|
|
||||||
qt_meta_stringdata_Primeless.data,
|
|
||||||
qt_meta_data_Primeless,
|
|
||||||
qt_static_metacall,
|
|
||||||
nullptr,
|
|
||||||
nullptr
|
|
||||||
} };
|
|
||||||
|
|
||||||
|
|
||||||
const QMetaObject *Primeless::metaObject() const
|
|
||||||
{
|
|
||||||
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *Primeless::qt_metacast(const char *_clname)
|
|
||||||
{
|
|
||||||
if (!_clname) return nullptr;
|
|
||||||
if (!strcmp(_clname, qt_meta_stringdata_Primeless.stringdata0))
|
|
||||||
return static_cast<void*>(this);
|
|
||||||
return QWidget::qt_metacast(_clname);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Primeless::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|
||||||
{
|
|
||||||
_id = QWidget::qt_metacall(_c, _id, _a);
|
|
||||||
if (_id < 0)
|
|
||||||
return _id;
|
|
||||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
|
||||||
if (_id < 1)
|
|
||||||
qt_static_metacall(this, _c, _id, _a);
|
|
||||||
_id -= 1;
|
|
||||||
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
|
|
||||||
if (_id < 1)
|
|
||||||
*reinterpret_cast<int*>(_a[0]) = -1;
|
|
||||||
_id -= 1;
|
|
||||||
}
|
|
||||||
return _id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SIGNAL 0
|
|
||||||
void Primeless::backPress()
|
|
||||||
{
|
|
||||||
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
|
|
||||||
}
|
|
||||||
struct qt_meta_stringdata_SelectMaps_t {
|
|
||||||
QByteArrayData data[4];
|
|
||||||
char stringdata0[30];
|
|
||||||
};
|
|
||||||
#define QT_MOC_LITERAL(idx, ofs, len) \
|
|
||||||
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
|
|
||||||
qptrdiff(offsetof(qt_meta_stringdata_SelectMaps_t, stringdata0) + ofs \
|
|
||||||
- idx * sizeof(QByteArrayData)) \
|
|
||||||
)
|
|
||||||
static const qt_meta_stringdata_SelectMaps_t qt_meta_stringdata_SelectMaps = {
|
|
||||||
{
|
|
||||||
QT_MOC_LITERAL(0, 0, 10), // "SelectMaps"
|
|
||||||
QT_MOC_LITERAL(1, 11, 9), // "backPress"
|
|
||||||
QT_MOC_LITERAL(2, 21, 0), // ""
|
|
||||||
QT_MOC_LITERAL(3, 22, 7) // "setMaps"
|
|
||||||
|
|
||||||
},
|
|
||||||
"SelectMaps\0backPress\0\0setMaps"
|
|
||||||
};
|
|
||||||
#undef QT_MOC_LITERAL
|
|
||||||
|
|
||||||
static const uint qt_meta_data_SelectMaps[] = {
|
|
||||||
|
|
||||||
// content:
|
|
||||||
8, // revision
|
|
||||||
0, // classname
|
|
||||||
0, 0, // classinfo
|
|
||||||
2, 14, // methods
|
|
||||||
0, 0, // properties
|
|
||||||
0, 0, // enums/sets
|
|
||||||
0, 0, // constructors
|
|
||||||
0, // flags
|
|
||||||
2, // signalCount
|
|
||||||
|
|
||||||
// signals: name, argc, parameters, tag, flags
|
|
||||||
1, 0, 24, 2, 0x06 /* Public */,
|
|
||||||
3, 0, 25, 2, 0x06 /* Public */,
|
|
||||||
|
|
||||||
// signals: parameters
|
|
||||||
QMetaType::Void,
|
|
||||||
QMetaType::Void,
|
|
||||||
|
|
||||||
0 // eod
|
|
||||||
};
|
|
||||||
|
|
||||||
void SelectMaps::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
|
|
||||||
{
|
|
||||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
|
||||||
auto *_t = static_cast<SelectMaps *>(_o);
|
|
||||||
Q_UNUSED(_t)
|
|
||||||
switch (_id) {
|
|
||||||
case 0: _t->backPress(); break;
|
|
||||||
case 1: _t->setMaps(); break;
|
|
||||||
default: ;
|
|
||||||
}
|
|
||||||
} else if (_c == QMetaObject::IndexOfMethod) {
|
|
||||||
int *result = reinterpret_cast<int *>(_a[0]);
|
|
||||||
{
|
|
||||||
using _t = void (SelectMaps::*)();
|
|
||||||
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SelectMaps::backPress)) {
|
|
||||||
*result = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
using _t = void (SelectMaps::*)();
|
|
||||||
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&SelectMaps::setMaps)) {
|
|
||||||
*result = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Q_UNUSED(_a);
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_INIT_METAOBJECT const QMetaObject SelectMaps::staticMetaObject = { {
|
|
||||||
&QWidget::staticMetaObject,
|
|
||||||
qt_meta_stringdata_SelectMaps.data,
|
|
||||||
qt_meta_data_SelectMaps,
|
|
||||||
qt_static_metacall,
|
|
||||||
nullptr,
|
|
||||||
nullptr
|
|
||||||
} };
|
|
||||||
|
|
||||||
|
|
||||||
const QMetaObject *SelectMaps::metaObject() const
|
|
||||||
{
|
|
||||||
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *SelectMaps::qt_metacast(const char *_clname)
|
|
||||||
{
|
|
||||||
if (!_clname) return nullptr;
|
|
||||||
if (!strcmp(_clname, qt_meta_stringdata_SelectMaps.stringdata0))
|
|
||||||
return static_cast<void*>(this);
|
|
||||||
return QWidget::qt_metacast(_clname);
|
|
||||||
}
|
|
||||||
|
|
||||||
int SelectMaps::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|
||||||
{
|
|
||||||
_id = QWidget::qt_metacall(_c, _id, _a);
|
|
||||||
if (_id < 0)
|
|
||||||
return _id;
|
|
||||||
if (_c == QMetaObject::InvokeMetaMethod) {
|
|
||||||
if (_id < 2)
|
|
||||||
qt_static_metacall(this, _c, _id, _a);
|
|
||||||
_id -= 2;
|
|
||||||
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
|
|
||||||
if (_id < 2)
|
|
||||||
*reinterpret_cast<int*>(_a[0]) = -1;
|
|
||||||
_id -= 2;
|
|
||||||
}
|
|
||||||
return _id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SIGNAL 0
|
|
||||||
void SelectMaps::backPress()
|
|
||||||
{
|
|
||||||
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// SIGNAL 1
|
|
||||||
void SelectMaps::setMaps()
|
|
||||||
{
|
|
||||||
QMetaObject::activate(this, &staticMetaObject, 1, nullptr);
|
|
||||||
}
|
|
||||||
struct qt_meta_stringdata_FrogPilotNavigationPanel_t {
|
|
||||||
QByteArrayData data[1];
|
|
||||||
char stringdata0[25];
|
|
||||||
};
|
|
||||||
#define QT_MOC_LITERAL(idx, ofs, len) \
|
|
||||||
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
|
|
||||||
qptrdiff(offsetof(qt_meta_stringdata_FrogPilotNavigationPanel_t, stringdata0) + ofs \
|
|
||||||
- idx * sizeof(QByteArrayData)) \
|
|
||||||
)
|
|
||||||
static const qt_meta_stringdata_FrogPilotNavigationPanel_t qt_meta_stringdata_FrogPilotNavigationPanel = {
|
|
||||||
{
|
|
||||||
QT_MOC_LITERAL(0, 0, 24) // "FrogPilotNavigationPanel"
|
|
||||||
|
|
||||||
},
|
|
||||||
"FrogPilotNavigationPanel"
|
|
||||||
};
|
|
||||||
#undef QT_MOC_LITERAL
|
|
||||||
|
|
||||||
static const uint qt_meta_data_FrogPilotNavigationPanel[] = {
|
|
||||||
|
|
||||||
// content:
|
|
||||||
8, // revision
|
|
||||||
0, // classname
|
|
||||||
0, 0, // classinfo
|
|
||||||
0, 0, // methods
|
|
||||||
0, 0, // properties
|
|
||||||
0, 0, // enums/sets
|
|
||||||
0, 0, // constructors
|
|
||||||
0, // flags
|
|
||||||
0, // signalCount
|
|
||||||
|
|
||||||
0 // eod
|
|
||||||
};
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
|
|
||||||
{
|
|
||||||
Q_UNUSED(_o);
|
|
||||||
Q_UNUSED(_id);
|
|
||||||
Q_UNUSED(_c);
|
|
||||||
Q_UNUSED(_a);
|
|
||||||
}
|
|
||||||
|
|
||||||
QT_INIT_METAOBJECT const QMetaObject FrogPilotNavigationPanel::staticMetaObject = { {
|
|
||||||
&QFrame::staticMetaObject,
|
|
||||||
qt_meta_stringdata_FrogPilotNavigationPanel.data,
|
|
||||||
qt_meta_data_FrogPilotNavigationPanel,
|
|
||||||
qt_static_metacall,
|
|
||||||
nullptr,
|
|
||||||
nullptr
|
|
||||||
} };
|
|
||||||
|
|
||||||
|
|
||||||
const QMetaObject *FrogPilotNavigationPanel::metaObject() const
|
|
||||||
{
|
|
||||||
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *FrogPilotNavigationPanel::qt_metacast(const char *_clname)
|
|
||||||
{
|
|
||||||
if (!_clname) return nullptr;
|
|
||||||
if (!strcmp(_clname, qt_meta_stringdata_FrogPilotNavigationPanel.stringdata0))
|
|
||||||
return static_cast<void*>(this);
|
|
||||||
return QFrame::qt_metacast(_clname);
|
|
||||||
}
|
|
||||||
|
|
||||||
int FrogPilotNavigationPanel::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
|
|
||||||
{
|
|
||||||
_id = QFrame::qt_metacall(_c, _id, _a);
|
|
||||||
return _id;
|
|
||||||
}
|
|
||||||
QT_WARNING_POP
|
|
||||||
QT_END_MOC_NAMESPACE
|
|
||||||
@@ -1,311 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QDirIterator>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
|
||||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
|
||||||
#include "selfdrive/ui/ui.h"
|
|
||||||
|
|
||||||
QMap<QString, QString> northeastMap = {
|
|
||||||
{"CT", "Connecticut"}, {"DE", "Delaware"}, {"MA", "Massachusetts"},
|
|
||||||
{"MD", "Maryland"}, {"ME", "Maine"}, {"NH", "New Hampshire"},
|
|
||||||
{"NJ", "New Jersey"}, {"NY", "New York"}, {"PA", "Pennsylvania"},
|
|
||||||
{"RI", "Rhode Island"}, {"VT", "Vermont"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> midwestMap = {
|
|
||||||
{"IA", "Iowa"}, {"IL", "Illinois"}, {"IN", "Indiana"},
|
|
||||||
{"KS", "Kansas"}, {"MI", "Michigan"}, {"MN", "Minnesota"},
|
|
||||||
{"MO", "Missouri"}, {"ND", "North Dakota"}, {"NE", "Nebraska"},
|
|
||||||
{"OH", "Ohio"}, {"SD", "South Dakota"}, {"WI", "Wisconsin"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> southMap = {
|
|
||||||
{"AL", "Alabama"}, {"AR", "Arkansas"}, {"FL", "Florida"},
|
|
||||||
{"GA", "Georgia"}, {"KY", "Kentucky"}, {"LA", "Louisiana"},
|
|
||||||
{"MS", "Mississippi"}, {"NC", "North Carolina"}, {"OK", "Oklahoma"},
|
|
||||||
{"SC", "South Carolina"}, {"TN", "Tennessee"}, {"TX", "Texas"},
|
|
||||||
{"VA", "Virginia"}, {"WV", "West Virginia"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> westMap = {
|
|
||||||
{"AK", "Alaska"}, {"AZ", "Arizona"}, {"CA", "California"},
|
|
||||||
{"CO", "Colorado"}, {"HI", "Hawaii"}, {"ID", "Idaho"},
|
|
||||||
{"MT", "Montana"}, {"NM", "New Mexico"}, {"NV", "Nevada"},
|
|
||||||
{"OR", "Oregon"}, {"UT", "Utah"}, {"WA", "Washington"},
|
|
||||||
{"WY", "Wyoming"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> territoriesMap = {
|
|
||||||
{"AS", "American Samoa"}, {"DC", "District of Columbia"},
|
|
||||||
{"GM", "Guam"}, {"MP", "North Mariana Islands"},
|
|
||||||
{"PR", "Puerto Rico"}, {"VI", "Virgin Islands"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> africaMap = {
|
|
||||||
{"DZ", "Algeria"}, {"AO", "Angola"}, {"BJ", "Benin"}, {"BW", "Botswana"},
|
|
||||||
{"BF", "Burkina Faso"}, {"BI", "Burundi"}, {"CM", "Cameroon"}, {"CV", "Cape Verde"},
|
|
||||||
{"CF", "Central African Republic"}, {"TD", "Chad"}, {"KM", "Comoros"}, {"CG", "Congo (Brazzaville)"},
|
|
||||||
{"CD", "Congo (Kinshasa)"}, {"CI", "Ivory Coast"}, {"DJ", "Djibouti"}, {"EG", "Egypt"},
|
|
||||||
{"GQ", "Equatorial Guinea"}, {"ER", "Eritrea"}, {"SZ", "Eswatini"}, {"ET", "Ethiopia"},
|
|
||||||
{"GA", "Gabon"}, {"GM", "Gambia"}, {"GH", "Ghana"}, {"GN", "Guinea"},
|
|
||||||
{"GW", "Guinea-Bissau"}, {"KE", "Kenya"}, {"LS", "Lesotho"}, {"LR", "Liberia"},
|
|
||||||
{"LY", "Libya"}, {"MG", "Madagascar"}, {"MW", "Malawi"}, {"ML", "Mali"},
|
|
||||||
{"MR", "Mauritania"}, {"MU", "Mauritius"}, {"MA", "Morocco"}, {"MZ", "Mozambique"},
|
|
||||||
{"NA", "Namibia"}, {"NE", "Niger"}, {"NG", "Nigeria"}, {"RW", "Rwanda"},
|
|
||||||
{"ST", "Sao Tome and Principe"}, {"SN", "Senegal"}, {"SC", "Seychelles"}, {"SL", "Sierra Leone"},
|
|
||||||
{"SO", "Somalia"}, {"ZA", "South Africa"}, {"SS", "South Sudan"}, {"SD", "Sudan"},
|
|
||||||
{"TZ", "Tanzania"}, {"TG", "Togo"}, {"TN", "Tunisia"}, {"UG", "Uganda"},
|
|
||||||
{"EH", "Western Sahara"}, {"ZM", "Zambia"}, {"ZW", "Zimbabwe"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> antarcticaMap = {
|
|
||||||
{"AQ", "Antarctica"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> asiaMap = {
|
|
||||||
{"AF", "Afghanistan"}, {"AM", "Armenia"}, {"AZ", "Azerbaijan"}, {"BH", "Bahrain"},
|
|
||||||
{"BD", "Bangladesh"}, {"BT", "Bhutan"}, {"BN", "Brunei"}, {"KH", "Cambodia"},
|
|
||||||
{"CN", "China"}, {"CY", "Cyprus"}, {"GE", "Georgia"}, {"IN", "India"},
|
|
||||||
{"ID", "Indonesia"}, {"IR", "Iran"}, {"IQ", "Iraq"}, {"IL", "Israel"},
|
|
||||||
{"JP", "Japan"}, {"JO", "Jordan"}, {"KZ", "Kazakhstan"}, {"KP", "North Korea"},
|
|
||||||
{"KR", "South Korea"}, {"KW", "Kuwait"}, {"KG", "Kyrgyzstan"}, {"LA", "Laos"},
|
|
||||||
{"LB", "Lebanon"}, {"MY", "Malaysia"}, {"MV", "Maldives"}, {"MN", "Mongolia"},
|
|
||||||
{"MM", "Myanmar"}, {"NP", "Nepal"}, {"OM", "Oman"}, {"PK", "Pakistan"},
|
|
||||||
{"PS", "Palestine"}, {"PH", "Philippines"}, {"QA", "Qatar"}, {"SA", "Saudi Arabia"},
|
|
||||||
{"SG", "Singapore"}, {"LK", "Sri Lanka"}, {"SY", "Syria"}, {"TW", "Taiwan"},
|
|
||||||
{"TJ", "Tajikistan"}, {"TH", "Thailand"}, {"TL", "Timor-Leste"}, {"TR", "Turkey"},
|
|
||||||
{"TM", "Turkmenistan"}, {"AE", "United Arab Emirates"}, {"UZ", "Uzbekistan"}, {"VN", "Vietnam"},
|
|
||||||
{"YE", "Yemen"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> europeMap = {
|
|
||||||
{"AL", "Albania"}, {"AD", "Andorra"}, {"AT", "Austria"}, {"BY", "Belarus"},
|
|
||||||
{"BE", "Belgium"}, {"BA", "Bosnia and Herzegovina"}, {"BG", "Bulgaria"}, {"HR", "Croatia"},
|
|
||||||
{"CY", "Cyprus"}, {"CZ", "Czech Republic"}, {"DK", "Denmark"}, {"EE", "Estonia"},
|
|
||||||
{"FI", "Finland"}, {"FR", "France"}, {"DE", "Germany"}, {"GR", "Greece"},
|
|
||||||
{"HU", "Hungary"}, {"IS", "Iceland"}, {"IE", "Ireland"}, {"IT", "Italy"},
|
|
||||||
{"LV", "Latvia"}, {"LI", "Liechtenstein"}, {"LT", "Lithuania"}, {"LU", "Luxembourg"},
|
|
||||||
{"MT", "Malta"}, {"MD", "Moldova"}, {"MC", "Monaco"}, {"ME", "Montenegro"},
|
|
||||||
{"NL", "Netherlands"}, {"MK", "North Macedonia"}, {"NO", "Norway"}, {"PL", "Poland"},
|
|
||||||
{"PT", "Portugal"}, {"RO", "Romania"}, {"RU", "Russia"}, {"SM", "San Marino"},
|
|
||||||
{"RS", "Serbia"}, {"SK", "Slovakia"}, {"SI", "Slovenia"}, {"ES", "Spain"},
|
|
||||||
{"SE", "Sweden"}, {"CH", "Switzerland"}, {"TR", "Turkey"}, {"UA", "Ukraine"},
|
|
||||||
{"GB", "United Kingdom"}, {"VA", "Vatican City"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> northAmericaMap = {
|
|
||||||
{"AG", "Antigua and Barbuda"}, {"BS", "Bahamas"}, {"BB", "Barbados"}, {"BZ", "Belize"},
|
|
||||||
{"CA", "Canada"}, {"CR", "Costa Rica"}, {"CU", "Cuba"}, {"DM", "Dominica"},
|
|
||||||
{"DO", "Dominican Republic"}, {"SV", "El Salvador"}, {"GD", "Grenada"}, {"GT", "Guatemala"},
|
|
||||||
{"HT", "Haiti"}, {"HN", "Honduras"}, {"JM", "Jamaica"}, {"MX", "Mexico"},
|
|
||||||
{"NI", "Nicaragua"}, {"PA", "Panama"}, {"KN", "Saint Kitts and Nevis"}, {"LC", "Saint Lucia"},
|
|
||||||
{"VC", "Saint Vincent and the Grenadines"}, {"TT", "Trinidad and Tobago"}, {"US", "United States"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> oceaniaMap = {
|
|
||||||
{"AU", "Australia"}, {"FJ", "Fiji"}, {"KI", "Kiribati"}, {"MH", "Marshall Islands"},
|
|
||||||
{"FM", "Micronesia"}, {"NR", "Nauru"}, {"NZ", "New Zealand"}, {"PW", "Palau"},
|
|
||||||
{"PG", "Papua New Guinea"}, {"WS", "Samoa"}, {"SB", "Solomon Islands"}, {"TO", "Tonga"},
|
|
||||||
{"TV", "Tuvalu"}, {"VU", "Vanuatu"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QMap<QString, QString> southAmericaMap = {
|
|
||||||
{"AR", "Argentina"}, {"BO", "Bolivia"}, {"BR", "Brazil"}, {"CL", "Chile"},
|
|
||||||
{"CO", "Colombia"}, {"EC", "Ecuador"}, {"GY", "Guyana"}, {"PY", "Paraguay"},
|
|
||||||
{"PE", "Peru"}, {"SR", "Suriname"}, {"TT", "Trinidad and Tobago"}, {"UY", "Uruguay"},
|
|
||||||
{"VE", "Venezuela"}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ButtonSelectionControl : public QWidget {
|
|
||||||
public:
|
|
||||||
static QString selectedStates;
|
|
||||||
static QString selectedCountries;
|
|
||||||
|
|
||||||
explicit ButtonSelectionControl(const QString &id, const QString &title, const QString &description,
|
|
||||||
const QMap<QString, QString> &map, bool isCountry, QWidget *parent = nullptr)
|
|
||||||
: QWidget(parent), country(isCountry) {
|
|
||||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
|
||||||
layout->setAlignment(Qt::AlignTop);
|
|
||||||
layout->setSpacing(10);
|
|
||||||
|
|
||||||
QHBoxLayout *buttonsLayout = new QHBoxLayout();
|
|
||||||
buttonsLayout->setSpacing(10);
|
|
||||||
layout->addLayout(buttonsLayout);
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
int max = country ? 3 : 4;
|
|
||||||
|
|
||||||
QJsonObject mapsSelected = QJsonDocument::fromJson(QString::fromStdString(Params().get("MapsSelected")).toUtf8()).object();
|
|
||||||
|
|
||||||
for (const QString &stateCode : map.keys()) {
|
|
||||||
if (count % max == 0 && count != 0) {
|
|
||||||
buttonsLayout = new QHBoxLayout();
|
|
||||||
buttonsLayout->setSpacing(10);
|
|
||||||
layout->addLayout(buttonsLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
QPushButton *button = createButton(buttonsLayout, map[stateCode], stateCode);
|
|
||||||
|
|
||||||
QString key = country ? "nations" : "states";
|
|
||||||
if (mapsSelected.contains(key)) {
|
|
||||||
QJsonArray selectedItems = mapsSelected.value(key).toArray();
|
|
||||||
button->setChecked(selectedItems.contains(stateCode));
|
|
||||||
}
|
|
||||||
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
adjustButtonWidths(buttonsLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool country;
|
|
||||||
|
|
||||||
const QString buttonStyle = R"(
|
|
||||||
QPushButton {
|
|
||||||
border-radius: 50px; font-size: 40px; font-weight: 500;
|
|
||||||
height: 100px; padding: 0 25 0 25; color: #E4E4E4;
|
|
||||||
background-color: #393939;
|
|
||||||
}
|
|
||||||
QPushButton:pressed, QPushButton:checked {
|
|
||||||
background-color: #4a4a4a;
|
|
||||||
}
|
|
||||||
QPushButton:checked:enabled {
|
|
||||||
background-color: #33Ab4C;
|
|
||||||
}
|
|
||||||
QPushButton:disabled {
|
|
||||||
color: #33E4E4E4;
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
QPushButton *createButton(QHBoxLayout *layout, const QString &label, const QString &stateCode) {
|
|
||||||
QPushButton *button = new QPushButton(label, this);
|
|
||||||
button->setCheckable(true);
|
|
||||||
button->setStyleSheet(buttonStyle);
|
|
||||||
connect(button, &QPushButton::clicked, this, [this, button, stateCode] { updateState(stateCode, button); });
|
|
||||||
layout->addWidget(button);
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
void adjustButtonWidths(QHBoxLayout *layout) {
|
|
||||||
if (!layout || layout->count() == (country ? 3 : 4)) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < layout->count(); ++i) {
|
|
||||||
QWidget *widget = layout->itemAt(i)->widget();
|
|
||||||
QPushButton *button = qobject_cast<QPushButton *>(widget);
|
|
||||||
if (button) {
|
|
||||||
button->setMinimumWidth(button->sizeHint().width());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateState(const QString &newState, QPushButton *button) {
|
|
||||||
QString &selectedList = country ? selectedCountries : selectedStates;
|
|
||||||
QStringList tempList = selectedList.split(',');
|
|
||||||
|
|
||||||
if (button->isChecked()) {
|
|
||||||
if (!selectedList.isEmpty()) selectedList += ",";
|
|
||||||
selectedList += newState;
|
|
||||||
} else {
|
|
||||||
tempList.removeAll(newState);
|
|
||||||
selectedList = tempList.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
Params("/dev/shm/params").remove("OSMDownloadLocations");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
QString ButtonSelectionControl::selectedStates = "";
|
|
||||||
QString ButtonSelectionControl::selectedCountries = "";
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template <typename T>
|
|
||||||
T extractFromJson(const std::string &jsonData, const std::string &key, T defaultValue = 0) {
|
|
||||||
std::string::size_type pos = jsonData.find(key);
|
|
||||||
return pos != std::string::npos ? std::stol(jsonData.substr(pos + key.length())) : defaultValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString formatTime(long timeInSeconds) {
|
|
||||||
long minutes = timeInSeconds / 60;
|
|
||||||
long seconds = timeInSeconds % 60;
|
|
||||||
QString formattedTime = (minutes > 0) ? QString::number(minutes) + "m " : "";
|
|
||||||
formattedTime += QString::number(seconds) + "s";
|
|
||||||
return formattedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString formatDateTime(const std::chrono::time_point<std::chrono::system_clock> &timePoint) {
|
|
||||||
return QDateTime::fromTime_t(std::chrono::system_clock::to_time_t(timePoint)).toString("h:mm ap");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString calculateElapsedTime(int totalFiles, int downloadedFiles, const std::chrono::steady_clock::time_point &startTime) {
|
|
||||||
using namespace std::chrono;
|
|
||||||
if (totalFiles <= 0 || downloadedFiles >= totalFiles) return "Calculating...";
|
|
||||||
|
|
||||||
long elapsed = duration_cast<seconds>(steady_clock::now() - startTime).count();
|
|
||||||
return formatTime(elapsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString calculateETA(int totalFiles, int downloadedFiles, const std::chrono::steady_clock::time_point &startTime) {
|
|
||||||
using namespace std::chrono;
|
|
||||||
if (totalFiles <= 0 || downloadedFiles >= totalFiles) return "Calculating...";
|
|
||||||
|
|
||||||
long elapsed = duration_cast<seconds>(steady_clock::now() - startTime).count();
|
|
||||||
|
|
||||||
if (downloadedFiles == 0 || elapsed <= 0) {
|
|
||||||
return "Calculating...";
|
|
||||||
}
|
|
||||||
|
|
||||||
double averageTimePerFile = static_cast<double>(elapsed) / downloadedFiles;
|
|
||||||
int remainingFiles = totalFiles - downloadedFiles;
|
|
||||||
long estimatedTimeRemaining = static_cast<long>(averageTimePerFile * remainingFiles);
|
|
||||||
|
|
||||||
std::chrono::time_point<std::chrono::system_clock> estimatedCompletionTime = system_clock::now() + seconds(estimatedTimeRemaining);
|
|
||||||
QString estimatedTimeStr = formatDateTime(estimatedCompletionTime);
|
|
||||||
|
|
||||||
return formatTime(estimatedTimeRemaining) + " (" + estimatedTimeStr + ")";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QString formatDownloadStatus(int totalFiles, int downloadedFiles) {
|
|
||||||
if (totalFiles <= 0) return "Calculating...";
|
|
||||||
if (downloadedFiles >= totalFiles) return "Downloaded";
|
|
||||||
|
|
||||||
int percentage = static_cast<int>(100 * downloadedFiles / totalFiles);
|
|
||||||
return QString::asprintf("Downloading: %d/%d (%d%%)", downloadedFiles, totalFiles, percentage);
|
|
||||||
}
|
|
||||||
|
|
||||||
quint64 calculateDirectorySize(const QString &path) {
|
|
||||||
quint64 totalSize = 0;
|
|
||||||
QDirIterator it(path, QDir::Files, QDirIterator::Subdirectories);
|
|
||||||
while (it.hasNext()) {
|
|
||||||
it.next();
|
|
||||||
QFileInfo fileInfo(it.filePath());
|
|
||||||
if (fileInfo.isFile()) {
|
|
||||||
totalSize += fileInfo.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return totalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString formatSize(qint64 size) {
|
|
||||||
const qint64 kb = 1024;
|
|
||||||
const qint64 mb = 1024 * kb;
|
|
||||||
const qint64 gb = 1024 * mb;
|
|
||||||
|
|
||||||
if (size < gb) {
|
|
||||||
double sizeMB = size / static_cast<double>(mb);
|
|
||||||
return QString::number(sizeMB, 'f', 2) + " MB";
|
|
||||||
} else {
|
|
||||||
double sizeGB = size / static_cast<double>(gb);
|
|
||||||
return QString::number(sizeGB, 'f', 2) + " GB";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,574 +0,0 @@
|
|||||||
#include <QMouseEvent>
|
|
||||||
|
|
||||||
#include "selfdrive/frogpilot/navigation/ui/navigation_functions.h"
|
|
||||||
#include "selfdrive/frogpilot/navigation/ui/navigation_settings.h"
|
|
||||||
|
|
||||||
FrogPilotNavigationPanel::FrogPilotNavigationPanel(QWidget *parent) : QFrame(parent), scene(uiState()->scene) {
|
|
||||||
mainLayout = new QStackedLayout(this);
|
|
||||||
|
|
||||||
navigationWidget = new QWidget();
|
|
||||||
QVBoxLayout *navigationLayout = new QVBoxLayout(navigationWidget);
|
|
||||||
navigationLayout->setMargin(40);
|
|
||||||
|
|
||||||
FrogPilotListWidget *list = new FrogPilotListWidget(navigationWidget);
|
|
||||||
|
|
||||||
Primeless *primelessPanel = new Primeless(this);
|
|
||||||
mainLayout->addWidget(primelessPanel);
|
|
||||||
|
|
||||||
ButtonControl *manageNOOButton = new ButtonControl(tr("Manage Primeless Navigation Settings"), tr("MANAGE"), tr("Manage primeless navigate on openpilot settings."));
|
|
||||||
QObject::connect(manageNOOButton, &ButtonControl::clicked, [=]() { mainLayout->setCurrentWidget(primelessPanel); });
|
|
||||||
QObject::connect(primelessPanel, &Primeless::backPress, [=]() { mainLayout->setCurrentWidget(navigationWidget); });
|
|
||||||
list->addItem(manageNOOButton);
|
|
||||||
manageNOOButton->setVisible(!uiState()->hasPrime());
|
|
||||||
|
|
||||||
std::vector<QString> scheduleOptions{tr("Manually"), tr("Weekly"), tr("Monthly")};
|
|
||||||
ButtonParamControl *preferredSchedule = new ButtonParamControl("PreferredSchedule", tr("Maps Scheduler"),
|
|
||||||
tr("Choose the frequency for updating maps with the latest OpenStreetMap (OSM) changes. "
|
|
||||||
"Weekly updates begin at midnight every Sunday, while monthly updates start at midnight on the 1st of each month. "
|
|
||||||
"If your device is off or not connected to WiFi during a scheduled update, the download will be conducted the next "
|
|
||||||
"time you're offroad with a WiFi connection."),
|
|
||||||
"",
|
|
||||||
scheduleOptions);
|
|
||||||
schedule = params.getInt("PreferredSchedule");
|
|
||||||
schedulePending = params.getBool("SchedulePending");
|
|
||||||
list->addItem(preferredSchedule);
|
|
||||||
|
|
||||||
list->addItem(offlineMapsSize = new LabelControl(tr("Offline Maps Size"), formatSize(calculateDirectorySize(offlineFolderPath))));
|
|
||||||
offlineMapsSize->setVisible(true);
|
|
||||||
list->addItem(lastMapsDownload = new LabelControl(tr("Last Download"), ""));
|
|
||||||
lastMapsDownload->setVisible(!params.get("LastMapsUpdate").empty());
|
|
||||||
lastMapsDownload->setText(QString::fromStdString(params.get("LastMapsUpdate")));
|
|
||||||
list->addItem(offlineMapsStatus = new LabelControl(tr("Offline Maps Status"), ""));
|
|
||||||
offlineMapsStatus->setVisible(false);
|
|
||||||
list->addItem(offlineMapsETA = new LabelControl(tr("Offline Maps ETA"), ""));
|
|
||||||
offlineMapsETA->setVisible(false);
|
|
||||||
list->addItem(offlineMapsElapsed = new LabelControl(tr("Time Elapsed"), ""));
|
|
||||||
offlineMapsElapsed->setVisible(false);
|
|
||||||
|
|
||||||
cancelDownloadButton = new ButtonControl(tr("Cancel Download"), tr("CANCEL"), tr("Cancel your current download."));
|
|
||||||
QObject::connect(cancelDownloadButton, &ButtonControl::clicked, [this] { cancelDownload(this); });
|
|
||||||
list->addItem(cancelDownloadButton);
|
|
||||||
cancelDownloadButton->setVisible(false);
|
|
||||||
|
|
||||||
downloadOfflineMapsButton = new ButtonControl(tr("Download Offline Maps"), tr("DOWNLOAD"), tr("Download your selected offline maps to use with openpilot."));
|
|
||||||
QObject::connect(downloadOfflineMapsButton, &ButtonControl::clicked, [this] { downloadMaps(); });
|
|
||||||
list->addItem(downloadOfflineMapsButton);
|
|
||||||
downloadOfflineMapsButton->setVisible(!params.get("MapsSelected").empty());
|
|
||||||
|
|
||||||
SelectMaps *mapsPanel = new SelectMaps(this);
|
|
||||||
mainLayout->addWidget(mapsPanel);
|
|
||||||
|
|
||||||
QObject::connect(mapsPanel, &SelectMaps::setMaps, [=]() { setMaps(); });
|
|
||||||
|
|
||||||
ButtonControl *selectMapsButton = new ButtonControl(tr("Select Offline Maps"), tr("SELECT"), tr("Select your maps to use with OSM."));
|
|
||||||
QObject::connect(selectMapsButton, &ButtonControl::clicked, [=]() { mainLayout->setCurrentWidget(mapsPanel); });
|
|
||||||
QObject::connect(mapsPanel, &SelectMaps::backPress, [=]() { mainLayout->setCurrentWidget(navigationWidget); });
|
|
||||||
list->addItem(selectMapsButton);
|
|
||||||
|
|
||||||
removeOfflineMapsButton = new ButtonControl(tr("Remove Offline Maps"), tr("REMOVE"), tr("Remove your downloaded offline maps to clear up storage space."));
|
|
||||||
QObject::connect(removeOfflineMapsButton, &ButtonControl::clicked, [this] { removeMaps(this); });
|
|
||||||
list->addItem(removeOfflineMapsButton);
|
|
||||||
removeOfflineMapsButton->setVisible(QDir(offlineFolderPath).exists());
|
|
||||||
|
|
||||||
navigationLayout->addWidget(new ScrollView(list, navigationWidget));
|
|
||||||
navigationWidget->setLayout(navigationLayout);
|
|
||||||
mainLayout->addWidget(navigationWidget);
|
|
||||||
mainLayout->setCurrentWidget(navigationWidget);
|
|
||||||
|
|
||||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &FrogPilotNavigationPanel::updateState);
|
|
||||||
|
|
||||||
checkIfUpdateMissed();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::hideEvent(QHideEvent *event) {
|
|
||||||
QWidget::hideEvent(event);
|
|
||||||
mainLayout->setCurrentWidget(navigationWidget);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::updateState() {
|
|
||||||
if (!isVisible() && downloadActive) updateVisibility(downloadActive);
|
|
||||||
if (downloadActive) updateStatuses();
|
|
||||||
if (schedule) downloadSchedule();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::updateStatuses() {
|
|
||||||
static std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
|
|
||||||
osmDownloadProgress = params.get("OSMDownloadProgress");
|
|
||||||
|
|
||||||
const int totalFiles = extractFromJson<int>(osmDownloadProgress, "\"total_files\":");
|
|
||||||
const int downloadedFiles = extractFromJson<int>(osmDownloadProgress, "\"downloaded_files\":");
|
|
||||||
|
|
||||||
if (paramsMemory.get("OSMDownloadLocations").empty()) {
|
|
||||||
downloadActive = false;
|
|
||||||
updateDownloadedLabel();
|
|
||||||
qint64 fileSize = calculateDirectorySize(offlineFolderPath);
|
|
||||||
offlineMapsSize->setText(formatSize(fileSize));
|
|
||||||
previousOSMDownloadProgress = osmDownloadProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (osmDownloadProgress != previousOSMDownloadProgress && isVisible()) {
|
|
||||||
qint64 fileSize = calculateDirectorySize(offlineFolderPath);
|
|
||||||
offlineMapsSize->setText(formatSize(fileSize));
|
|
||||||
previousOSMDownloadProgress = osmDownloadProgress;
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsedTime = calculateElapsedTime(totalFiles, downloadedFiles, startTime);
|
|
||||||
|
|
||||||
offlineMapsElapsed->setText(elapsedTime);
|
|
||||||
offlineMapsETA->setText(calculateETA(totalFiles, downloadedFiles, startTime));
|
|
||||||
offlineMapsStatus->setText(formatDownloadStatus(totalFiles, downloadedFiles));
|
|
||||||
|
|
||||||
if (downloadActive != previousDownloadActive) {
|
|
||||||
startTime = !downloadActive ? std::chrono::steady_clock::now() : startTime;
|
|
||||||
updateVisibility(downloadActive);
|
|
||||||
previousDownloadActive = downloadActive;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::updateVisibility(bool visibility) {
|
|
||||||
cancelDownloadButton->setVisible(visibility);
|
|
||||||
offlineMapsElapsed->setVisible(visibility);
|
|
||||||
offlineMapsETA->setVisible(visibility);
|
|
||||||
offlineMapsStatus->setVisible(visibility);
|
|
||||||
downloadOfflineMapsButton->setVisible(!visibility);
|
|
||||||
lastMapsDownload->setVisible(QDir(offlineFolderPath).exists() && !downloadActive);
|
|
||||||
removeOfflineMapsButton->setVisible(QDir(offlineFolderPath).exists() && !downloadActive);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::checkIfUpdateMissed() {
|
|
||||||
std::string lastMapsUpdate = params.get("LastMapsUpdate");
|
|
||||||
|
|
||||||
if (lastMapsUpdate.empty() || schedule == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::time_t currentTime = std::time(nullptr);
|
|
||||||
std::tm *now = std::localtime(¤tTime);
|
|
||||||
|
|
||||||
std::tm lastUpdate = {};
|
|
||||||
sscanf(lastMapsUpdate.c_str(), "%d-%d-%d", &lastUpdate.tm_year, &lastUpdate.tm_mon, &lastUpdate.tm_mday);
|
|
||||||
lastUpdate.tm_year -= 1900;
|
|
||||||
lastUpdate.tm_mon -= 1;
|
|
||||||
|
|
||||||
std::time_t lastUpdateTime = std::mktime(&lastUpdate);
|
|
||||||
std::tm *lastUpdateDay = std::localtime(&lastUpdateTime);
|
|
||||||
|
|
||||||
if (schedule == 1) {
|
|
||||||
schedulePending = (now->tm_wday == 0 && lastUpdateDay->tm_wday != 0) || (now->tm_wday > lastUpdateDay->tm_wday);
|
|
||||||
} else if (schedule == 2) {
|
|
||||||
schedulePending = (now->tm_mday == 1 && lastUpdate.tm_mday != 1) || (now->tm_mon != lastUpdate.tm_mon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::updateDownloadedLabel() {
|
|
||||||
std::time_t t = std::time(nullptr);
|
|
||||||
std::tm now = *std::localtime(&t);
|
|
||||||
char dateBuffer[11];
|
|
||||||
std::strftime(dateBuffer, sizeof(dateBuffer), "%Y-%m-%d", &now);
|
|
||||||
QDate date = QDate::fromString(dateBuffer, "yyyy-MM-dd");
|
|
||||||
|
|
||||||
int day = date.day();
|
|
||||||
std::string suffix = (day == 1 || day == 21 || day == 31) ? "st" :
|
|
||||||
(day == 2 || day == 22) ? "nd" :
|
|
||||||
(day == 3 || day == 23) ? "rd" : "th";
|
|
||||||
std::string lastMapsUpdate = date.toString("MMMM d").toStdString() + suffix + date.toString(", yyyy").toStdString();
|
|
||||||
lastMapsDownload->setText(QString::fromStdString(lastMapsUpdate));
|
|
||||||
params.put("LastMapsUpdate", lastMapsUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::downloadSchedule() {
|
|
||||||
const bool wifi = (*uiState()->sm)["deviceState"].getDeviceState().getNetworkType() == cereal::DeviceState::NetworkType::WIFI;
|
|
||||||
|
|
||||||
const std::time_t t = std::time(nullptr);
|
|
||||||
const std::tm *now = std::localtime(&t);
|
|
||||||
|
|
||||||
const bool isScheduleTime = (schedule == 1 && now->tm_wday == 0) || (schedule == 2 && now->tm_mday == 1);
|
|
||||||
|
|
||||||
if ((isScheduleTime || schedulePending) && !(scene.started || scheduleCompleted) && wifi) {
|
|
||||||
downloadMaps();
|
|
||||||
scheduleCompleted = true;
|
|
||||||
|
|
||||||
if (schedulePending) {
|
|
||||||
schedulePending = false;
|
|
||||||
params.putBool("SchedulePending", false);
|
|
||||||
}
|
|
||||||
} else if (!isScheduleTime) {
|
|
||||||
scheduleCompleted = false;
|
|
||||||
} else {
|
|
||||||
if (!schedulePending) {
|
|
||||||
params.putBool("SchedulePending", true);
|
|
||||||
}
|
|
||||||
schedulePending = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::cancelDownload(QWidget *parent) {
|
|
||||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to cancel the download?"), parent)) {
|
|
||||||
std::thread([&] {
|
|
||||||
std::system("pkill mapd");
|
|
||||||
}).detach();
|
|
||||||
if (FrogPilotConfirmationDialog::toggle(tr("Reboot required to re-enable map downloads"), tr("Reboot Now"), parent)) {
|
|
||||||
Hardware::soft_reboot();
|
|
||||||
}
|
|
||||||
downloadActive = false;
|
|
||||||
updateVisibility(downloadActive);
|
|
||||||
downloadOfflineMapsButton->setVisible(downloadActive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::downloadMaps() {
|
|
||||||
params.remove("OSMDownloadProgress");
|
|
||||||
paramsMemory.put("OSMDownloadLocations", params.get("MapsSelected"));
|
|
||||||
removeOfflineMapsButton->setVisible(true);
|
|
||||||
downloadActive = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::removeMaps(QWidget *parent) {
|
|
||||||
if (FrogPilotConfirmationDialog::yesorno(tr("Are you sure you want to delete all of your downloaded maps?"), parent)) {
|
|
||||||
std::thread([&] {
|
|
||||||
lastMapsDownload->setVisible(false);
|
|
||||||
removeOfflineMapsButton->setVisible(false);
|
|
||||||
offlineMapsSize->setText(formatSize(0));
|
|
||||||
params.remove("LastMapsUpdate");
|
|
||||||
std::system("rm -rf /data/media/0/osm/offline");
|
|
||||||
}).detach();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FrogPilotNavigationPanel::setMaps() {
|
|
||||||
std::thread([&] {
|
|
||||||
QStringList states = ButtonSelectionControl::selectedStates.split(',', QString::SkipEmptyParts);
|
|
||||||
QStringList countries = ButtonSelectionControl::selectedCountries.split(',', QString::SkipEmptyParts);
|
|
||||||
|
|
||||||
QJsonObject json;
|
|
||||||
json.insert("states", QJsonArray::fromStringList(states));
|
|
||||||
json.insert("nations", QJsonArray::fromStringList(countries));
|
|
||||||
|
|
||||||
params.put("MapsSelected", QJsonDocument(json).toJson(QJsonDocument::Compact).toStdString());
|
|
||||||
|
|
||||||
if (!states.isEmpty() || !countries.isEmpty()) {
|
|
||||||
downloadOfflineMapsButton->setVisible(true);
|
|
||||||
update();
|
|
||||||
} else {
|
|
||||||
params.remove("MapsSelected");
|
|
||||||
}
|
|
||||||
}).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectMaps::SelectMaps(QWidget *parent) : QWidget(parent) {
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
||||||
|
|
||||||
QHBoxLayout *buttonsLayout = new QHBoxLayout();
|
|
||||||
buttonsLayout->setContentsMargins(20, 40, 20, 0);
|
|
||||||
|
|
||||||
backButton = new QPushButton(tr("Back"), this);
|
|
||||||
statesButton = new QPushButton(tr("States"), this);
|
|
||||||
countriesButton = new QPushButton(tr("Countries"), this);
|
|
||||||
|
|
||||||
backButton->setFixedSize(400, 100);
|
|
||||||
statesButton->setFixedSize(400, 100);
|
|
||||||
countriesButton->setFixedSize(400, 100);
|
|
||||||
|
|
||||||
buttonsLayout->addWidget(backButton);
|
|
||||||
buttonsLayout->addStretch();
|
|
||||||
buttonsLayout->addWidget(statesButton);
|
|
||||||
buttonsLayout->addStretch();
|
|
||||||
buttonsLayout->addWidget(countriesButton);
|
|
||||||
mainLayout->addLayout(buttonsLayout);
|
|
||||||
|
|
||||||
mainLayout->addWidget(horizontalLine());
|
|
||||||
mainLayout->setSpacing(20);
|
|
||||||
|
|
||||||
mapsLayout = new QStackedLayout();
|
|
||||||
mapsLayout->setMargin(40);
|
|
||||||
mapsLayout->setSpacing(20);
|
|
||||||
mainLayout->addLayout(mapsLayout);
|
|
||||||
|
|
||||||
QObject::connect(backButton, &QPushButton::clicked, this, [this]() { emit backPress(), emit setMaps(); });
|
|
||||||
|
|
||||||
FrogPilotListWidget *statesList = new FrogPilotListWidget();
|
|
||||||
|
|
||||||
LabelControl *northeastLabel = new LabelControl(tr("United States - Northeast"), "");
|
|
||||||
statesList->addItem(northeastLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *northeastControl = new ButtonSelectionControl("", tr(""), tr(""), northeastMap, false);
|
|
||||||
statesList->addItem(northeastControl);
|
|
||||||
|
|
||||||
LabelControl *midwestLabel = new LabelControl(tr("United States - Midwest"), "");
|
|
||||||
statesList->addItem(midwestLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *midwestControl = new ButtonSelectionControl("", tr(""), tr(""), midwestMap, false);
|
|
||||||
statesList->addItem(midwestControl);
|
|
||||||
|
|
||||||
LabelControl *southLabel = new LabelControl(tr("United States - South"), "");
|
|
||||||
statesList->addItem(southLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *southControl = new ButtonSelectionControl("", tr(""), tr(""), southMap, false);
|
|
||||||
statesList->addItem(southControl);
|
|
||||||
|
|
||||||
LabelControl *westLabel = new LabelControl(tr("United States - West"), "");
|
|
||||||
statesList->addItem(westLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *westControl = new ButtonSelectionControl("", tr(""), tr(""), westMap, false);
|
|
||||||
statesList->addItem(westControl);
|
|
||||||
|
|
||||||
LabelControl *territoriesLabel = new LabelControl(tr("United States - Territories"), "");
|
|
||||||
statesList->addItem(territoriesLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *territoriesControl = new ButtonSelectionControl("", tr(""), tr(""), territoriesMap, false);
|
|
||||||
statesList->addItem(territoriesControl);
|
|
||||||
|
|
||||||
statesScrollView = new ScrollView(statesList);
|
|
||||||
mapsLayout->addWidget(statesScrollView);
|
|
||||||
|
|
||||||
QObject::connect(statesButton, &QPushButton::clicked, this, [this]() {
|
|
||||||
mapsLayout->setCurrentWidget(statesScrollView);
|
|
||||||
statesButton->setStyleSheet(activeButtonStyle);
|
|
||||||
countriesButton->setStyleSheet(normalButtonStyle);
|
|
||||||
});
|
|
||||||
|
|
||||||
FrogPilotListWidget *countriesList = new FrogPilotListWidget();
|
|
||||||
|
|
||||||
LabelControl *africaLabel = new LabelControl(tr("Africa"), "");
|
|
||||||
countriesList->addItem(africaLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *africaControl = new ButtonSelectionControl("", tr(""), tr(""), africaMap, true);
|
|
||||||
countriesList->addItem(africaControl);
|
|
||||||
|
|
||||||
LabelControl *antarcticaLabel = new LabelControl(tr("Antarctica"), "");
|
|
||||||
countriesList->addItem(antarcticaLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *antarcticaControl = new ButtonSelectionControl("", tr(""), tr(""), antarcticaMap, true);
|
|
||||||
countriesList->addItem(antarcticaControl);
|
|
||||||
|
|
||||||
LabelControl *asiaLabel = new LabelControl(tr("Asia"), "");
|
|
||||||
countriesList->addItem(asiaLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *asiaControl = new ButtonSelectionControl("", tr(""), tr(""), asiaMap, true);
|
|
||||||
countriesList->addItem(asiaControl);
|
|
||||||
|
|
||||||
LabelControl *europeLabel = new LabelControl(tr("Europe"), "");
|
|
||||||
countriesList->addItem(europeLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *europeControl = new ButtonSelectionControl("", tr(""), tr(""), europeMap, true);
|
|
||||||
countriesList->addItem(europeControl);
|
|
||||||
|
|
||||||
LabelControl *northAmericaLabel = new LabelControl(tr("North America"), "");
|
|
||||||
countriesList->addItem(northAmericaLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *northAmericaControl = new ButtonSelectionControl("", tr(""), tr(""), northAmericaMap, true);
|
|
||||||
countriesList->addItem(northAmericaControl);
|
|
||||||
|
|
||||||
LabelControl *oceaniaLabel = new LabelControl(tr("Oceania"), "");
|
|
||||||
countriesList->addItem(oceaniaLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *oceaniaControl = new ButtonSelectionControl("", tr(""), tr(""), oceaniaMap, true);
|
|
||||||
countriesList->addItem(oceaniaControl);
|
|
||||||
|
|
||||||
LabelControl *southAmericaLabel = new LabelControl(tr("South America"), "");
|
|
||||||
countriesList->addItem(southAmericaLabel);
|
|
||||||
|
|
||||||
ButtonSelectionControl *southAmericaControl = new ButtonSelectionControl("", tr(""), tr(""), southAmericaMap, true);
|
|
||||||
countriesList->addItem(southAmericaControl);
|
|
||||||
|
|
||||||
countriesScrollView = new ScrollView(countriesList);
|
|
||||||
mapsLayout->addWidget(countriesScrollView);
|
|
||||||
|
|
||||||
QObject::connect(countriesButton, &QPushButton::clicked, this, [this]() {
|
|
||||||
mapsLayout->setCurrentWidget(countriesScrollView);
|
|
||||||
statesButton->setStyleSheet(normalButtonStyle);
|
|
||||||
countriesButton->setStyleSheet(activeButtonStyle);
|
|
||||||
});
|
|
||||||
|
|
||||||
mapsLayout->setCurrentWidget(statesScrollView);
|
|
||||||
statesButton->setStyleSheet(activeButtonStyle);
|
|
||||||
|
|
||||||
setStyleSheet(R"(
|
|
||||||
QPushButton {
|
|
||||||
font-size: 50px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 15px;
|
|
||||||
border-width: 0;
|
|
||||||
border-radius: 30px;
|
|
||||||
color: #dddddd;
|
|
||||||
background-color: #393939;
|
|
||||||
}
|
|
||||||
QPushButton:pressed {
|
|
||||||
background-color: #4a4a4a;
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
QString SelectMaps::activeButtonStyle = R"(
|
|
||||||
font-size: 50px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 15px;
|
|
||||||
border-width: 0;
|
|
||||||
border-radius: 30px;
|
|
||||||
color: #dddddd;
|
|
||||||
background-color: #33Ab4C;
|
|
||||||
)";
|
|
||||||
|
|
||||||
QString SelectMaps::normalButtonStyle = R"(
|
|
||||||
font-size: 50px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 15px;
|
|
||||||
border-width: 0;
|
|
||||||
border-radius: 30px;
|
|
||||||
color: #dddddd;
|
|
||||||
background-color: #393939;
|
|
||||||
)";
|
|
||||||
|
|
||||||
QFrame *SelectMaps::horizontalLine(QWidget *parent) const {
|
|
||||||
QFrame *line = new QFrame(parent);
|
|
||||||
|
|
||||||
line->setFrameShape(QFrame::StyledPanel);
|
|
||||||
line->setStyleSheet(R"(
|
|
||||||
border-width: 2px;
|
|
||||||
border-bottom-style: solid;
|
|
||||||
border-color: gray;
|
|
||||||
)");
|
|
||||||
line->setFixedHeight(2);
|
|
||||||
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectMaps::hideEvent(QHideEvent *event) {
|
|
||||||
QWidget::hideEvent(event);
|
|
||||||
emit setMaps();
|
|
||||||
}
|
|
||||||
|
|
||||||
Primeless::Primeless(QWidget *parent) : QWidget(parent) {
|
|
||||||
QStackedLayout *primelessLayout = new QStackedLayout(this);
|
|
||||||
|
|
||||||
QWidget *mainWidget = new QWidget();
|
|
||||||
mainLayout = new QVBoxLayout(mainWidget);
|
|
||||||
mainLayout->setMargin(40);
|
|
||||||
|
|
||||||
backButton = new QPushButton(tr("Back"), this);
|
|
||||||
backButton->setObjectName("backButton");
|
|
||||||
backButton->setFixedSize(400, 100);
|
|
||||||
QObject::connect(backButton, &QPushButton::clicked, this, [this]() { emit backPress(); });
|
|
||||||
mainLayout->addWidget(backButton, 0, Qt::AlignLeft);
|
|
||||||
|
|
||||||
list = new FrogPilotListWidget(mainWidget);
|
|
||||||
|
|
||||||
wifi = new WifiManager(this);
|
|
||||||
ipLabel = new LabelControl(tr("Manage Your Settings At"), QString("%1:8082").arg(wifi->getIp4Address()));
|
|
||||||
list->addItem(ipLabel);
|
|
||||||
|
|
||||||
std::vector<QString> searchOptions{tr("MapBox"), tr("Amap"), tr("Google")};
|
|
||||||
ButtonParamControl *searchInput = new ButtonParamControl("SearchInput", tr("Destination Search Provider"),
|
|
||||||
tr("Select a search provider for destination queries in Navigate on Openpilot. Options include MapBox (recommended), Amap, and Google Maps."),
|
|
||||||
"", searchOptions);
|
|
||||||
list->addItem(searchInput);
|
|
||||||
|
|
||||||
createMapboxKeyControl(publicMapboxKeyControl, tr("Public Mapbox Key"), "MapboxPublicKey", "pk.");
|
|
||||||
createMapboxKeyControl(secretMapboxKeyControl, tr("Secret Mapbox Key"), "MapboxSecretKey", "sk.");
|
|
||||||
|
|
||||||
mapboxPublicKeySet = !params.get("MapboxPublicKey").empty();
|
|
||||||
mapboxSecretKeySet = !params.get("MapboxSecretKey").empty();
|
|
||||||
setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet;
|
|
||||||
|
|
||||||
QHBoxLayout *setupLayout = new QHBoxLayout();
|
|
||||||
setupLayout->setMargin(0);
|
|
||||||
|
|
||||||
imageLabel = new QLabel(this);
|
|
||||||
pixmap.load(currentStep);
|
|
||||||
imageLabel->setPixmap(pixmap.scaledToWidth(1500, Qt::SmoothTransformation));
|
|
||||||
setupLayout->addWidget(imageLabel, 0, Qt::AlignCenter);
|
|
||||||
imageLabel->hide();
|
|
||||||
|
|
||||||
ButtonControl *setupButton = new ButtonControl(tr("Setup Instructions"), tr("VIEW"), tr("View the instructions to set up MapBox for Primeless Navigation."), this);
|
|
||||||
QObject::connect(setupButton, &ButtonControl::clicked, this, [this]() {
|
|
||||||
updateStep();
|
|
||||||
backButton->hide();
|
|
||||||
list->setVisible(false);
|
|
||||||
imageLabel->show();
|
|
||||||
});
|
|
||||||
list->addItem(setupButton);
|
|
||||||
|
|
||||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &Primeless::updateState);
|
|
||||||
|
|
||||||
mainLayout->addLayout(setupLayout);
|
|
||||||
mainLayout->addWidget(new ScrollView(list, mainWidget));
|
|
||||||
mainWidget->setLayout(mainLayout);
|
|
||||||
primelessLayout->addWidget(mainWidget);
|
|
||||||
|
|
||||||
setLayout(primelessLayout);
|
|
||||||
|
|
||||||
setStyleSheet(R"(
|
|
||||||
QPushButton {
|
|
||||||
font-size: 50px;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 15px;
|
|
||||||
border-width: 0;
|
|
||||||
border-radius: 30px;
|
|
||||||
color: #dddddd;
|
|
||||||
background-color: #393939;
|
|
||||||
}
|
|
||||||
QPushButton:pressed {
|
|
||||||
background-color: #4a4a4a;
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Primeless::hideEvent(QHideEvent *event) {
|
|
||||||
QWidget::hideEvent(event);
|
|
||||||
backButton->show();
|
|
||||||
list->setVisible(true);
|
|
||||||
imageLabel->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Primeless::mousePressEvent(QMouseEvent *event) {
|
|
||||||
backButton->show();
|
|
||||||
list->setVisible(true);
|
|
||||||
imageLabel->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Primeless::updateState() {
|
|
||||||
if (!isVisible()) return;
|
|
||||||
|
|
||||||
QString ipAddress = wifi->getIp4Address();
|
|
||||||
ipLabel->setText(ipAddress.isEmpty() ? tr("Device Offline") : QString("%1:8082").arg(ipAddress));
|
|
||||||
|
|
||||||
mapboxPublicKeySet = !params.get("MapboxPublicKey").empty();
|
|
||||||
mapboxSecretKeySet = !params.get("MapboxSecretKey").empty();
|
|
||||||
setupCompleted = mapboxPublicKeySet && mapboxSecretKeySet && setupCompleted;
|
|
||||||
|
|
||||||
publicMapboxKeyControl->setText(mapboxPublicKeySet ? tr("REMOVE") : tr("ADD"));
|
|
||||||
secretMapboxKeyControl->setText(mapboxSecretKeySet ? tr("REMOVE") : tr("ADD"));
|
|
||||||
|
|
||||||
if (imageLabel->isVisible()) {
|
|
||||||
updateStep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Primeless::createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix) {
|
|
||||||
control = new ButtonControl(label, "", tr("Manage your %1."), this);
|
|
||||||
QObject::connect(control, &ButtonControl::clicked, this, [this, control, label, paramKey, prefix] {
|
|
||||||
if (control->text() == tr("ADD")) {
|
|
||||||
QString key = InputDialog::getText(tr("Enter your %1").arg(label), this);
|
|
||||||
if (!key.startsWith(prefix)) {
|
|
||||||
key = prefix + key;
|
|
||||||
}
|
|
||||||
if (key.length() >= 80) {
|
|
||||||
params.put(paramKey, key.toStdString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
params.remove(paramKey);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
list->addItem(control);
|
|
||||||
control->setText(params.get(paramKey).empty() ? tr("ADD") : tr("REMOVE"));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Primeless::updateStep() {
|
|
||||||
currentStep = setupCompleted ? "../frogpilot/navigation/navigation_training/setup_completed.png" :
|
|
||||||
(mapboxPublicKeySet && mapboxSecretKeySet) ? "../frogpilot/navigation/navigation_training/both_keys_set.png" :
|
|
||||||
mapboxPublicKeySet ? "../frogpilot/navigation/navigation_training/public_key_set.png" : "../frogpilot/navigation/navigation_training/no_keys_set.png";
|
|
||||||
|
|
||||||
pixmap.load(currentStep);
|
|
||||||
imageLabel->setPixmap(pixmap.scaledToWidth(1500, Qt::SmoothTransformation));
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "selfdrive/frogpilot/ui/qt/widgets/frogpilot_controls.h"
|
|
||||||
#include "selfdrive/ui/qt/network/wifi_manager.h"
|
|
||||||
#include "selfdrive/ui/qt/offroad/settings.h"
|
|
||||||
#include "selfdrive/ui/qt/widgets/scrollview.h"
|
|
||||||
#include "selfdrive/ui/ui.h"
|
|
||||||
|
|
||||||
class Primeless : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Primeless(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
void hideEvent(QHideEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void createMapboxKeyControl(ButtonControl *&control, const QString &label, const std::string ¶mKey, const QString &prefix);
|
|
||||||
void updateState();
|
|
||||||
void updateStep();
|
|
||||||
|
|
||||||
QVBoxLayout *mainLayout;
|
|
||||||
FrogPilotListWidget *list;
|
|
||||||
|
|
||||||
QPushButton *backButton;
|
|
||||||
QLabel *imageLabel;
|
|
||||||
|
|
||||||
ButtonControl *publicMapboxKeyControl;
|
|
||||||
ButtonControl *secretMapboxKeyControl;
|
|
||||||
LabelControl *ipLabel;
|
|
||||||
|
|
||||||
WifiManager *wifi;
|
|
||||||
|
|
||||||
bool mapboxPublicKeySet;
|
|
||||||
bool mapboxSecretKeySet;
|
|
||||||
bool setupCompleted;
|
|
||||||
QPixmap pixmap;
|
|
||||||
QString currentStep = "../assets/images/setup_completed.png";
|
|
||||||
|
|
||||||
Params params;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void backPress();
|
|
||||||
};
|
|
||||||
|
|
||||||
class SelectMaps : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SelectMaps(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
QFrame *horizontalLine(QWidget *parent = nullptr) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void hideEvent(QHideEvent *event);
|
|
||||||
|
|
||||||
ScrollView *countriesScrollView;
|
|
||||||
ScrollView *statesScrollView;
|
|
||||||
QStackedLayout *mapsLayout;
|
|
||||||
|
|
||||||
QPushButton *backButton;
|
|
||||||
QPushButton *statesButton;
|
|
||||||
QPushButton *countriesButton;
|
|
||||||
|
|
||||||
static QString activeButtonStyle;
|
|
||||||
static QString normalButtonStyle;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void backPress();
|
|
||||||
void setMaps();
|
|
||||||
};
|
|
||||||
|
|
||||||
class FrogPilotNavigationPanel : public QFrame {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FrogPilotNavigationPanel(QWidget *parent = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void cancelDownload(QWidget *parent);
|
|
||||||
void checkIfUpdateMissed();
|
|
||||||
void downloadMaps();
|
|
||||||
void downloadSchedule();
|
|
||||||
void hideEvent(QHideEvent *event);
|
|
||||||
void removeMaps(QWidget *parent);
|
|
||||||
void setMaps();
|
|
||||||
void updateDownloadedLabel();
|
|
||||||
void updateState();
|
|
||||||
void updateStatuses();
|
|
||||||
void updateVisibility(bool visibility);
|
|
||||||
|
|
||||||
QStackedLayout *mainLayout;
|
|
||||||
QWidget *navigationWidget;
|
|
||||||
|
|
||||||
ButtonControl *cancelDownloadButton;
|
|
||||||
ButtonControl *downloadOfflineMapsButton;
|
|
||||||
ButtonControl *redownloadOfflineMapsButton;
|
|
||||||
ButtonControl *removeOfflineMapsButton;
|
|
||||||
|
|
||||||
LabelControl *lastMapsDownload;
|
|
||||||
LabelControl *offlineMapsSize;
|
|
||||||
LabelControl *offlineMapsStatus;
|
|
||||||
LabelControl *offlineMapsETA;
|
|
||||||
LabelControl *offlineMapsElapsed;
|
|
||||||
|
|
||||||
bool downloadActive;
|
|
||||||
bool previousDownloadActive;
|
|
||||||
bool scheduleCompleted;
|
|
||||||
bool schedulePending;
|
|
||||||
int schedule;
|
|
||||||
QString elapsedTime;
|
|
||||||
QString offlineFolderPath = "/data/media/0/osm/offline";
|
|
||||||
std::string osmDownloadProgress;
|
|
||||||
std::string previousOSMDownloadProgress;
|
|
||||||
|
|
||||||
Params params;
|
|
||||||
Params paramsMemory{"/dev/shm/params"};
|
|
||||||
UIScene &scene;
|
|
||||||
};
|
|
||||||
@@ -21,19 +21,14 @@ qt_env['CXXFLAGS'] += ["-Wno-deprecated-declarations"]
|
|||||||
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs)
|
qt_util = qt_env.Library("qt_util", ["#selfdrive/ui/qt/api.cc", "#selfdrive/ui/qt/util.cc"], LIBS=base_libs)
|
||||||
widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/wifi.cc",
|
widgets_src = ["ui.cc", "qt/widgets/input.cc", "qt/widgets/drive_stats.cc", "qt/widgets/wifi.cc",
|
||||||
"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/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/network/networking.cc", "qt/network/wifi_manager.cc",
|
"qt/request_repeater.cc", "qt/qt_window.cc", "qt/network/networking.cc", "qt/network/wifi_manager.cc",
|
||||||
"../frogpilot/ui/qt/widgets/frogpilot_controls.cc", "../frogpilot/navigation/ui/navigation_settings.cc",
|
"../frogpilot/ui/qt/widgets/frogpilot_controls.cc",
|
||||||
"../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc",
|
"../frogpilot/ui/qt/offroad/control_settings.cc", "../frogpilot/ui/qt/offroad/vehicle_settings.cc",
|
||||||
"../frogpilot/ui/qt/offroad/visual_settings.cc"]
|
"../frogpilot/ui/qt/offroad/visual_settings.cc"]
|
||||||
|
|
||||||
qt_env['CPPDEFINES'] = []
|
qt_env['CPPDEFINES'] = []
|
||||||
if maps:
|
|
||||||
base_libs += ['QMapLibre']
|
|
||||||
widgets_src += ["qt/maps/map_helpers.cc", "qt/maps/map_settings.cc", "qt/maps/map.cc", "qt/maps/map_panel.cc",
|
|
||||||
"qt/maps/map_eta.cc", "qt/maps/map_instructions.cc"]
|
|
||||||
qt_env['CPPDEFINES'] += ["ENABLE_MAPS"]
|
|
||||||
|
|
||||||
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
|
widgets = qt_env.Library("qt_widgets", widgets_src, LIBS=base_libs)
|
||||||
Export('widgets')
|
Export('widgets')
|
||||||
|
|||||||
@@ -8,15 +8,10 @@
|
|||||||
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
|
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
|
||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
#include "selfdrive/ui/qt/widgets/drive_stats.h"
|
#include "selfdrive/ui/qt/widgets/drive_stats.h"
|
||||||
#include "selfdrive/ui/qt/widgets/prime.h"
|
|
||||||
#include "system/hardware/hw.h"
|
#include "system/hardware/hw.h"
|
||||||
|
|
||||||
#include <QWebEngineView>
|
#include <QWebEngineView>
|
||||||
|
|
||||||
#ifdef ENABLE_MAPS
|
|
||||||
#include "selfdrive/ui/qt/maps/map_settings.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// HomeWindow: the container for the offroad and onroad UIs
|
// HomeWindow: the container for the offroad and onroad UIs
|
||||||
|
|
||||||
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
||||||
@@ -40,7 +35,6 @@ HomeWindow::HomeWindow(QWidget* parent) : QWidget(parent) {
|
|||||||
slayout->addWidget(home);
|
slayout->addWidget(home);
|
||||||
|
|
||||||
onroad = new OnroadWindow(this);
|
onroad = new OnroadWindow(this);
|
||||||
QObject::connect(onroad, &OnroadWindow::mapPanelRequested, this, [=] { sidebar->hide(); });
|
|
||||||
slayout->addWidget(onroad);
|
slayout->addWidget(onroad);
|
||||||
|
|
||||||
// CLEARPILOT
|
// CLEARPILOT
|
||||||
@@ -71,10 +65,6 @@ void HomeWindow::showSidebar(bool show) {
|
|||||||
sidebar->setVisible(show);
|
sidebar->setVisible(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HomeWindow::showMapPanel(bool show) {
|
|
||||||
onroad->showMapPanel(show);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HomeWindow::updateState(const UIState &s) {
|
void HomeWindow::updateState(const UIState &s) {
|
||||||
// const SubMaster &sm = *(s.sm);
|
// const SubMaster &sm = *(s.sm);
|
||||||
if (s.scene.started) {
|
if (s.scene.started) {
|
||||||
@@ -89,7 +79,6 @@ void HomeWindow::offroadTransition(bool offroad) {
|
|||||||
} else {
|
} else {
|
||||||
sidebar->setVisible(false);
|
sidebar->setVisible(false);
|
||||||
slayout->setCurrentWidget(onroad);
|
slayout->setCurrentWidget(onroad);
|
||||||
uiState()->scene.map_open = onroad->isMapVisible();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ public slots:
|
|||||||
void showDriverView(bool show, bool started=false);
|
void showDriverView(bool show, bool started=false);
|
||||||
void showOnroad();
|
void showOnroad();
|
||||||
void showSidebar(bool show);
|
void showSidebar(bool show);
|
||||||
void showMapPanel(bool show);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void mousePressEvent(QMouseEvent* e) override;
|
void mousePressEvent(QMouseEvent* e) override;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
|
#include "selfdrive/ui/qt/offroad/experimental_mode.h"
|
||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
#include "selfdrive/ui/qt/widgets/drive_stats.h"
|
#include "selfdrive/ui/qt/widgets/drive_stats.h"
|
||||||
#include "selfdrive/ui/qt/widgets/prime.h"
|
|
||||||
#include "system/hardware/hw.h"
|
#include "system/hardware/hw.h"
|
||||||
|
|
||||||
#ifdef ENABLE_MAPS
|
#ifdef ENABLE_MAPS
|
||||||
|
|||||||
@@ -1,469 +0,0 @@
|
|||||||
#include "selfdrive/ui/qt/maps/map.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <eigen3/Eigen/Dense>
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/maps/map_helpers.h"
|
|
||||||
#include "selfdrive/ui/qt/util.h"
|
|
||||||
#include "selfdrive/ui/ui.h"
|
|
||||||
|
|
||||||
|
|
||||||
const int INTERACTION_TIMEOUT = 100;
|
|
||||||
|
|
||||||
const float MAX_ZOOM = 17;
|
|
||||||
const float MIN_ZOOM = 14;
|
|
||||||
const float MAX_PITCH = 50;
|
|
||||||
const float MIN_PITCH = 0;
|
|
||||||
const float MAP_SCALE = 2;
|
|
||||||
|
|
||||||
MapWindow::MapWindow(const QMapLibre::Settings &settings) : m_settings(settings), velocity_filter(0, 10, 0.05, false) {
|
|
||||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &MapWindow::updateState);
|
|
||||||
|
|
||||||
map_overlay = new QWidget (this);
|
|
||||||
map_overlay->setAttribute(Qt::WA_TranslucentBackground, true);
|
|
||||||
QVBoxLayout *overlay_layout = new QVBoxLayout(map_overlay);
|
|
||||||
overlay_layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
// Instructions
|
|
||||||
map_instructions = new MapInstructions(this);
|
|
||||||
map_instructions->setVisible(false);
|
|
||||||
|
|
||||||
map_eta = new MapETA(this);
|
|
||||||
map_eta->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
||||||
map_eta->setFixedHeight(120);
|
|
||||||
|
|
||||||
error = new QLabel(this);
|
|
||||||
error->setStyleSheet(R"(color:white;padding:50px 11px;font-size: 90px; background-color:rgba(0, 0, 0, 150);)");
|
|
||||||
error->setAlignment(Qt::AlignCenter);
|
|
||||||
|
|
||||||
overlay_layout->addWidget(error);
|
|
||||||
overlay_layout->addWidget(map_instructions);
|
|
||||||
overlay_layout->addStretch(1);
|
|
||||||
overlay_layout->addWidget(map_eta);
|
|
||||||
|
|
||||||
last_position = coordinate_from_param("LastGPSPosition");
|
|
||||||
grabGesture(Qt::GestureType::PinchGesture);
|
|
||||||
qDebug() << "MapWindow initialized";
|
|
||||||
}
|
|
||||||
|
|
||||||
MapWindow::~MapWindow() {
|
|
||||||
makeCurrent();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::initLayers() {
|
|
||||||
// This doesn't work from initializeGL
|
|
||||||
if (!m_map->layerExists("modelPathLayer")) {
|
|
||||||
qDebug() << "Initializing modelPathLayer";
|
|
||||||
QVariantMap modelPath;
|
|
||||||
//modelPath["id"] = "modelPathLayer";
|
|
||||||
modelPath["type"] = "line";
|
|
||||||
modelPath["source"] = "modelPathSource";
|
|
||||||
m_map->addLayer("modelPathLayer", modelPath);
|
|
||||||
m_map->setPaintProperty("modelPathLayer", "line-color", QColor("red"));
|
|
||||||
m_map->setPaintProperty("modelPathLayer", "line-width", 5.0);
|
|
||||||
m_map->setLayoutProperty("modelPathLayer", "line-cap", "round");
|
|
||||||
}
|
|
||||||
if (!m_map->layerExists("navLayer")) {
|
|
||||||
qDebug() << "Initializing navLayer";
|
|
||||||
QVariantMap nav;
|
|
||||||
nav["type"] = "line";
|
|
||||||
nav["source"] = "navSource";
|
|
||||||
m_map->addLayer("navLayer", nav, "road-intersection");
|
|
||||||
|
|
||||||
QVariantMap transition;
|
|
||||||
transition["duration"] = 400; // ms
|
|
||||||
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(uiState()->scene.navigate_on_openpilot));
|
|
||||||
m_map->setPaintProperty("navLayer", "line-color-transition", transition);
|
|
||||||
m_map->setPaintProperty("navLayer", "line-width", 7.5);
|
|
||||||
m_map->setLayoutProperty("navLayer", "line-cap", "round");
|
|
||||||
}
|
|
||||||
if (!m_map->layerExists("pinLayer")) {
|
|
||||||
qDebug() << "Initializing pinLayer";
|
|
||||||
m_map->addImage("default_marker", QImage("../assets/navigation/default_marker.svg"));
|
|
||||||
QVariantMap pin;
|
|
||||||
pin["type"] = "symbol";
|
|
||||||
pin["source"] = "pinSource";
|
|
||||||
m_map->addLayer("pinLayer", pin);
|
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-pitch-alignment", "viewport");
|
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-image", "default_marker");
|
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-ignore-placement", true);
|
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-allow-overlap", true);
|
|
||||||
m_map->setLayoutProperty("pinLayer", "symbol-sort-key", 0);
|
|
||||||
m_map->setLayoutProperty("pinLayer", "icon-anchor", "bottom");
|
|
||||||
}
|
|
||||||
if (!m_map->layerExists("carPosLayer")) {
|
|
||||||
qDebug() << "Initializing carPosLayer";
|
|
||||||
m_map->addImage("label-arrow", QImage("../assets/images/triangle.svg"));
|
|
||||||
|
|
||||||
QVariantMap carPos;
|
|
||||||
carPos["type"] = "symbol";
|
|
||||||
carPos["source"] = "carPosSource";
|
|
||||||
m_map->addLayer("carPosLayer", carPos);
|
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-pitch-alignment", "map");
|
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-image", "label-arrow");
|
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-size", 0.5);
|
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-ignore-placement", true);
|
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-allow-overlap", true);
|
|
||||||
// TODO: remove, symbol-sort-key does not seem to matter outside of each layer
|
|
||||||
m_map->setLayoutProperty("carPosLayer", "symbol-sort-key", 0);
|
|
||||||
}
|
|
||||||
if (!m_map->layerExists("buildingsLayer")) {
|
|
||||||
qDebug() << "Initializing buildingsLayer";
|
|
||||||
QVariantMap buildings;
|
|
||||||
buildings["id"] = "buildingsLayer";
|
|
||||||
buildings["source"] = "composite";
|
|
||||||
buildings["source-layer"] = "building";
|
|
||||||
buildings["type"] = "fill-extrusion";
|
|
||||||
buildings["minzoom"] = 15;
|
|
||||||
m_map->addLayer("buildingsLayer", buildings);
|
|
||||||
m_map->setFilter("buildingsLayer", QVariantList({"==", "extrude", "true"}));
|
|
||||||
|
|
||||||
QVariantList fillExtrusionHight = {
|
|
||||||
"interpolate",
|
|
||||||
QVariantList{"linear"},
|
|
||||||
QVariantList{"zoom"},
|
|
||||||
15, 0,
|
|
||||||
15.05, QVariantList{"get", "height"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QVariantList fillExtrusionBase = {
|
|
||||||
"interpolate",
|
|
||||||
QVariantList{"linear"},
|
|
||||||
QVariantList{"zoom"},
|
|
||||||
15, 0,
|
|
||||||
15.05, QVariantList{"get", "min_height"}
|
|
||||||
};
|
|
||||||
|
|
||||||
QVariantList fillExtrusionOpacity = {
|
|
||||||
"interpolate",
|
|
||||||
QVariantList{"linear"},
|
|
||||||
QVariantList{"zoom"},
|
|
||||||
15, 0,
|
|
||||||
15.5, .6,
|
|
||||||
17, .6,
|
|
||||||
20, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-color", QColor("grey"));
|
|
||||||
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-opacity", fillExtrusionOpacity);
|
|
||||||
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-height", fillExtrusionHight);
|
|
||||||
m_map->setPaintProperty("buildingsLayer", "fill-extrusion-base", fillExtrusionBase);
|
|
||||||
m_map->setLayoutProperty("buildingsLayer", "visibility", "visible");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::updateState(const UIState &s) {
|
|
||||||
if (!uiState()->scene.started) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const SubMaster &sm = *(s.sm);
|
|
||||||
update();
|
|
||||||
|
|
||||||
if (sm.updated("modelV2")) {
|
|
||||||
// set path color on change, and show map on rising edge of navigate on openpilot
|
|
||||||
bool nav_enabled = sm["modelV2"].getModelV2().getNavEnabled() &&
|
|
||||||
(sm["controlsState"].getControlsState().getEnabled() || sm["frogpilotCarControl"].getFrogpilotCarControl().getAlwaysOnLateral());
|
|
||||||
if (nav_enabled != uiState()->scene.navigate_on_openpilot) {
|
|
||||||
if (loaded_once) {
|
|
||||||
m_map->setPaintProperty("navLayer", "line-color", getNavPathColor(nav_enabled));
|
|
||||||
}
|
|
||||||
if (nav_enabled) {
|
|
||||||
emit requestVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uiState()->scene.navigate_on_openpilot = nav_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sm.updated("liveLocationKalman")) {
|
|
||||||
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
|
|
||||||
auto locationd_pos = locationd_location.getPositionGeodetic();
|
|
||||||
auto locationd_orientation = locationd_location.getCalibratedOrientationNED();
|
|
||||||
auto locationd_velocity = locationd_location.getVelocityCalibrated();
|
|
||||||
|
|
||||||
// Check std norm
|
|
||||||
auto pos_ecef_std = locationd_location.getPositionECEF().getStd();
|
|
||||||
bool pos_accurate_enough = sqrt(pow(pos_ecef_std[0], 2) + pow(pos_ecef_std[1], 2) + pow(pos_ecef_std[2], 2)) < 100;
|
|
||||||
|
|
||||||
locationd_valid = (locationd_pos.getValid() && locationd_orientation.getValid() && locationd_velocity.getValid() && pos_accurate_enough);
|
|
||||||
|
|
||||||
if (locationd_valid) {
|
|
||||||
last_position = QMapLibre::Coordinate(locationd_pos.getValue()[0], locationd_pos.getValue()[1]);
|
|
||||||
last_bearing = RAD2DEG(locationd_orientation.getValue()[2]);
|
|
||||||
velocity_filter.update(std::max(10.0, locationd_velocity.getValue()[0]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Credit to jakethesnake420
|
|
||||||
if (loaded_once && (sm.rcv_frame("uiPlan") != model_rcv_frame)) {
|
|
||||||
auto locationd_location = sm["liveLocationKalman"].getLiveLocationKalman();
|
|
||||||
auto model_path = model_to_collection(locationd_location.getCalibratedOrientationECEF(), locationd_location.getPositionECEF(), sm["uiPlan"].getUiPlan().getPosition());
|
|
||||||
QMapLibre::Feature model_path_feature(QMapLibre::Feature::LineStringType, model_path, {}, {});
|
|
||||||
QVariantMap modelV2Path;
|
|
||||||
modelV2Path["type"] = "geojson";
|
|
||||||
modelV2Path["data"] = QVariant::fromValue<QMapLibre::Feature>(model_path_feature);
|
|
||||||
m_map->updateSource("modelPathSource", modelV2Path);
|
|
||||||
model_rcv_frame = sm.rcv_frame("uiPlan");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sm.updated("navRoute") && sm["navRoute"].getNavRoute().getCoordinates().size()) {
|
|
||||||
auto nav_dest = coordinate_from_param("NavDestination");
|
|
||||||
bool allow_open = std::exchange(last_valid_nav_dest, nav_dest) != nav_dest &&
|
|
||||||
nav_dest && !isVisible();
|
|
||||||
qWarning() << "Got new navRoute from navd. Opening map:" << allow_open;
|
|
||||||
|
|
||||||
// Show map on destination set/change
|
|
||||||
if (allow_open) {
|
|
||||||
emit requestSettings(false);
|
|
||||||
emit requestVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
loaded_once = loaded_once || (m_map && m_map->isFullyLoaded());
|
|
||||||
if (!loaded_once) {
|
|
||||||
setError(tr("Map Loading"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
initLayers();
|
|
||||||
|
|
||||||
if (!locationd_valid) {
|
|
||||||
setError(tr("Waiting for GPS"));
|
|
||||||
} else if (routing_problem) {
|
|
||||||
setError(tr("Waiting for route"));
|
|
||||||
} else {
|
|
||||||
setError("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (locationd_valid) {
|
|
||||||
// Update current location marker
|
|
||||||
auto point = coordinate_to_collection(*last_position);
|
|
||||||
QMapLibre::Feature feature1(QMapLibre::Feature::PointType, point, {}, {});
|
|
||||||
QVariantMap carPosSource;
|
|
||||||
carPosSource["type"] = "geojson";
|
|
||||||
carPosSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature1);
|
|
||||||
m_map->updateSource("carPosSource", carPosSource);
|
|
||||||
|
|
||||||
// Map bearing isn't updated when interacting, keep location marker up to date
|
|
||||||
if (last_bearing) {
|
|
||||||
m_map->setLayoutProperty("carPosLayer", "icon-rotate", *last_bearing - m_map->bearing());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (interaction_counter == 0) {
|
|
||||||
if (last_position) m_map->setCoordinate(*last_position);
|
|
||||||
if (last_bearing) m_map->setBearing(*last_bearing);
|
|
||||||
m_map->setZoom(util::map_val<float>(velocity_filter.x(), 0, 30, MAX_ZOOM, MIN_ZOOM));
|
|
||||||
} else {
|
|
||||||
interaction_counter--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sm.updated("navInstruction")) {
|
|
||||||
// an invalid navInstruction packet with a nav destination is only possible if:
|
|
||||||
// - API exception/no internet
|
|
||||||
// - route response is empty
|
|
||||||
// - any time navd is waiting for recompute_countdown
|
|
||||||
routing_problem = !sm.valid("navInstruction") && coordinate_from_param("NavDestination").has_value();
|
|
||||||
|
|
||||||
if (sm.valid("navInstruction")) {
|
|
||||||
auto i = sm["navInstruction"].getNavInstruction();
|
|
||||||
map_eta->updateETA(i.getTimeRemaining(), i.getTimeRemainingTypical(), i.getDistanceRemaining());
|
|
||||||
|
|
||||||
if (locationd_valid) {
|
|
||||||
m_map->setPitch(MAX_PITCH); // TODO: smooth pitching based on maneuver distance
|
|
||||||
map_instructions->updateInstructions(i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clearRoute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sm.rcv_frame("navRoute") != route_rcv_frame) {
|
|
||||||
qWarning() << "Updating navLayer with new route";
|
|
||||||
auto route = sm["navRoute"].getNavRoute();
|
|
||||||
auto route_points = capnp_coordinate_list_to_collection(route.getCoordinates());
|
|
||||||
QMapLibre::Feature feature(QMapLibre::Feature::LineStringType, route_points, {}, {});
|
|
||||||
QVariantMap navSource;
|
|
||||||
navSource["type"] = "geojson";
|
|
||||||
navSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
|
|
||||||
m_map->updateSource("navSource", navSource);
|
|
||||||
m_map->setLayoutProperty("navLayer", "visibility", "visible");
|
|
||||||
|
|
||||||
route_rcv_frame = sm.rcv_frame("navRoute");
|
|
||||||
updateDestinationMarker();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map Styling - Credit goes to OPKR!
|
|
||||||
int map_style = uiState()->scene.map_style;
|
|
||||||
|
|
||||||
if (map_style != previous_map_style) {
|
|
||||||
std::unordered_map<int, std::string> styleUrls = {
|
|
||||||
{0, "mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj"}, // Stock openpilot
|
|
||||||
{1, "mapbox://styles/mapbox/streets-v11"}, // Mapbox Streets
|
|
||||||
{2, "mapbox://styles/mapbox/outdoors-v11"}, // Mapbox Outdoors
|
|
||||||
{3, "mapbox://styles/mapbox/light-v10"}, // Mapbox Light
|
|
||||||
{4, "mapbox://styles/mapbox/dark-v10"}, // Mapbox Dark
|
|
||||||
{5, "mapbox://styles/mapbox/satellite-v9"}, // Mapbox Satellite
|
|
||||||
{6, "mapbox://styles/mapbox/satellite-streets-v11"}, // Mapbox Satellite Streets
|
|
||||||
{7, "mapbox://styles/mapbox/navigation-day-v1"}, // Mapbox Navigation Day
|
|
||||||
{8, "mapbox://styles/mapbox/navigation-night-v1"}, // Mapbox Navigation Night
|
|
||||||
{9, "mapbox://styles/mapbox/traffic-night-v2"}, // Mapbox Traffic Night
|
|
||||||
{10, "mapbox://styles/mike854/clt0hm8mw01ok01p4blkr27jp"}, // mike854's (Satellite hybrid)
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<int, std::string>::iterator it = styleUrls.find(map_style);
|
|
||||||
m_map->setStyleUrl(QString::fromStdString(it->second));
|
|
||||||
}
|
|
||||||
|
|
||||||
previous_map_style = map_style;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::setError(const QString &err_str) {
|
|
||||||
if (err_str != error->text()) {
|
|
||||||
error->setText(err_str);
|
|
||||||
error->setVisible(!err_str.isEmpty());
|
|
||||||
if (!err_str.isEmpty()) map_instructions->setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::resizeGL(int w, int h) {
|
|
||||||
m_map->resize(size() / MAP_SCALE);
|
|
||||||
map_overlay->setFixedSize(width(), height());
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::initializeGL() {
|
|
||||||
m_map.reset(new QMapLibre::Map(this, m_settings, size(), 1));
|
|
||||||
|
|
||||||
if (last_position) {
|
|
||||||
m_map->setCoordinateZoom(*last_position, MAX_ZOOM);
|
|
||||||
} else {
|
|
||||||
m_map->setCoordinateZoom(QMapLibre::Coordinate(64.31990695292795, -149.79038934046247), MIN_ZOOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_map->setMargins({0, 350, 0, 50});
|
|
||||||
m_map->setPitch(MIN_PITCH);
|
|
||||||
m_map->setStyleUrl("mapbox://styles/commaai/clkqztk0f00ou01qyhsa5bzpj");
|
|
||||||
|
|
||||||
QObject::connect(m_map.data(), &QMapLibre::Map::mapChanged, [=](QMapLibre::Map::MapChange change) {
|
|
||||||
// set global animation duration to 0 ms so visibility changes are instant
|
|
||||||
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingStyle) {
|
|
||||||
m_map->setTransitionOptions(0, 0);
|
|
||||||
}
|
|
||||||
if (change == QMapLibre::Map::MapChange::MapChangeDidFinishLoadingMap) {
|
|
||||||
loaded_once = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::paintGL() {
|
|
||||||
if (!isVisible() || m_map.isNull()) return;
|
|
||||||
m_map->render();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::clearRoute() {
|
|
||||||
if (!m_map.isNull()) {
|
|
||||||
m_map->setLayoutProperty("navLayer", "visibility", "none");
|
|
||||||
m_map->setPitch(MIN_PITCH);
|
|
||||||
updateDestinationMarker();
|
|
||||||
}
|
|
||||||
|
|
||||||
map_instructions->setVisible(false);
|
|
||||||
map_eta->setVisible(false);
|
|
||||||
last_valid_nav_dest = std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::mousePressEvent(QMouseEvent *ev) {
|
|
||||||
m_lastPos = ev->localPos();
|
|
||||||
ev->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::mouseDoubleClickEvent(QMouseEvent *ev) {
|
|
||||||
if (last_position) m_map->setCoordinate(*last_position);
|
|
||||||
if (last_bearing) m_map->setBearing(*last_bearing);
|
|
||||||
m_map->setZoom(util::map_val<float>(velocity_filter.x(), 0, 30, MAX_ZOOM, MIN_ZOOM));
|
|
||||||
update();
|
|
||||||
|
|
||||||
interaction_counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::mouseMoveEvent(QMouseEvent *ev) {
|
|
||||||
QPointF delta = ev->localPos() - m_lastPos;
|
|
||||||
|
|
||||||
if (!delta.isNull()) {
|
|
||||||
interaction_counter = INTERACTION_TIMEOUT;
|
|
||||||
m_map->moveBy(delta / MAP_SCALE);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lastPos = ev->localPos();
|
|
||||||
ev->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::wheelEvent(QWheelEvent *ev) {
|
|
||||||
if (ev->orientation() == Qt::Horizontal) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float factor = ev->delta() / 1200.;
|
|
||||||
if (ev->delta() < 0) {
|
|
||||||
factor = factor > -1 ? factor : 1 / factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_map->scaleBy(1 + factor, ev->pos() / MAP_SCALE);
|
|
||||||
update();
|
|
||||||
|
|
||||||
interaction_counter = INTERACTION_TIMEOUT;
|
|
||||||
ev->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MapWindow::event(QEvent *event) {
|
|
||||||
if (event->type() == QEvent::Gesture) {
|
|
||||||
return gestureEvent(static_cast<QGestureEvent*>(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
return QWidget::event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MapWindow::gestureEvent(QGestureEvent *event) {
|
|
||||||
if (QGesture *pinch = event->gesture(Qt::PinchGesture)) {
|
|
||||||
pinchTriggered(static_cast<QPinchGesture *>(pinch));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::pinchTriggered(QPinchGesture *gesture) {
|
|
||||||
QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
|
|
||||||
if (changeFlags & QPinchGesture::ScaleFactorChanged) {
|
|
||||||
// TODO: figure out why gesture centerPoint doesn't work
|
|
||||||
m_map->scaleBy(gesture->scaleFactor(), {width() / 2.0 / MAP_SCALE, height() / 2.0 / MAP_SCALE});
|
|
||||||
update();
|
|
||||||
interaction_counter = INTERACTION_TIMEOUT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::offroadTransition(bool offroad) {
|
|
||||||
if (offroad) {
|
|
||||||
clearRoute();
|
|
||||||
uiState()->scene.navigate_on_openpilot = false;
|
|
||||||
routing_problem = false;
|
|
||||||
} else {
|
|
||||||
auto dest = coordinate_from_param("NavDestination");
|
|
||||||
emit requestVisible(dest.has_value());
|
|
||||||
}
|
|
||||||
last_bearing = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapWindow::updateDestinationMarker() {
|
|
||||||
auto nav_dest = coordinate_from_param("NavDestination");
|
|
||||||
if (nav_dest.has_value()) {
|
|
||||||
auto point = coordinate_to_collection(*nav_dest);
|
|
||||||
QMapLibre::Feature feature(QMapLibre::Feature::PointType, point, {}, {});
|
|
||||||
QVariantMap pinSource;
|
|
||||||
pinSource["type"] = "geojson";
|
|
||||||
pinSource["data"] = QVariant::fromValue<QMapLibre::Feature>(feature);
|
|
||||||
m_map->updateSource("pinSource", pinSource);
|
|
||||||
m_map->setPaintProperty("pinLayer", "visibility", "visible");
|
|
||||||
} else {
|
|
||||||
m_map->setPaintProperty("pinLayer", "visibility", "none");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
#include <QGeoCoordinate>
|
|
||||||
#include <QGestureEvent>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QMap>
|
|
||||||
#include <QMapLibre/Map>
|
|
||||||
#include <QMapLibre/Settings>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include <QOpenGLWidget>
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QScopedPointer>
|
|
||||||
#include <QString>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QWheelEvent>
|
|
||||||
|
|
||||||
#include "cereal/messaging/messaging.h"
|
|
||||||
#include "common/params.h"
|
|
||||||
#include "common/util.h"
|
|
||||||
#include "selfdrive/ui/ui.h"
|
|
||||||
#include "selfdrive/ui/qt/maps/map_eta.h"
|
|
||||||
#include "selfdrive/ui/qt/maps/map_instructions.h"
|
|
||||||
|
|
||||||
class MapWindow : public QOpenGLWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
MapWindow(const QMapLibre::Settings &);
|
|
||||||
~MapWindow();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initializeGL() final;
|
|
||||||
void paintGL() final;
|
|
||||||
void resizeGL(int w, int h) override;
|
|
||||||
|
|
||||||
QMapLibre::Settings m_settings;
|
|
||||||
QScopedPointer<QMapLibre::Map> m_map;
|
|
||||||
|
|
||||||
void initLayers();
|
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent *ev) final;
|
|
||||||
void mouseDoubleClickEvent(QMouseEvent *ev) final;
|
|
||||||
void mouseMoveEvent(QMouseEvent *ev) final;
|
|
||||||
void wheelEvent(QWheelEvent *ev) final;
|
|
||||||
bool event(QEvent *event) final;
|
|
||||||
bool gestureEvent(QGestureEvent *event);
|
|
||||||
void pinchTriggered(QPinchGesture *gesture);
|
|
||||||
void setError(const QString &err_str);
|
|
||||||
|
|
||||||
bool loaded_once = false;
|
|
||||||
|
|
||||||
// Panning
|
|
||||||
QPointF m_lastPos;
|
|
||||||
int interaction_counter = 0;
|
|
||||||
|
|
||||||
// Position
|
|
||||||
std::optional<QMapLibre::Coordinate> last_valid_nav_dest;
|
|
||||||
std::optional<QMapLibre::Coordinate> last_position;
|
|
||||||
std::optional<float> last_bearing;
|
|
||||||
FirstOrderFilter velocity_filter;
|
|
||||||
bool locationd_valid = false;
|
|
||||||
bool routing_problem = false;
|
|
||||||
|
|
||||||
QWidget *map_overlay;
|
|
||||||
QLabel *error;
|
|
||||||
MapInstructions* map_instructions;
|
|
||||||
MapETA* map_eta;
|
|
||||||
|
|
||||||
// Blue with normal nav, green when nav is input into the model
|
|
||||||
QColor getNavPathColor(bool nav_enabled) {
|
|
||||||
return nav_enabled ? QColor("#31ee73") : QColor("#31a1ee");
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearRoute();
|
|
||||||
void updateDestinationMarker();
|
|
||||||
uint64_t route_rcv_frame = 0;
|
|
||||||
|
|
||||||
// FrogPilot variables
|
|
||||||
Params params;
|
|
||||||
|
|
||||||
int previous_map_style;
|
|
||||||
|
|
||||||
uint64_t model_rcv_frame = 0;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void updateState(const UIState &s);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void offroadTransition(bool offroad);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void requestVisible(bool visible);
|
|
||||||
void requestSettings(bool settings);
|
|
||||||
};
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#include "selfdrive/ui/qt/maps/map_eta.h"
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QPainter>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/maps/map_helpers.h"
|
|
||||||
#include "selfdrive/ui/ui.h"
|
|
||||||
|
|
||||||
const float MANEUVER_TRANSITION_THRESHOLD = 10;
|
|
||||||
|
|
||||||
MapETA::MapETA(QWidget *parent) : QWidget(parent) {
|
|
||||||
setVisible(false);
|
|
||||||
setAttribute(Qt::WA_TranslucentBackground);
|
|
||||||
eta_doc.setUndoRedoEnabled(false);
|
|
||||||
eta_doc.setDefaultStyleSheet("body {font-family:Inter;font-size:70px;color:white;} b{font-weight:600;} td{padding:0 3px;}");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapETA::paintEvent(QPaintEvent *event) {
|
|
||||||
if (!eta_doc.isEmpty()) {
|
|
||||||
QPainter p(this);
|
|
||||||
p.setRenderHint(QPainter::Antialiasing);
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
p.setBrush(QColor(0, 0, 0, 255));
|
|
||||||
QSizeF txt_size = eta_doc.size();
|
|
||||||
p.drawRoundedRect((width() - txt_size.width()) / 2 - UI_BORDER_SIZE, 0, txt_size.width() + UI_BORDER_SIZE * 2, height() + 25, 25, 25);
|
|
||||||
p.translate((width() - txt_size.width()) / 2, (height() - txt_size.height()) / 2);
|
|
||||||
eta_doc.drawContents(&p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapETA::updateETA(float s, float s_typical, float d) {
|
|
||||||
// ETA
|
|
||||||
auto eta_t = QDateTime::currentDateTime().addSecs(s).time();
|
|
||||||
auto eta = format_24h ? std::pair{eta_t.toString("HH:mm"), tr("eta")}
|
|
||||||
: std::pair{eta_t.toString("h:mm a").split(' ')[0], eta_t.toString("a")};
|
|
||||||
|
|
||||||
// Remaining time
|
|
||||||
auto remaining = s < 3600 ? std::pair{QString::number(int(s / 60)), tr("min")}
|
|
||||||
: std::pair{QString("%1:%2").arg((int)s / 3600).arg(((int)s % 3600) / 60, 2, 10, QLatin1Char('0')), tr("hr")};
|
|
||||||
QString color = "#25DA6E";
|
|
||||||
if (s / s_typical > 1.5)
|
|
||||||
color = "#DA3025";
|
|
||||||
else if (s / s_typical > 1.2)
|
|
||||||
color = "#DAA725";
|
|
||||||
|
|
||||||
// Distance
|
|
||||||
auto distance = map_format_distance(d, uiState()->scene.is_metric);
|
|
||||||
|
|
||||||
eta_doc.setHtml(QString(R"(<body><table><tr style="vertical-align:bottom;"><td><b>%1</b></td><td>%2</td>
|
|
||||||
<td style="padding-left:40px;color:%3;"><b>%4</b></td><td style="padding-right:40px;color:%3;">%5</td>
|
|
||||||
<td><b>%6</b></td><td>%7</td></tr></body>)")
|
|
||||||
.arg(eta.first, eta.second, color, remaining.first, remaining.second, distance.first, distance.second));
|
|
||||||
|
|
||||||
setVisible(d >= MANEUVER_TRANSITION_THRESHOLD);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QTextDocument>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "common/params.h"
|
|
||||||
|
|
||||||
class MapETA : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
MapETA(QWidget * parent=nullptr);
|
|
||||||
void updateETA(float seconds, float seconds_typical, float distance);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
void showEvent(QShowEvent *event) override { format_24h = param.getBool("NavSettingTime24h"); }
|
|
||||||
|
|
||||||
bool format_24h = false;
|
|
||||||
QTextDocument eta_doc;
|
|
||||||
Params param;
|
|
||||||
};
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
#include "selfdrive/ui/qt/maps/map_helpers.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
|
|
||||||
#include "common/params.h"
|
|
||||||
#include "system/hardware/hw.h"
|
|
||||||
#include "selfdrive/ui/qt/api.h"
|
|
||||||
|
|
||||||
QString get_mapbox_token() {
|
|
||||||
// Valid for 4 weeks since we can't swap tokens on the fly
|
|
||||||
return MAPBOX_TOKEN.isEmpty() ? CommaApi::create_jwt({}, 4 * 7 * 24 * 3600) : MAPBOX_TOKEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMapLibre::Settings get_mapbox_settings() {
|
|
||||||
QMapLibre::Settings settings;
|
|
||||||
settings.setProviderTemplate(QMapLibre::Settings::ProviderTemplate::MapboxProvider);
|
|
||||||
|
|
||||||
if (!Hardware::PC()) {
|
|
||||||
settings.setCacheDatabasePath(MAPS_CACHE_PATH);
|
|
||||||
settings.setCacheDatabaseMaximumSize(100 * 1024 * 1024);
|
|
||||||
}
|
|
||||||
settings.setApiBaseUrl(MAPS_HOST);
|
|
||||||
settings.setApiKey(get_mapbox_token());
|
|
||||||
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in) {
|
|
||||||
return QGeoCoordinate(in.first, in.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
QMapLibre::CoordinatesCollections model_to_collection(
|
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
|
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
|
|
||||||
const cereal::XYZTData::Reader &line){
|
|
||||||
|
|
||||||
Eigen::Vector3d ecef(positionECEF.getValue()[0], positionECEF.getValue()[1], positionECEF.getValue()[2]);
|
|
||||||
Eigen::Vector3d orient(calibratedOrientationECEF.getValue()[0], calibratedOrientationECEF.getValue()[1], calibratedOrientationECEF.getValue()[2]);
|
|
||||||
Eigen::Matrix3d ecef_from_local = euler2rot(orient);
|
|
||||||
|
|
||||||
QMapLibre::Coordinates coordinates;
|
|
||||||
auto x = line.getX();
|
|
||||||
auto y = line.getY();
|
|
||||||
auto z = line.getZ();
|
|
||||||
for (int i = 0; i < x.size(); i++) {
|
|
||||||
Eigen::Vector3d point_ecef = ecef_from_local * Eigen::Vector3d(x[i], y[i], z[i]) + ecef;
|
|
||||||
Geodetic point_geodetic = ecef2geodetic((ECEF){.x = point_ecef[0], .y = point_ecef[1], .z = point_ecef[2]});
|
|
||||||
coordinates.push_back({point_geodetic.lat, point_geodetic.lon});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {QMapLibre::CoordinatesCollection{coordinates}};
|
|
||||||
}
|
|
||||||
|
|
||||||
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c) {
|
|
||||||
QMapLibre::Coordinates coordinates{c};
|
|
||||||
return {QMapLibre::CoordinatesCollection{coordinates}};
|
|
||||||
}
|
|
||||||
|
|
||||||
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader& coordinate_list) {
|
|
||||||
QMapLibre::Coordinates coordinates;
|
|
||||||
for (auto const &c : coordinate_list) {
|
|
||||||
coordinates.push_back({c.getLatitude(), c.getLongitude()});
|
|
||||||
}
|
|
||||||
return {QMapLibre::CoordinatesCollection{coordinates}};
|
|
||||||
}
|
|
||||||
|
|
||||||
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list) {
|
|
||||||
QMapLibre::Coordinates coordinates;
|
|
||||||
for (auto &c : coordinate_list) {
|
|
||||||
coordinates.push_back({c.latitude(), c.longitude()});
|
|
||||||
}
|
|
||||||
return {QMapLibre::CoordinatesCollection{coordinates}};
|
|
||||||
}
|
|
||||||
|
|
||||||
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString) {
|
|
||||||
QList<QGeoCoordinate> path;
|
|
||||||
if (polylineString.isEmpty())
|
|
||||||
return path;
|
|
||||||
|
|
||||||
QByteArray data = polylineString.toLatin1();
|
|
||||||
|
|
||||||
bool parsingLatitude = true;
|
|
||||||
|
|
||||||
int shift = 0;
|
|
||||||
int value = 0;
|
|
||||||
|
|
||||||
QGeoCoordinate coord(0, 0);
|
|
||||||
|
|
||||||
for (int i = 0; i < data.length(); ++i) {
|
|
||||||
unsigned char c = data.at(i) - 63;
|
|
||||||
|
|
||||||
value |= (c & 0x1f) << shift;
|
|
||||||
shift += 5;
|
|
||||||
|
|
||||||
// another chunk
|
|
||||||
if (c & 0x20)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int diff = (value & 1) ? ~(value >> 1) : (value >> 1);
|
|
||||||
|
|
||||||
if (parsingLatitude) {
|
|
||||||
coord.setLatitude(coord.latitude() + (double)diff/1e6);
|
|
||||||
} else {
|
|
||||||
coord.setLongitude(coord.longitude() + (double)diff/1e6);
|
|
||||||
path.append(coord);
|
|
||||||
}
|
|
||||||
|
|
||||||
parsingLatitude = !parsingLatitude;
|
|
||||||
|
|
||||||
value = 0;
|
|
||||||
shift = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string ¶m) {
|
|
||||||
QString json_str = QString::fromStdString(Params().get(param));
|
|
||||||
if (json_str.isEmpty()) return {};
|
|
||||||
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(json_str.toUtf8());
|
|
||||||
if (doc.isNull()) return {};
|
|
||||||
|
|
||||||
QJsonObject json = doc.object();
|
|
||||||
if (json["latitude"].isDouble() && json["longitude"].isDouble()) {
|
|
||||||
QMapLibre::Coordinate coord(json["latitude"].toDouble(), json["longitude"].toDouble());
|
|
||||||
return coord;
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return {distance, unit}
|
|
||||||
std::pair<QString, QString> map_format_distance(float d, bool is_metric) {
|
|
||||||
auto round_distance = [](float d) -> float {
|
|
||||||
return (d > 10) ? std::nearbyint(d) : std::nearbyint(d * 10) / 10.0;
|
|
||||||
};
|
|
||||||
|
|
||||||
d = std::max(d, 0.0f);
|
|
||||||
if (is_metric) {
|
|
||||||
return (d > 500) ? std::pair{QString::number(round_distance(d / 1000)), QObject::tr("km")}
|
|
||||||
: std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("m")};
|
|
||||||
} else {
|
|
||||||
float feet = d * METER_TO_FOOT;
|
|
||||||
return (feet > 500) ? std::pair{QString::number(round_distance(d * METER_TO_MILE)), QObject::tr("mi")}
|
|
||||||
: std::pair{QString::number(50 * std::nearbyint(d / 50)), QObject::tr("ft")};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <QMapLibre/Map>
|
|
||||||
#include <QMapLibre/Settings>
|
|
||||||
#include <eigen3/Eigen/Dense>
|
|
||||||
#include <QGeoCoordinate>
|
|
||||||
|
|
||||||
#include "common/params.h"
|
|
||||||
#include "common/util.h"
|
|
||||||
#include "common/transformations/coordinates.hpp"
|
|
||||||
#include "common/transformations/orientation.hpp"
|
|
||||||
#include "cereal/messaging/messaging.h"
|
|
||||||
|
|
||||||
const QString MAPBOX_TOKEN = QString::fromStdString(Params().get("MapboxPublicKey"));
|
|
||||||
const QString MAPS_HOST = util::getenv("MAPS_HOST", MAPBOX_TOKEN.isEmpty() ? "https://maps.comma.ai" : "https://api.mapbox.com").c_str();
|
|
||||||
const QString MAPS_CACHE_PATH = "/data/mbgl-cache-navd.db";
|
|
||||||
|
|
||||||
QString get_mapbox_token();
|
|
||||||
QMapLibre::Settings get_mapbox_settings();
|
|
||||||
QGeoCoordinate to_QGeoCoordinate(const QMapLibre::Coordinate &in);
|
|
||||||
QMapLibre::CoordinatesCollections model_to_collection(
|
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &calibratedOrientationECEF,
|
|
||||||
const cereal::LiveLocationKalman::Measurement::Reader &positionECEF,
|
|
||||||
const cereal::XYZTData::Reader &line);
|
|
||||||
QMapLibre::CoordinatesCollections coordinate_to_collection(const QMapLibre::Coordinate &c);
|
|
||||||
QMapLibre::CoordinatesCollections capnp_coordinate_list_to_collection(const capnp::List<cereal::NavRoute::Coordinate>::Reader &coordinate_list);
|
|
||||||
QMapLibre::CoordinatesCollections coordinate_list_to_collection(const QList<QGeoCoordinate> &coordinate_list);
|
|
||||||
QList<QGeoCoordinate> polyline_to_coordinate_list(const QString &polylineString);
|
|
||||||
std::optional<QMapLibre::Coordinate> coordinate_from_param(const std::string ¶m);
|
|
||||||
std::pair<QString, QString> map_format_distance(float d, bool is_metric);
|
|
||||||
@@ -1,144 +0,0 @@
|
|||||||
#include "selfdrive/ui/qt/maps/map_instructions.h"
|
|
||||||
|
|
||||||
#include <QDir>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/maps/map_helpers.h"
|
|
||||||
#include "selfdrive/ui/ui.h"
|
|
||||||
|
|
||||||
const QString ICON_SUFFIX = ".png";
|
|
||||||
|
|
||||||
MapInstructions::MapInstructions(QWidget *parent) : QWidget(parent) {
|
|
||||||
is_rhd = Params().getBool("IsRhdDetected");
|
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
|
||||||
main_layout->setContentsMargins(11, UI_BORDER_SIZE, 11, 20);
|
|
||||||
|
|
||||||
QHBoxLayout *top_layout = new QHBoxLayout;
|
|
||||||
top_layout->addWidget(icon_01 = new QLabel, 0, Qt::AlignTop);
|
|
||||||
|
|
||||||
QVBoxLayout *right_layout = new QVBoxLayout;
|
|
||||||
right_layout->setContentsMargins(9, 9, 9, 0);
|
|
||||||
right_layout->addWidget(distance = new QLabel);
|
|
||||||
distance->setStyleSheet(R"(font-size: 90px;)");
|
|
||||||
|
|
||||||
right_layout->addWidget(primary = new QLabel);
|
|
||||||
primary->setStyleSheet(R"(font-size: 60px;)");
|
|
||||||
primary->setWordWrap(true);
|
|
||||||
|
|
||||||
right_layout->addWidget(secondary = new QLabel);
|
|
||||||
secondary->setStyleSheet(R"(font-size: 50px;)");
|
|
||||||
secondary->setWordWrap(true);
|
|
||||||
|
|
||||||
top_layout->addLayout(right_layout);
|
|
||||||
|
|
||||||
main_layout->addLayout(top_layout);
|
|
||||||
main_layout->addLayout(lane_layout = new QHBoxLayout);
|
|
||||||
lane_layout->setAlignment(Qt::AlignHCenter);
|
|
||||||
lane_layout->setSpacing(10);
|
|
||||||
|
|
||||||
setStyleSheet("color:white");
|
|
||||||
QPalette pal = palette();
|
|
||||||
pal.setColor(QPalette::Background, QColor(0, 0, 0, 150));
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
setPalette(pal);
|
|
||||||
|
|
||||||
buildPixmapCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapInstructions::buildPixmapCache() {
|
|
||||||
QDir dir("../assets/navigation");
|
|
||||||
for (QString fn : dir.entryList({"*" + ICON_SUFFIX}, QDir::Files)) {
|
|
||||||
QPixmap pm(dir.filePath(fn));
|
|
||||||
QString key = fn.left(fn.size() - ICON_SUFFIX.length());
|
|
||||||
pm = pm.scaledToWidth(200, Qt::SmoothTransformation);
|
|
||||||
|
|
||||||
// Maneuver icons
|
|
||||||
pixmap_cache[key] = pm;
|
|
||||||
// lane direction icons
|
|
||||||
if (key.contains("turn_")) {
|
|
||||||
pixmap_cache["lane_" + key] = pm.scaled({125, 125}, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
|
||||||
}
|
|
||||||
|
|
||||||
// for rhd, reflect direction and then flip
|
|
||||||
if (key.contains("_left")) {
|
|
||||||
pixmap_cache["rhd_" + key.replace("_left", "_right")] = pm.transformed(QTransform().scale(-1, 1));
|
|
||||||
} else if (key.contains("_right")) {
|
|
||||||
pixmap_cache["rhd_" + key.replace("_right", "_left")] = pm.transformed(QTransform().scale(-1, 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapInstructions::updateInstructions(cereal::NavInstruction::Reader instruction) {
|
|
||||||
setUpdatesEnabled(false);
|
|
||||||
|
|
||||||
// Show instruction text
|
|
||||||
QString primary_str = QString::fromStdString(instruction.getManeuverPrimaryText());
|
|
||||||
QString secondary_str = QString::fromStdString(instruction.getManeuverSecondaryText());
|
|
||||||
|
|
||||||
primary->setText(primary_str);
|
|
||||||
secondary->setVisible(secondary_str.length() > 0);
|
|
||||||
secondary->setText(secondary_str);
|
|
||||||
|
|
||||||
auto distance_str_pair = map_format_distance(instruction.getManeuverDistance(), uiState()->scene.is_metric);
|
|
||||||
distance->setText(QString("%1 %2").arg(distance_str_pair.first, distance_str_pair.second));
|
|
||||||
|
|
||||||
// Show arrow with direction
|
|
||||||
QString type = QString::fromStdString(instruction.getManeuverType());
|
|
||||||
QString modifier = QString::fromStdString(instruction.getManeuverModifier());
|
|
||||||
if (!type.isEmpty()) {
|
|
||||||
QString fn = "direction_" + type;
|
|
||||||
if (!modifier.isEmpty()) {
|
|
||||||
fn += "_" + modifier;
|
|
||||||
}
|
|
||||||
fn = fn.replace(' ', '_');
|
|
||||||
bool rhd = is_rhd && (fn.contains("_left") || fn.contains("_right"));
|
|
||||||
icon_01->setPixmap(pixmap_cache[!rhd ? fn : "rhd_" + fn]);
|
|
||||||
icon_01->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
|
|
||||||
icon_01->setVisible(true);
|
|
||||||
} else {
|
|
||||||
icon_01->setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide distance after arrival
|
|
||||||
distance->setVisible(type != "arrive" || instruction.getManeuverDistance() > 0);
|
|
||||||
|
|
||||||
// Show lanes
|
|
||||||
auto lanes = instruction.getLanes();
|
|
||||||
for (int i = 0; i < lanes.size(); ++i) {
|
|
||||||
bool active = lanes[i].getActive();
|
|
||||||
const auto active_direction = lanes[i].getActiveDirection();
|
|
||||||
|
|
||||||
// TODO: Make more images based on active direction and combined directions
|
|
||||||
QString fn = "lane_direction_";
|
|
||||||
|
|
||||||
// active direction has precedence
|
|
||||||
if (active && active_direction != cereal::NavInstruction::Direction::NONE) {
|
|
||||||
fn += "turn_" + DIRECTIONS[active_direction];
|
|
||||||
} else {
|
|
||||||
for (auto const &direction : lanes[i].getDirections()) {
|
|
||||||
if (direction != cereal::NavInstruction::Direction::NONE) {
|
|
||||||
fn += "turn_" + DIRECTIONS[direction];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!active) {
|
|
||||||
fn += "_inactive";
|
|
||||||
}
|
|
||||||
|
|
||||||
QLabel *label = (i < lane_labels.size()) ? lane_labels[i] : lane_labels.emplace_back(new QLabel);
|
|
||||||
if (!label->parentWidget()) {
|
|
||||||
lane_layout->addWidget(label);
|
|
||||||
}
|
|
||||||
label->setPixmap(pixmap_cache[fn]);
|
|
||||||
label->setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = lanes.size(); i < lane_labels.size(); ++i) {
|
|
||||||
lane_labels[i]->setVisible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setUpdatesEnabled(true);
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
|
|
||||||
#include "cereal/gen/cpp/log.capnp.h"
|
|
||||||
|
|
||||||
static std::map<cereal::NavInstruction::Direction, QString> DIRECTIONS = {
|
|
||||||
{cereal::NavInstruction::Direction::NONE, "none"},
|
|
||||||
{cereal::NavInstruction::Direction::LEFT, "left"},
|
|
||||||
{cereal::NavInstruction::Direction::RIGHT, "right"},
|
|
||||||
{cereal::NavInstruction::Direction::STRAIGHT, "straight"},
|
|
||||||
{cereal::NavInstruction::Direction::SLIGHT_LEFT, "slight_left"},
|
|
||||||
{cereal::NavInstruction::Direction::SLIGHT_RIGHT, "slight_right"},
|
|
||||||
};
|
|
||||||
|
|
||||||
class MapInstructions : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLabel *distance;
|
|
||||||
QLabel *primary;
|
|
||||||
QLabel *secondary;
|
|
||||||
QLabel *icon_01;
|
|
||||||
QHBoxLayout *lane_layout;
|
|
||||||
bool is_rhd = false;
|
|
||||||
std::vector<QLabel *> lane_labels;
|
|
||||||
QHash<QString, QPixmap> pixmap_cache;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MapInstructions(QWidget * parent=nullptr);
|
|
||||||
void buildPixmapCache();
|
|
||||||
void updateInstructions(cereal::NavInstruction::Reader instruction);
|
|
||||||
};
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "selfdrive/ui/qt/maps/map_panel.h"
|
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/maps/map.h"
|
|
||||||
#include "selfdrive/ui/qt/maps/map_settings.h"
|
|
||||||
#include "selfdrive/ui/qt/util.h"
|
|
||||||
#include "selfdrive/ui/ui.h"
|
|
||||||
|
|
||||||
MapPanel::MapPanel(const QMapLibre::Settings &mapboxSettings, QWidget *parent) : QFrame(parent) {
|
|
||||||
content_stack = new QStackedLayout(this);
|
|
||||||
content_stack->setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
auto map = new MapWindow(mapboxSettings);
|
|
||||||
QObject::connect(uiState(), &UIState::offroadTransition, map, &MapWindow::offroadTransition);
|
|
||||||
QObject::connect(device(), &Device::interactiveTimeout, this, [=]() {
|
|
||||||
content_stack->setCurrentIndex(0);
|
|
||||||
});
|
|
||||||
QObject::connect(map, &MapWindow::requestVisible, this, [=](bool visible) {
|
|
||||||
// when we show the map for a new route, signal HomeWindow to hide the sidebar
|
|
||||||
if (visible) { emit mapPanelRequested(); }
|
|
||||||
setVisible(visible);
|
|
||||||
});
|
|
||||||
QObject::connect(map, &MapWindow::requestSettings, this, [=](bool settings) {
|
|
||||||
content_stack->setCurrentIndex(settings ? 1 : 0);
|
|
||||||
});
|
|
||||||
content_stack->addWidget(map);
|
|
||||||
|
|
||||||
auto settings = new MapSettings(true, parent);
|
|
||||||
QObject::connect(settings, &MapSettings::closeSettings, this, [=]() {
|
|
||||||
content_stack->setCurrentIndex(0);
|
|
||||||
});
|
|
||||||
content_stack->addWidget(settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapPanel::toggleMapSettings() {
|
|
||||||
// show settings if not visible, then toggle between map and settings
|
|
||||||
int new_index = isVisible() ? (1 - content_stack->currentIndex()) : 1;
|
|
||||||
content_stack->setCurrentIndex(new_index);
|
|
||||||
emit mapPanelRequested();
|
|
||||||
show();
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QFrame>
|
|
||||||
#include <QMapLibre/Settings>
|
|
||||||
#include <QStackedLayout>
|
|
||||||
|
|
||||||
class MapPanel : public QFrame {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MapPanel(const QMapLibre::Settings &settings, QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void mapPanelRequested();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void toggleMapSettings();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QStackedLayout *content_stack;
|
|
||||||
};
|
|
||||||
@@ -1,393 +0,0 @@
|
|||||||
#include "selfdrive/ui/qt/maps/map_settings.h"
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "common/util.h"
|
|
||||||
#include "selfdrive/ui/qt/request_repeater.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 ×tamp) {
|
|
||||||
QDateTime dt = QDateTime::fromString(timestamp, Qt::ISODate);
|
|
||||||
return dt.isValid() ? dt.toSecsSinceEpoch() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
MapSettings::MapSettings(bool closeable, QWidget *parent) : QFrame(parent) {
|
|
||||||
setContentsMargins(0, 0, 0, 0);
|
|
||||||
setAttribute(Qt::WA_NoMousePropagation);
|
|
||||||
|
|
||||||
auto *frame = new QVBoxLayout(this);
|
|
||||||
frame->setContentsMargins(40, 40, 40, 0);
|
|
||||||
frame->setSpacing(0);
|
|
||||||
|
|
||||||
auto *heading_frame = new QHBoxLayout;
|
|
||||||
heading_frame->setContentsMargins(0, 0, 0, 0);
|
|
||||||
heading_frame->setSpacing(32);
|
|
||||||
{
|
|
||||||
if (closeable) {
|
|
||||||
auto *close_btn = new QPushButton("←");
|
|
||||||
close_btn->setStyleSheet(R"(
|
|
||||||
QPushButton {
|
|
||||||
color: #FFFFFF;
|
|
||||||
font-size: 100px;
|
|
||||||
padding-bottom: 8px;
|
|
||||||
border 1px grey solid;
|
|
||||||
border-radius: 70px;
|
|
||||||
background-color: #292929;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
QPushButton:pressed {
|
|
||||||
background-color: #3B3B3B;
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
close_btn->setFixedSize(140, 140);
|
|
||||||
QObject::connect(close_btn, &QPushButton::clicked, [=]() { emit closeSettings(); });
|
|
||||||
// TODO: read map_on_left from ui state
|
|
||||||
heading_frame->addWidget(close_btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *heading = new QVBoxLayout;
|
|
||||||
heading->setContentsMargins(0, 0, 0, 0);
|
|
||||||
heading->setSpacing(16);
|
|
||||||
{
|
|
||||||
auto *title = new QLabel(tr("NAVIGATION"), this);
|
|
||||||
title->setStyleSheet("color: #FFFFFF; font-size: 54px; font-weight: 600;");
|
|
||||||
heading->addWidget(title);
|
|
||||||
|
|
||||||
subtitle = new QLabel(tr("Manage at connect.comma.ai"), this);
|
|
||||||
subtitle->setStyleSheet("color: #A0A0A0; font-size: 40px; font-weight: 300;");
|
|
||||||
heading->addWidget(subtitle);
|
|
||||||
}
|
|
||||||
heading_frame->addLayout(heading, 1);
|
|
||||||
}
|
|
||||||
frame->addLayout(heading_frame);
|
|
||||||
frame->addSpacing(32);
|
|
||||||
|
|
||||||
current_widget = new DestinationWidget(this);
|
|
||||||
QObject::connect(current_widget, &DestinationWidget::actionClicked,
|
|
||||||
[]() { NavManager::instance()->setCurrentDestination({}); });
|
|
||||||
frame->addWidget(current_widget);
|
|
||||||
frame->addSpacing(32);
|
|
||||||
|
|
||||||
QWidget *destinations_container = new QWidget(this);
|
|
||||||
destinations_layout = new QVBoxLayout(destinations_container);
|
|
||||||
destinations_layout->setContentsMargins(0, 32, 0, 32);
|
|
||||||
destinations_layout->setSpacing(20);
|
|
||||||
destinations_layout->addWidget(home_widget = new DestinationWidget(this));
|
|
||||||
destinations_layout->addWidget(work_widget = new DestinationWidget(this));
|
|
||||||
QObject::connect(home_widget, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
|
|
||||||
QObject::connect(work_widget, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
|
|
||||||
destinations_layout->addStretch();
|
|
||||||
|
|
||||||
ScrollView *destinations_scroller = new ScrollView(destinations_container, this);
|
|
||||||
destinations_scroller->setFrameShape(QFrame::NoFrame);
|
|
||||||
frame->addWidget(destinations_scroller);
|
|
||||||
|
|
||||||
setStyleSheet("MapSettings { background-color: #333333; }");
|
|
||||||
QObject::connect(NavManager::instance(), &NavManager::updated, this, &MapSettings::refresh);
|
|
||||||
|
|
||||||
wifi = new WifiManager(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapSettings::showEvent(QShowEvent *event) {
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapSettings::refresh() {
|
|
||||||
if (!isVisible()) return;
|
|
||||||
|
|
||||||
setUpdatesEnabled(false);
|
|
||||||
|
|
||||||
auto get_w = [this](int i) {
|
|
||||||
auto w = i < widgets.size() ? widgets[i] : widgets.emplace_back(new DestinationWidget);
|
|
||||||
if (!w->parentWidget()) {
|
|
||||||
destinations_layout->insertWidget(destinations_layout->count() - 1, w);
|
|
||||||
QObject::connect(w, &DestinationWidget::navigateTo, this, &MapSettings::navigateTo);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
work_widget->unset(NAV_FAVORITE_LABEL_WORK);
|
|
||||||
|
|
||||||
int n = 0;
|
|
||||||
for (auto location : NavManager::instance()->currentLocations()) {
|
|
||||||
DestinationWidget *w = nullptr;
|
|
||||||
auto dest = location.toObject();
|
|
||||||
if (dest["save_type"].toString() == NAV_TYPE_FAVORITE) {
|
|
||||||
auto label = dest["label"].toString();
|
|
||||||
if (label == NAV_FAVORITE_LABEL_HOME) w = home_widget;
|
|
||||||
if (label == NAV_FAVORITE_LABEL_WORK) w = work_widget;
|
|
||||||
}
|
|
||||||
w = w ? w : get_w(n++);
|
|
||||||
w->set(dest, false);
|
|
||||||
w->setVisible(!locationEqual(dest, current_dest));
|
|
||||||
}
|
|
||||||
for (; n < widgets.size(); ++n) widgets[n]->setVisible(false);
|
|
||||||
|
|
||||||
setUpdatesEnabled(true);
|
|
||||||
|
|
||||||
// Use IP for NOO without Prime
|
|
||||||
if (!uiState()->hasPrime()) {
|
|
||||||
QString ipAddress = QString("%1:8082").arg(wifi->getIp4Address());
|
|
||||||
subtitle->setText(tr("Manage at %1").arg(ipAddress));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapSettings::navigateTo(const QJsonObject &place) {
|
|
||||||
NavManager::instance()->setCurrentDestination(place);
|
|
||||||
emit closeSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
DestinationWidget::DestinationWidget(QWidget *parent) : QPushButton(parent) {
|
|
||||||
setContentsMargins(0, 0, 0, 0);
|
|
||||||
|
|
||||||
auto *frame = new QHBoxLayout(this);
|
|
||||||
frame->setContentsMargins(32, 24, 32, 24);
|
|
||||||
frame->setSpacing(32);
|
|
||||||
|
|
||||||
icon = new QLabel(this);
|
|
||||||
icon->setAlignment(Qt::AlignCenter);
|
|
||||||
icon->setFixedSize(96, 96);
|
|
||||||
icon->setObjectName("icon");
|
|
||||||
frame->addWidget(icon);
|
|
||||||
|
|
||||||
auto *inner_frame = new QVBoxLayout;
|
|
||||||
inner_frame->setContentsMargins(0, 0, 0, 0);
|
|
||||||
inner_frame->setSpacing(0);
|
|
||||||
{
|
|
||||||
title = new ElidedLabel(this);
|
|
||||||
title->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
inner_frame->addWidget(title);
|
|
||||||
|
|
||||||
subtitle = new ElidedLabel(this);
|
|
||||||
subtitle->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
||||||
subtitle->setObjectName("subtitle");
|
|
||||||
inner_frame->addWidget(subtitle);
|
|
||||||
}
|
|
||||||
frame->addLayout(inner_frame, 1);
|
|
||||||
|
|
||||||
action = new QPushButton(this);
|
|
||||||
action->setFixedSize(96, 96);
|
|
||||||
action->setObjectName("action");
|
|
||||||
action->setStyleSheet("font-size: 65px; font-weight: 600;");
|
|
||||||
QObject::connect(action, &QPushButton::clicked, this, &QPushButton::clicked);
|
|
||||||
QObject::connect(action, &QPushButton::clicked, this, &DestinationWidget::actionClicked);
|
|
||||||
frame->addWidget(action);
|
|
||||||
|
|
||||||
setFixedHeight(164);
|
|
||||||
setStyleSheet(R"(
|
|
||||||
DestinationWidget { background-color: #202123; border-radius: 10px; }
|
|
||||||
QLabel { color: #FFFFFF; font-size: 48px; font-weight: 400; }
|
|
||||||
#icon { background-color: #3B4356; border-radius: 48px; }
|
|
||||||
#subtitle { color: #9BA0A5; }
|
|
||||||
#action { border: none; border-radius: 48px; color: #FFFFFF; padding-bottom: 4px; }
|
|
||||||
|
|
||||||
/* current destination */
|
|
||||||
[current="true"] { background-color: #E8E8E8; }
|
|
||||||
[current="true"] QLabel { color: #000000; }
|
|
||||||
[current="true"] #icon { background-color: #42906B; }
|
|
||||||
[current="true"] #subtitle { color: #333333; }
|
|
||||||
[current="true"] #action { color: #202123; }
|
|
||||||
|
|
||||||
/* no saved destination */
|
|
||||||
[set="false"] QLabel { color: #9BA0A5; }
|
|
||||||
[current="true"][set="false"] QLabel { color: #A0000000; }
|
|
||||||
|
|
||||||
/* pressed */
|
|
||||||
[current="false"]:pressed { background-color: #18191B; }
|
|
||||||
[current="true"] #action:pressed { background-color: #D6D6D6; }
|
|
||||||
)");
|
|
||||||
QObject::connect(this, &QPushButton::clicked, [this]() { if (!dest.isEmpty()) emit navigateTo(dest); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void DestinationWidget::set(const QJsonObject &destination, bool current) {
|
|
||||||
if (dest == destination) return;
|
|
||||||
|
|
||||||
dest = destination;
|
|
||||||
setProperty("current", current);
|
|
||||||
setProperty("set", true);
|
|
||||||
|
|
||||||
auto icon_pixmap = current ? icons().directions : icons().recent;
|
|
||||||
auto title_text = destination["place_name"].toString();
|
|
||||||
auto subtitle_text = destination["place_details"].toString();
|
|
||||||
|
|
||||||
if (destination["save_type"] == NAV_TYPE_FAVORITE) {
|
|
||||||
if (destination["label"] == NAV_FAVORITE_LABEL_HOME) {
|
|
||||||
icon_pixmap = icons().home;
|
|
||||||
subtitle_text = title_text + ", " + subtitle_text;
|
|
||||||
title_text = tr("Home");
|
|
||||||
} else if (destination["label"] == NAV_FAVORITE_LABEL_WORK) {
|
|
||||||
icon_pixmap = icons().work;
|
|
||||||
subtitle_text = title_text + ", " + subtitle_text;
|
|
||||||
title_text = tr("Work");
|
|
||||||
} else {
|
|
||||||
icon_pixmap = icons().favorite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
icon->setPixmap(icon_pixmap);
|
|
||||||
|
|
||||||
title->setText(title_text);
|
|
||||||
subtitle->setText(subtitle_text);
|
|
||||||
subtitle->setVisible(true);
|
|
||||||
|
|
||||||
// TODO: use pixmap
|
|
||||||
action->setAttribute(Qt::WA_TransparentForMouseEvents, !current);
|
|
||||||
action->setText(current ? "×" : "→");
|
|
||||||
action->setVisible(true);
|
|
||||||
|
|
||||||
setStyleSheet(styleSheet());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DestinationWidget::unset(const QString &label, bool current) {
|
|
||||||
dest = {};
|
|
||||||
setProperty("current", current);
|
|
||||||
setProperty("set", false);
|
|
||||||
|
|
||||||
if (label.isEmpty()) {
|
|
||||||
icon->setPixmap(icons().directions);
|
|
||||||
title->setText(tr("No destination set"));
|
|
||||||
} else {
|
|
||||||
QString title_text = label == NAV_FAVORITE_LABEL_HOME ? tr("home") : tr("work");
|
|
||||||
icon->setPixmap(label == NAV_FAVORITE_LABEL_HOME ? icons().home : icons().work);
|
|
||||||
title->setText(tr("No %1 location set").arg(title_text));
|
|
||||||
}
|
|
||||||
|
|
||||||
subtitle->setVisible(false);
|
|
||||||
action->setVisible(false);
|
|
||||||
|
|
||||||
setStyleSheet(styleSheet());
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// singleton NavManager
|
|
||||||
|
|
||||||
NavManager *NavManager::instance() {
|
|
||||||
static NavManager *request = new NavManager(qApp);
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()) {
|
|
||||||
{
|
|
||||||
// Fetch favorite and recent locations
|
|
||||||
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/locations";
|
|
||||||
RequestRepeater *repeater = new RequestRepeater(this, url, "ApiCache_NavDestinations", 30, true);
|
|
||||||
QObject::connect(repeater, &RequestRepeater::requestDone, this, &NavManager::parseLocationsResponse);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto param_watcher = new ParamWatcher(this);
|
|
||||||
QObject::connect(param_watcher, &ParamWatcher::paramChanged, this, &NavManager::updated);
|
|
||||||
|
|
||||||
// Destination set while offline
|
|
||||||
QString url = CommaApi::BASE_URL + "/v1/navigation/" + *dongle_id + "/next";
|
|
||||||
HttpRequest *deleter = new HttpRequest(this);
|
|
||||||
RequestRepeater *repeater = new RequestRepeater(this, url, "", 10, true);
|
|
||||||
QObject::connect(repeater, &RequestRepeater::requestDone, [=](const QString &resp, bool success) {
|
|
||||||
if (success && resp != "null") {
|
|
||||||
if (params.get("NavDestination").empty()) {
|
|
||||||
qWarning() << "Setting NavDestination from /next" << resp;
|
|
||||||
params.put("NavDestination", resp.toStdString());
|
|
||||||
} else {
|
|
||||||
qWarning() << "Got location from /next, but NavDestination already set";
|
|
||||||
}
|
|
||||||
// Send DELETE to clear destination server side
|
|
||||||
deleter->sendRequest(url, HttpRequest::Method::DELETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// athena can set destination at any time
|
|
||||||
param_watcher->addParam("NavDestination");
|
|
||||||
current_dest = QJsonDocument::fromJson(params.get("NavDestination").c_str()).object();
|
|
||||||
emit updated();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NavManager::parseLocationsResponse(const QString &response, bool success) {
|
|
||||||
if (!success || response == prev_response) return;
|
|
||||||
|
|
||||||
prev_response = response;
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response.trimmed().toUtf8());
|
|
||||||
if (doc.isNull()) {
|
|
||||||
qWarning() << "JSON Parse failed on navigation locations" << response;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
std::stable_sort(locations.begin(), locations.end(), [](const QJsonValue &a, const QJsonValue &b) {
|
|
||||||
if (a["save_type"] == NAV_TYPE_FAVORITE || b["save_type"] == NAV_TYPE_FAVORITE) {
|
|
||||||
return (std::tuple(a["save_type"].toString(), a["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());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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,108 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <future>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <QFrame>
|
|
||||||
#include <QJsonArray>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "common/params.h"
|
|
||||||
#include "selfdrive/ui/qt/network/wifi_manager.h"
|
|
||||||
#include "selfdrive/ui/qt/util.h"
|
|
||||||
#include "selfdrive/ui/qt/widgets/controls.h"
|
|
||||||
|
|
||||||
const QString NAV_TYPE_FAVORITE = "favorite";
|
|
||||||
const QString NAV_TYPE_RECENT = "recent";
|
|
||||||
|
|
||||||
const QString NAV_FAVORITE_LABEL_HOME = "home";
|
|
||||||
const QString NAV_FAVORITE_LABEL_WORK = "work";
|
|
||||||
|
|
||||||
class DestinationWidget;
|
|
||||||
|
|
||||||
class NavManager : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
static NavManager *instance();
|
|
||||||
QJsonArray currentLocations() const { return locations; }
|
|
||||||
QJsonObject currentDestination() const { return current_dest; }
|
|
||||||
void setCurrentDestination(const QJsonObject &loc);
|
|
||||||
qint64 getLastActivity(const QJsonObject &loc) const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void updated();
|
|
||||||
|
|
||||||
private:
|
|
||||||
NavManager(QObject *parent);
|
|
||||||
void parseLocationsResponse(const QString &response, bool success);
|
|
||||||
void sortLocations();
|
|
||||||
|
|
||||||
Params params;
|
|
||||||
QString prev_response;
|
|
||||||
QJsonArray locations;
|
|
||||||
QJsonObject current_dest;
|
|
||||||
std::future<void> write_param_future;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MapSettings : public QFrame {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit MapSettings(bool closeable = false, QWidget *parent = nullptr);
|
|
||||||
void navigateTo(const QJsonObject &place);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void showEvent(QShowEvent *event) override;
|
|
||||||
void refresh();
|
|
||||||
|
|
||||||
QVBoxLayout *destinations_layout;
|
|
||||||
DestinationWidget *current_widget;
|
|
||||||
DestinationWidget *home_widget;
|
|
||||||
DestinationWidget *work_widget;
|
|
||||||
std::vector<DestinationWidget *> widgets;
|
|
||||||
|
|
||||||
// FrogPilot variables
|
|
||||||
QLabel *subtitle;
|
|
||||||
|
|
||||||
WifiManager *wifi;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void closeSettings();
|
|
||||||
};
|
|
||||||
|
|
||||||
class DestinationWidget : public QPushButton {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DestinationWidget(QWidget *parent = nullptr);
|
|
||||||
void set(const QJsonObject &location, bool current = false);
|
|
||||||
void unset(const QString &label, bool current = false);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void actionClicked();
|
|
||||||
void navigateTo(const QJsonObject &destination);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct NavIcons {
|
|
||||||
QPixmap home, work, favorite, recent, directions;
|
|
||||||
};
|
|
||||||
|
|
||||||
static NavIcons icons() {
|
|
||||||
static NavIcons nav_icons {
|
|
||||||
loadPixmap("../assets/navigation/icon_home.svg", {48, 48}),
|
|
||||||
loadPixmap("../assets/navigation/icon_work.svg", {48, 48}),
|
|
||||||
loadPixmap("../assets/navigation/icon_favorite.svg", {48, 48}),
|
|
||||||
loadPixmap("../assets/navigation/icon_recent.svg", {48, 48}),
|
|
||||||
loadPixmap("../assets/navigation/icon_directions.svg", {48, 48}),
|
|
||||||
};
|
|
||||||
return nav_icons;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLabel *icon, *title, *subtitle;
|
|
||||||
QPushButton *action;
|
|
||||||
QJsonObject dest;
|
|
||||||
};
|
|
||||||
@@ -205,16 +205,6 @@ AdvancedNetworking::AdvancedNetworking(QWidget* parent, WifiManager* wifi): QWid
|
|||||||
});
|
});
|
||||||
list->addItem(hiddenNetworkButton);
|
list->addItem(hiddenNetworkButton);
|
||||||
|
|
||||||
// Set initial config
|
|
||||||
wifi->updateGsmSettings(roamingEnabled, QString::fromStdString(params.get("GsmApn")), metered);
|
|
||||||
|
|
||||||
connect(uiState(), &UIState::primeTypeChanged, this, [=](PrimeType prime_type) {
|
|
||||||
bool gsmVisible = prime_type == PrimeType::NONE || prime_type == PrimeType::LITE;
|
|
||||||
roamingToggle->setVisible(gsmVisible);
|
|
||||||
editApnButton->setVisible(gsmVisible);
|
|
||||||
meteredToggle->setVisible(gsmVisible);
|
|
||||||
});
|
|
||||||
|
|
||||||
main_layout->addWidget(new ScrollView(list, this));
|
main_layout->addWidget(new ScrollView(list, this));
|
||||||
main_layout->addStretch(1);
|
main_layout->addStretch(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "selfdrive/ui/qt/network/wifi_manager.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 "common/params.h"
|
#include "common/params.h"
|
||||||
#include "common/swaglog.h"
|
#include "common/swaglog.h"
|
||||||
@@ -432,10 +431,8 @@ void WifiManager::addTetheringConnection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WifiManager::tetheringActivated(QDBusPendingCallWatcher *call) {
|
void WifiManager::tetheringActivated(QDBusPendingCallWatcher *call) {
|
||||||
int prime_type = uiState()->primeType();
|
// Todo: this is false if not prime. Override?
|
||||||
int ipv4_forward = (prime_type == PrimeType::NONE || prime_type == PrimeType::LITE);
|
if (!ipv4_forward && false) {
|
||||||
|
|
||||||
if (!ipv4_forward) {
|
|
||||||
QTimer::singleShot(5000, this, [=] {
|
QTimer::singleShot(5000, this, [=] {
|
||||||
qWarning() << "net.ipv4.ip_forward = 0";
|
qWarning() << "net.ipv4.ip_forward = 0";
|
||||||
std::system("sudo sysctl net.ipv4.ip_forward=0");
|
std::system("sudo sysctl net.ipv4.ip_forward=0");
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
#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/frogpilot/navigation/ui/navigation_settings.h"
|
|
||||||
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
|
#include "selfdrive/frogpilot/ui/qt/offroad/control_settings.h"
|
||||||
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
#include "selfdrive/frogpilot/ui/qt/offroad/vehicle_settings.h"
|
||||||
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
#include "selfdrive/frogpilot/ui/qt/offroad/visual_settings.h"
|
||||||
@@ -79,20 +78,6 @@ TogglesPanel::TogglesPanel(SettingsWindow *parent) : ListWidget(parent) {
|
|||||||
tr("Display speed in km/h instead of mph."),
|
tr("Display speed in km/h instead of mph."),
|
||||||
"../assets/offroad/icon_metric.png",
|
"../assets/offroad/icon_metric.png",
|
||||||
},
|
},
|
||||||
#ifdef ENABLE_MAPS
|
|
||||||
{
|
|
||||||
"NavSettingTime24h",
|
|
||||||
tr("Show ETA in 24h Format"),
|
|
||||||
tr("Use 24h format instead of am/pm"),
|
|
||||||
"../assets/offroad/icon_metric.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NavSettingLeftSide",
|
|
||||||
tr("Show Map on Left Side of UI"),
|
|
||||||
tr("Show map on left side when in split screen view."),
|
|
||||||
"../assets/offroad/icon_road.png",
|
|
||||||
},
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -161,8 +146,6 @@ void TogglesPanel::updateToggles() {
|
|||||||
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
|
disengage_on_accelerator_toggle->setVisible(!params.getBool("AlwaysOnLateral"));
|
||||||
auto driver_camera_toggle = toggles["RecordFront"];
|
auto driver_camera_toggle = toggles["RecordFront"];
|
||||||
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
|
driver_camera_toggle->setVisible(!(params.getBool("DeviceManagement") && params.getBool("NoLogging") && params.getBool("NoUploads")));
|
||||||
auto nav_settings_left_toggle = toggles["NavSettingLeftSide"];
|
|
||||||
nav_settings_left_toggle->setVisible(!params.getBool("FullMap"));
|
|
||||||
|
|
||||||
auto experimental_mode_toggle = toggles["ExperimentalMode"];
|
auto experimental_mode_toggle = toggles["ExperimentalMode"];
|
||||||
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
|
auto op_long_toggle = toggles["ExperimentalLongitudinalEnabled"];
|
||||||
@@ -178,10 +161,6 @@ void TogglesPanel::updateToggles() {
|
|||||||
.arg(tr("Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. "
|
.arg(tr("Let the driving model control the gas and brakes. openpilot will drive as it thinks a human would, including stopping for red lights and stop signs. "
|
||||||
"Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; "
|
"Since the driving model decides the speed to drive, the set speed will only act as an upper bound. This is an alpha quality feature; "
|
||||||
"mistakes should be expected."))
|
"mistakes should be expected."))
|
||||||
.arg(tr("Navigate on openpilot"))
|
|
||||||
.arg(tr("When navigation has a destination, openpilot will input the map information into the model. This provides useful context for the model and allows openpilot to keep left or right "
|
|
||||||
"appropriately at forks/exits. Lane change behavior is unchanged and still activated by the driver. This is an alpha quality feature; mistakes should be expected, particularly around "
|
|
||||||
"exits and forks. These mistakes can include unintended laneline crossings, late exit taking, driving towards dividing barriers in the gore areas, etc."))
|
|
||||||
.arg(tr("New Driving Visualization"))
|
.arg(tr("New Driving Visualization"))
|
||||||
.arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. "
|
.arg(tr("The driving visualization will transition to the road-facing wide-angle camera at low speeds to better show some turns. The Experimental mode logo will also be shown in the top right corner. "
|
||||||
"When a navigation destination is set and the driving model is using it as input, the driving path on the map will turn green."));
|
"When a navigation destination is set and the driving model is using it as input, the driving path on the map will turn green."));
|
||||||
@@ -651,7 +630,6 @@ void SettingsWindow::showEvent(QShowEvent *event) {
|
|||||||
|
|
||||||
void SettingsWindow::setCurrentPanel(int index, const QString ¶m) {
|
void SettingsWindow::setCurrentPanel(int index, const QString ¶m) {
|
||||||
panel_widget->setCurrentIndex(index);
|
panel_widget->setCurrentIndex(index);
|
||||||
nav_btns->buttons()[index]->setChecked(true);
|
|
||||||
if (!param.isEmpty()) {
|
if (!param.isEmpty()) {
|
||||||
emit expandToggleDescription(param);
|
emit expandToggleDescription(param);
|
||||||
}
|
}
|
||||||
@@ -715,16 +693,13 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
{tr("Toggles"), toggles},
|
{tr("Toggles"), toggles},
|
||||||
{tr("Software"), new SoftwarePanel(this)},
|
{tr("Software"), new SoftwarePanel(this)},
|
||||||
{tr("Controls"), frogpilotControls},
|
{tr("Controls"), frogpilotControls},
|
||||||
{tr("Navigation"), new FrogPilotNavigationPanel(this)},
|
|
||||||
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
|
{tr("Vehicles"), new FrogPilotVehiclesPanel(this)},
|
||||||
{tr("Visuals"), frogpilotVisuals},
|
{tr("Visuals"), frogpilotVisuals},
|
||||||
};
|
};
|
||||||
|
|
||||||
nav_btns = new QButtonGroup(this);
|
|
||||||
for (auto &[name, panel] : panels) {
|
for (auto &[name, panel] : panels) {
|
||||||
QPushButton *btn = new QPushButton(name);
|
QPushButton *btn = new QPushButton(name);
|
||||||
btn->setCheckable(true);
|
btn->setCheckable(true);
|
||||||
btn->setChecked(nav_btns->buttons().size() == 0);
|
|
||||||
btn->setStyleSheet(R"(
|
btn->setStyleSheet(R"(
|
||||||
QPushButton {
|
QPushButton {
|
||||||
color: grey;
|
color: grey;
|
||||||
@@ -741,7 +716,6 @@ SettingsWindow::SettingsWindow(QWidget *parent) : QFrame(parent) {
|
|||||||
}
|
}
|
||||||
)");
|
)");
|
||||||
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
btn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding);
|
||||||
nav_btns->addButton(btn);
|
|
||||||
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
|
sidebar_layout->addWidget(btn, 0, Qt::AlignRight);
|
||||||
|
|
||||||
const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins
|
const int lr_margin = name != tr("Network") ? 50 : 0; // Network panel handles its own margins
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QPushButton *sidebar_alert_widget;
|
QPushButton *sidebar_alert_widget;
|
||||||
QWidget *sidebar_widget;
|
QWidget *sidebar_widget;
|
||||||
QButtonGroup *nav_btns;
|
|
||||||
QStackedWidget *panel_widget;
|
QStackedWidget *panel_widget;
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
|
|||||||
@@ -13,10 +13,6 @@
|
|||||||
#include "common/swaglog.h"
|
#include "common/swaglog.h"
|
||||||
#include "common/timing.h"
|
#include "common/timing.h"
|
||||||
#include "selfdrive/ui/qt/util.h"
|
#include "selfdrive/ui/qt/util.h"
|
||||||
#ifdef ENABLE_MAPS
|
|
||||||
#include "selfdrive/ui/qt/maps/map_helpers.h"
|
|
||||||
#include "selfdrive/ui/qt/maps/map_panel.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity, const int angle = 0) {
|
static void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity, const int angle = 0) {
|
||||||
p.setRenderHint(QPainter::Antialiasing);
|
p.setRenderHint(QPainter::Antialiasing);
|
||||||
@@ -60,16 +56,6 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->
|
|||||||
split->setSpacing(0);
|
split->setSpacing(0);
|
||||||
split->addWidget(nvg);
|
split->addWidget(nvg);
|
||||||
|
|
||||||
if (getenv("DUAL_CAMERA_VIEW")) {
|
|
||||||
CameraWidget *arCam = new CameraWidget("camerad", VISION_STREAM_ROAD, true, this);
|
|
||||||
split->insertWidget(0, arCam);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getenv("MAP_RENDER_VIEW")) {
|
|
||||||
CameraWidget *map_render = new CameraWidget("navd", VISION_STREAM_MAP, false, this);
|
|
||||||
split->insertWidget(0, map_render);
|
|
||||||
}
|
|
||||||
|
|
||||||
stacked_layout->addWidget(split_wrapper);
|
stacked_layout->addWidget(split_wrapper);
|
||||||
|
|
||||||
alerts = new OnroadAlerts(this);
|
alerts = new OnroadAlerts(this);
|
||||||
@@ -82,7 +68,6 @@ OnroadWindow::OnroadWindow(QWidget *parent) : QWidget(parent), scene(uiState()->
|
|||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
|
QObject::connect(uiState(), &UIState::uiUpdate, this, &OnroadWindow::updateState);
|
||||||
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
|
QObject::connect(uiState(), &UIState::offroadTransition, this, &OnroadWindow::offroadTransition);
|
||||||
QObject::connect(uiState(), &UIState::primeChanged, this, &OnroadWindow::primeChanged);
|
|
||||||
|
|
||||||
QObject::connect(&clickTimer, &QTimer::timeout, this, [this]() {
|
QObject::connect(&clickTimer, &QTimer::timeout, this, [this]() {
|
||||||
clickTimer.stop();
|
clickTimer.stop();
|
||||||
@@ -99,12 +84,6 @@ void OnroadWindow::updateState(const UIState &s) {
|
|||||||
Alert alert = Alert::get(*(s.sm), s.scene.started_frame);
|
Alert alert = Alert::get(*(s.sm), s.scene.started_frame);
|
||||||
alerts->updateAlert(alert);
|
alerts->updateAlert(alert);
|
||||||
|
|
||||||
if (s.scene.map_on_left || scene.full_map) {
|
|
||||||
split->setDirection(QBoxLayout::LeftToRight);
|
|
||||||
} else {
|
|
||||||
split->setDirection(QBoxLayout::RightToLeft);
|
|
||||||
}
|
|
||||||
|
|
||||||
nvg->updateState(s);
|
nvg->updateState(s);
|
||||||
|
|
||||||
QColor bgColor = bg_colors[s.status];
|
QColor bgColor = bg_colors[s.status];
|
||||||
@@ -194,19 +173,6 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) {
|
|||||||
// widgetClicked = true;
|
// widgetClicked = true;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
#ifdef ENABLE_MAPS
|
|
||||||
if (map != nullptr && !widgetClicked) {
|
|
||||||
// Switch between map and sidebar when using navigate on openpilot
|
|
||||||
bool sidebarVisible = geometry().x() > 0;
|
|
||||||
bool show_map = uiState()->scene.navigate_on_openpilot ? sidebarVisible : !sidebarVisible;
|
|
||||||
map->setVisible(show_map && !map->isVisible());
|
|
||||||
if (scene.big_map) {
|
|
||||||
map->setFixedWidth(width());
|
|
||||||
} else {
|
|
||||||
map->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// propagation event to parent(HomeWindow)
|
// propagation event to parent(HomeWindow)
|
||||||
if (!widgetClicked) {
|
if (!widgetClicked) {
|
||||||
QWidget::mousePressEvent(e);
|
QWidget::mousePressEvent(e);
|
||||||
@@ -214,39 +180,10 @@ void OnroadWindow::mousePressEvent(QMouseEvent* e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OnroadWindow::offroadTransition(bool offroad) {
|
void OnroadWindow::offroadTransition(bool offroad) {
|
||||||
#ifdef ENABLE_MAPS
|
|
||||||
if (!offroad) {
|
|
||||||
if (map == nullptr && (uiState()->hasPrime() || !MAPBOX_TOKEN.isEmpty())) {
|
|
||||||
auto m = new MapPanel(get_mapbox_settings());
|
|
||||||
map = m;
|
|
||||||
|
|
||||||
QObject::connect(m, &MapPanel::mapPanelRequested, this, &OnroadWindow::mapPanelRequested);
|
|
||||||
QObject::connect(nvg->map_settings_btn, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings);
|
|
||||||
QObject::connect(nvg->map_settings_btn_bottom, &MapSettingsButton::clicked, m, &MapPanel::toggleMapSettings);
|
|
||||||
nvg->map_settings_btn->setEnabled(true);
|
|
||||||
|
|
||||||
m->setFixedWidth(topWidget(this)->width() / 2 - UI_BORDER_SIZE);
|
|
||||||
split->insertWidget(0, m);
|
|
||||||
|
|
||||||
// hidden by default, made visible when navRoute is published
|
|
||||||
m->setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
alerts->updateAlert({});
|
alerts->updateAlert({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnroadWindow::primeChanged(bool prime) {
|
|
||||||
#ifdef ENABLE_MAPS
|
|
||||||
if (map && (!prime && MAPBOX_TOKEN.isEmpty())) {
|
|
||||||
nvg->map_settings_btn->setEnabled(false);
|
|
||||||
nvg->map_settings_btn->setVisible(false);
|
|
||||||
map->deleteLater();
|
|
||||||
map = nullptr;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnroadWindow::paintEvent(QPaintEvent *event) {
|
void OnroadWindow::paintEvent(QPaintEvent *event) {
|
||||||
QPainter p(this);
|
QPainter p(this);
|
||||||
@@ -448,100 +385,85 @@ void ExperimentalButton::changeMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ExperimentalButton::updateState(const UIState &s, bool leadInfo) {
|
void ExperimentalButton::updateState(const UIState &s, bool leadInfo) {
|
||||||
const auto cs = (*s.sm)["controlsState"].getControlsState();
|
// const auto cs = (*s.sm)["controlsState"].getControlsState();
|
||||||
bool eng = cs.getEngageable() || cs.getEnabled() || scene.always_on_lateral_active;
|
// bool eng = cs.getEngageable() || cs.getEnabled() || scene.always_on_lateral_active;
|
||||||
if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
|
// if ((cs.getExperimentalMode() != experimental_mode) || (eng != engageable)) {
|
||||||
engageable = eng;
|
// engageable = eng;
|
||||||
experimental_mode = cs.getExperimentalMode();
|
// experimental_mode = cs.getExperimentalMode();
|
||||||
update();
|
// update();
|
||||||
}
|
// }
|
||||||
|
|
||||||
// FrogPilot variables
|
// // FrogPilot variables
|
||||||
int randomEvent = scene.current_random_event;
|
// int randomEvent = scene.current_random_event;
|
||||||
|
|
||||||
rotatingWheel = scene.rotating_wheel;
|
// rotatingWheel = scene.rotating_wheel;
|
||||||
wheelIcon = scene.wheel_icon;
|
// wheelIcon = scene.wheel_icon;
|
||||||
wheelIconGif = 0;
|
// wheelIconGif = 0;
|
||||||
|
|
||||||
y_offset = leadInfo ? 10 : 0;
|
// y_offset = leadInfo ? 10 : 0;
|
||||||
|
|
||||||
if (randomEvent == 0 && gifLabel) {
|
// if (randomEvent == 0 && gifLabel) {
|
||||||
delete gifLabel;
|
// delete gifLabel;
|
||||||
gifLabel = nullptr;
|
// gifLabel = nullptr;
|
||||||
} else if (randomEvent == 1) {
|
// } else if (randomEvent == 1) {
|
||||||
static int rotationDegree = 0;
|
// static int rotationDegree = 0;
|
||||||
rotationDegree = (rotationDegree + 36) % 360;
|
// rotationDegree = (rotationDegree + 36) % 360;
|
||||||
steeringAngleDeg = rotationDegree;
|
// steeringAngleDeg = rotationDegree;
|
||||||
wheelIcon = 7;
|
// wheelIcon = 7;
|
||||||
update();
|
// update();
|
||||||
|
|
||||||
} else if (randomEvent == 2 || randomEvent == 3 || randomEvent == 4) {
|
// } else if (randomEvent == 2 || randomEvent == 3 || randomEvent == 4) {
|
||||||
if (!gifLabel) {
|
// if (!gifLabel) {
|
||||||
gifLabel = new QLabel(this);
|
// gifLabel = new QLabel(this);
|
||||||
QMovie *movie = wheelImagesGif[randomEvent - 1];
|
// QMovie *movie = wheelImagesGif[randomEvent - 1];
|
||||||
if (movie) {
|
// if (movie) {
|
||||||
gifLabel->setMovie(movie);
|
// gifLabel->setMovie(movie);
|
||||||
gifLabel->setFixedSize(img_size, img_size);
|
// gifLabel->setFixedSize(img_size, img_size);
|
||||||
gifLabel->move((width() - gifLabel->width()) / 2, (height() - gifLabel->height()) / 2 + y_offset);
|
// gifLabel->move((width() - gifLabel->width()) / 2, (height() - gifLabel->height()) / 2 + y_offset);
|
||||||
gifLabel->movie()->start();
|
// gifLabel->movie()->start();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
gifLabel->show();
|
// gifLabel->show();
|
||||||
wheelIconGif = randomEvent - 1;
|
// wheelIconGif = randomEvent - 1;
|
||||||
update();
|
// update();
|
||||||
|
|
||||||
} else if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
|
// } else if (rotatingWheel && steeringAngleDeg != scene.steering_angle_deg) {
|
||||||
steeringAngleDeg = scene.steering_angle_deg;
|
// steeringAngleDeg = scene.steering_angle_deg;
|
||||||
update();
|
// update();
|
||||||
steeringAngleDeg = scene.steering_angle_deg;
|
// steeringAngleDeg = scene.steering_angle_deg;
|
||||||
} else if (!rotatingWheel) {
|
// } else if (!rotatingWheel) {
|
||||||
steeringAngleDeg = 0;
|
// steeringAngleDeg = 0;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExperimentalButton::paintEvent(QPaintEvent *event) {
|
void ExperimentalButton::paintEvent(QPaintEvent *event) {
|
||||||
if (wheelIcon < 0) {
|
// if (wheelIcon < 0) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
QPainter p(this);
|
// QPainter p(this);
|
||||||
engage_img = wheelImages[wheelIcon];
|
// engage_img = wheelImages[wheelIcon];
|
||||||
QPixmap img = wheelIcon != 0 ? engage_img : (experimental_mode ? experimental_img : engage_img);
|
// QPixmap img = wheelIcon != 0 ? engage_img : (experimental_mode ? experimental_img : engage_img);
|
||||||
QMovie *gif = wheelImagesGif[wheelIconGif];
|
// QMovie *gif = wheelImagesGif[wheelIconGif];
|
||||||
|
|
||||||
QColor background_color = wheelIcon != 0 && !isDown() && engageable ?
|
// QColor background_color = wheelIcon != 0 && !isDown() && engageable ?
|
||||||
(scene.always_on_lateral_active ? QColor(10, 186, 181, 255) :
|
// (scene.always_on_lateral_active ? QColor(10, 186, 181, 255) :
|
||||||
(scene.conditional_status == 1 || scene.conditional_status == 3 || scene.conditional_status == 5 ? QColor(255, 246, 0, 255) :
|
// (scene.conditional_status == 1 || scene.conditional_status == 3 || scene.conditional_status == 5 ? QColor(255, 246, 0, 255) :
|
||||||
(experimental_mode ? QColor(218, 111, 37, 241) :
|
// (experimental_mode ? QColor(218, 111, 37, 241) :
|
||||||
(scene.traffic_mode_active ? QColor(201, 34, 49, 255) :
|
// (scene.traffic_mode_active ? QColor(201, 34, 49, 255) :
|
||||||
(scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166)))))) :
|
// (scene.navigate_on_openpilot ? QColor(49, 161, 238, 255) : QColor(0, 0, 0, 166)))))) :
|
||||||
QColor(0, 0, 0, 166);
|
// QColor(0, 0, 0, 166);
|
||||||
|
|
||||||
if (!(scene.map_open && scene.big_map)) {
|
// if (!(scene.map_open && scene.big_map)) {
|
||||||
if (wheelIconGif != 0) {
|
// if (wheelIconGif != 0) {
|
||||||
drawIconGif(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), *gif, background_color, 1.0);
|
// drawIconGif(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), *gif, background_color, 1.0);
|
||||||
} else {
|
// } else {
|
||||||
drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
|
// drawIcon(p, QPoint(btn_size / 2, btn_size / 2 + y_offset), img, background_color, (isDown() || !engageable) ? 0.6 : 1.0, steeringAngleDeg);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MapSettingsButton
|
|
||||||
MapSettingsButton::MapSettingsButton(QWidget *parent) : QPushButton(parent) {
|
|
||||||
setFixedSize(btn_size, btn_size + 20);
|
|
||||||
settings_img = loadPixmap("../assets/navigation/icon_directions_outlined.svg", {img_size, img_size});
|
|
||||||
|
|
||||||
// hidden by default, made visible if map is created (has prime or mapbox token)
|
|
||||||
setVisible(false);
|
|
||||||
setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MapSettingsButton::paintEvent(QPaintEvent *event) {
|
|
||||||
QPainter p(this);
|
|
||||||
drawIcon(p, QPoint(btn_size / 2, btn_size / 2), settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Window that shows camera view and variety of info drawn on top
|
// Window that shows camera view and variety of info drawn on top
|
||||||
AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent), scene(uiState()->scene) {
|
AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* parent) : fps_filter(UI_FREQ, 3, 1. / UI_FREQ), CameraWidget("camerad", type, true, parent), scene(uiState()->scene) {
|
||||||
@@ -572,9 +494,6 @@ AnnotatedCameraWidget::AnnotatedCameraWidget(VisionStreamType type, QWidget* par
|
|||||||
main_layout->addLayout(top_right_layout, 0);
|
main_layout->addLayout(top_right_layout, 0);
|
||||||
main_layout->setAlignment(top_right_layout, Qt::AlignTop | Qt::AlignRight);
|
main_layout->setAlignment(top_right_layout, Qt::AlignTop | Qt::AlignRight);
|
||||||
|
|
||||||
map_settings_btn = new MapSettingsButton(this);
|
|
||||||
main_layout->addWidget(map_settings_btn, 0, Qt::AlignBottom | Qt::AlignRight);
|
|
||||||
|
|
||||||
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5});
|
dm_img = loadPixmap("../assets/img_driver_face.png", {img_size + 5, img_size + 5});
|
||||||
|
|
||||||
// Initialize FrogPilot widgets
|
// Initialize FrogPilot widgets
|
||||||
@@ -586,10 +505,8 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
|
|||||||
const SubMaster &sm = *(s.sm);
|
const SubMaster &sm = *(s.sm);
|
||||||
|
|
||||||
const bool cs_alive = sm.alive("controlsState");
|
const bool cs_alive = sm.alive("controlsState");
|
||||||
const bool nav_alive = sm.alive("navInstruction") && sm["navInstruction"].getValid();
|
|
||||||
const auto cs = sm["controlsState"].getControlsState();
|
const auto cs = sm["controlsState"].getControlsState();
|
||||||
const auto car_state = sm["carState"].getCarState();
|
const auto car_state = sm["carState"].getCarState();
|
||||||
const auto nav_instruction = sm["navInstruction"].getNavInstruction();
|
|
||||||
|
|
||||||
// Handle older routes where vCruiseCluster is not set
|
// Handle older routes where vCruiseCluster is not set
|
||||||
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
|
float v_cruise = cs.getVCruiseCluster() == 0.0 ? cs.getVCruise() : cs.getVCruiseCluster();
|
||||||
@@ -605,19 +522,19 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
|
|||||||
speed = cs_alive ? std::max<float>(0.0, v_ego) : 0.0;
|
speed = cs_alive ? std::max<float>(0.0, v_ego) : 0.0;
|
||||||
speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH;
|
speed *= s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH;
|
||||||
|
|
||||||
auto speed_limit_sign = nav_instruction.getSpeedLimitSign();
|
// auto speed_limit_sign = nav_instruction.getSpeedLimitSign();
|
||||||
speedLimit = slcOverridden ? scene.speed_limit_overridden_speed : speedLimitController ? scene.speed_limit : nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
|
// speedLimit = slcOverridden ? scene.speed_limit_overridden_speed : speedLimitController ? scene.speed_limit : nav_alive ? nav_instruction.getSpeedLimit() : 0.0;
|
||||||
speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
|
// speedLimit *= (s.scene.is_metric ? MS_TO_KPH : MS_TO_MPH);
|
||||||
if (speedLimitController && !slcOverridden) {
|
// if (speedLimitController && !slcOverridden) {
|
||||||
speedLimit = speedLimit - (showSLCOffset ? slcSpeedLimitOffset : 0);
|
// speedLimit = speedLimit - (showSLCOffset ? slcSpeedLimitOffset : 0);
|
||||||
}
|
// }
|
||||||
|
|
||||||
has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || (speedLimitController && !useViennaSLCSign);
|
// has_us_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::MUTCD) || (speedLimitController && !useViennaSLCSign);
|
||||||
has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign);
|
// has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign);
|
||||||
is_metric = s.scene.is_metric;
|
// is_metric = s.scene.is_metric;
|
||||||
speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
|
// speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph");
|
||||||
hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals != 0 && (turnSignalLeft || turnSignalRight) || bigMapOpen);
|
// hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals != 0 && (turnSignalLeft || turnSignalRight));
|
||||||
status = s.status;
|
// status = s.status;
|
||||||
|
|
||||||
// update engageability/experimental mode button
|
// update engageability/experimental mode button
|
||||||
experimental_btn->updateState(s, leadInfo);
|
experimental_btn->updateState(s, leadInfo);
|
||||||
@@ -631,12 +548,6 @@ void AnnotatedCameraWidget::updateState(const UIState &s) {
|
|||||||
// DM icon transition
|
// DM icon transition
|
||||||
dm_fade_state = std::clamp(dm_fade_state+0.2*(0.5-dmActive), 0.0, 1.0);
|
dm_fade_state = std::clamp(dm_fade_state+0.2*(0.5-dmActive), 0.0, 1.0);
|
||||||
|
|
||||||
// hide map settings button for alerts and flip for right hand DM
|
|
||||||
if (map_settings_btn->isEnabled()) {
|
|
||||||
map_settings_btn->setVisible(!hideBottomIcons && compass && !scene.hide_map_icon);
|
|
||||||
main_layout->setAlignment(map_settings_btn, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignTop);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFrogPilotWidgets();
|
updateFrogPilotWidgets();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,7 +688,7 @@ void AnnotatedCameraWidget::drawHud(QPainter &p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// current speed
|
// current speed
|
||||||
if (!(scene.hide_speed || bigMapOpen)) {
|
if (!(scene.hide_speed)) {
|
||||||
// CLEARPILOT changes to 120 from ~176
|
// CLEARPILOT changes to 120 from ~176
|
||||||
// Maybe we want to hide this?
|
// Maybe we want to hide this?
|
||||||
p.setFont(InterFont(140, QFont::Bold));
|
p.setFont(InterFont(140, QFont::Bold));
|
||||||
@@ -1193,9 +1104,6 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() {
|
|||||||
compass_img = new Compass(this);
|
compass_img = new Compass(this);
|
||||||
// bottom_layout->addWidget(compass_img);
|
// bottom_layout->addWidget(compass_img);
|
||||||
|
|
||||||
map_settings_btn_bottom = new MapSettingsButton(this);
|
|
||||||
bottom_layout->addWidget(map_settings_btn_bottom);
|
|
||||||
|
|
||||||
main_layout->addLayout(bottom_layout);
|
main_layout->addLayout(bottom_layout);
|
||||||
|
|
||||||
themeConfiguration = {
|
themeConfiguration = {
|
||||||
@@ -1293,9 +1201,6 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
|
|||||||
obstacleDistance = scene.obstacle_distance;
|
obstacleDistance = scene.obstacle_distance;
|
||||||
obstacleDistanceStock = scene.obstacle_distance_stock;
|
obstacleDistanceStock = scene.obstacle_distance_stock;
|
||||||
|
|
||||||
mapOpen = scene.map_open;
|
|
||||||
bigMapOpen = mapOpen && scene.big_map;
|
|
||||||
|
|
||||||
onroadDistanceButton = scene.onroad_distance_button;
|
onroadDistanceButton = scene.onroad_distance_button;
|
||||||
|
|
||||||
roadNameUI = scene.road_name_ui;
|
roadNameUI = scene.road_name_ui;
|
||||||
@@ -1350,7 +1255,7 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
||||||
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI) && !bigMapOpen) {
|
if ((showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI)) {
|
||||||
drawStatusBar(p);
|
drawStatusBar(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1363,7 +1268,7 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
|||||||
// animationTimer->stop();
|
// animationTimer->stop();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (leadInfo && !bigMapOpen) {
|
if (leadInfo) {
|
||||||
drawLeadInfo(p);
|
drawLeadInfo(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1391,12 +1296,6 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) {
|
|||||||
// pedal_icons->updateState();
|
// pedal_icons->updateState();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled());
|
|
||||||
if (map_settings_btn_bottom->isEnabled()) {
|
|
||||||
map_settings_btn_bottom->setVisible(!hideBottomIcons && !compass && !scene.hide_map_icon);
|
|
||||||
bottom_layout->setAlignment(map_settings_btn_bottom, rightHandDM ? Qt::AlignLeft : Qt::AlignRight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// recorder_btn->setVisible(scene.screen_recorder && !mapOpen);
|
// recorder_btn->setVisible(scene.screen_recorder && !mapOpen);
|
||||||
recorder_btn->setVisible(false);
|
recorder_btn->setVisible(false);
|
||||||
}
|
}
|
||||||
@@ -1581,7 +1480,7 @@ void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) {
|
|||||||
constexpr int maxAccelDuration = 5000;
|
constexpr int maxAccelDuration = 5000;
|
||||||
|
|
||||||
QString accelerationUnit = tr(" m/s²");
|
QString accelerationUnit = tr(" m/s²");
|
||||||
leadDistanceUnit = tr(mapOpen ? "m" : "meters");
|
leadDistanceUnit = tr("meters");
|
||||||
leadSpeedUnit = tr("m/s");
|
leadSpeedUnit = tr("m/s");
|
||||||
|
|
||||||
float accelerationConversion = 1.0f;
|
float accelerationConversion = 1.0f;
|
||||||
@@ -1635,15 +1534,15 @@ void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) {
|
|||||||
.arg(accelerationUnit);
|
.arg(accelerationUnit);
|
||||||
|
|
||||||
QString maxAccSuffix;
|
QString maxAccSuffix;
|
||||||
if (!mapOpen) {
|
|
||||||
maxAccSuffix = tr(" - Max: %1%2")
|
maxAccSuffix = tr(" - Max: %1%2")
|
||||||
.arg(maxAcceleration * accelerationConversion, 0, 'f', 2)
|
.arg(maxAcceleration * accelerationConversion, 0, 'f', 2)
|
||||||
.arg(accelerationUnit);
|
.arg(accelerationUnit);
|
||||||
}
|
|
||||||
|
|
||||||
QString obstacleText = createText(mapOpen ? tr(" | Obstacle: ") : tr(" | Obstacle Factor: "), obstacleDistance);
|
|
||||||
QString stopText = createText(mapOpen ? tr(" - Stop: ") : tr(" - Stop Factor: "), scene.stopped_equivalence);
|
QString obstacleText = createText(tr(" | Obstacle Factor: "), obstacleDistance);
|
||||||
QString followText = " = " + createText(mapOpen ? tr("Follow: ") : tr("Follow Distance: "), scene.desired_follow);
|
QString stopText = createText(tr(" - Stop Factor: "), scene.stopped_equivalence);
|
||||||
|
QString followText = " = " + createText(tr("Follow Distance: "), scene.desired_follow);
|
||||||
|
|
||||||
auto createDiffText = [&](const double data, const double stockData) {
|
auto createDiffText = [&](const double data, const double stockData) {
|
||||||
double difference = std::round((data - stockData) * distanceConversion);
|
double difference = std::round((data - stockData) * distanceConversion);
|
||||||
@@ -1784,21 +1683,21 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
|||||||
{4, tr("Experimental Mode manually activated")},
|
{4, tr("Experimental Mode manually activated")},
|
||||||
{5, tr("Conditional Experimental overridden")},
|
{5, tr("Conditional Experimental overridden")},
|
||||||
{6, tr("Experimental Mode manually activated")},
|
{6, tr("Experimental Mode manually activated")},
|
||||||
{7, tr("Experimental Mode activated for") + (mapOpen ? tr(" intersection") : tr(" upcoming intersection"))},
|
{7, tr("Experimental Mode activated for") + (tr(" upcoming intersection"))},
|
||||||
{8, tr("Experimental Mode activated for") + (mapOpen ? tr(" turn") : tr(" upcoming turn"))},
|
{8, tr("Experimental Mode activated for") + (tr(" upcoming turn"))},
|
||||||
{9, tr("Experimental Mode activated due to") + (mapOpen ? tr(" SLC") : tr(" no speed limit set"))},
|
{9, tr("Experimental Mode activated due to") + (tr(" no speed limit set"))},
|
||||||
{10, tr("Experimental Mode activated due to") + (mapOpen ? tr(" speed") : tr(" speed being less than ") + QString::number(scene.conditional_speed_lead) + (is_metric ? tr(" kph") : tr(" mph")))},
|
{10, tr("Experimental Mode activated due to") + (tr(" speed being less than ") + QString::number(scene.conditional_speed_lead) + (is_metric ? tr(" kph") : tr(" mph")))},
|
||||||
{11, tr("Experimental Mode activated due to") + (mapOpen ? tr(" speed") : tr(" speed being less than ") + QString::number(scene.conditional_speed) + (is_metric ? tr(" kph") : tr(" mph")))},
|
{11, tr("Experimental Mode activated due to") + (tr(" speed being less than ") + QString::number(scene.conditional_speed) + (is_metric ? tr(" kph") : tr(" mph")))},
|
||||||
{12, tr("Experimental Mode activated for slower lead")},
|
{12, tr("Experimental Mode activated for slower lead")},
|
||||||
{13, tr("Experimental Mode activated for turn") + (mapOpen ? "" : tr(" / lane change"))},
|
{13, tr("Experimental Mode activated for turn") + (tr(" / lane change"))},
|
||||||
{14, tr("Experimental Mode activated for curve")},
|
{14, tr("Experimental Mode activated for curve")},
|
||||||
{15, tr("Experimental Mode activated for stop") + (mapOpen ? "" : tr(" sign / stop light"))},
|
{15, tr("Experimental Mode activated for stop") + (tr(" sign / stop light"))},
|
||||||
};
|
};
|
||||||
|
|
||||||
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
|
QString roadName = roadNameUI ? QString::fromStdString(paramsMemory.get("RoadName")) : QString();
|
||||||
|
|
||||||
if (alwaysOnLateralActive && showAlwaysOnLateralStatusBar) {
|
if (alwaysOnLateralActive && showAlwaysOnLateralStatusBar) {
|
||||||
newStatus = tr("Always On Lateral active") + (mapOpen ? "" : tr(". Press the \"Cruise Control\" button to disable"));
|
newStatus = tr("Always On Lateral active") + (tr(". Press the \"Cruise Control\" button to disable"));
|
||||||
} else if (showConditionalExperimentalStatusBar) {
|
} else if (showConditionalExperimentalStatusBar) {
|
||||||
newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0];
|
newStatus = conditionalStatusMap[status != STATUS_DISENGAGED ? conditionalStatus : 0];
|
||||||
}
|
}
|
||||||
@@ -1807,7 +1706,7 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) {
|
|||||||
QString lkasSuffix = tr(". Double press the \"LKAS\" button to revert");
|
QString lkasSuffix = tr(". Double press the \"LKAS\" button to revert");
|
||||||
QString screenSuffix = tr(". Double tap the screen to revert");
|
QString screenSuffix = tr(". Double tap the screen to revert");
|
||||||
|
|
||||||
if (!alwaysOnLateralActive && !mapOpen && status != STATUS_DISENGAGED && !newStatus.isEmpty()) {
|
if (!alwaysOnLateralActive && status != STATUS_DISENGAGED && !newStatus.isEmpty()) {
|
||||||
if (conditionalStatus == 1 || conditionalStatus == 2) {
|
if (conditionalStatus == 1 || conditionalStatus == 2) {
|
||||||
newStatus += distanceSuffix;
|
newStatus += distanceSuffix;
|
||||||
} else if (conditionalStatus == 3 || conditionalStatus == 4) {
|
} else if (conditionalStatus == 3 || conditionalStatus == 4) {
|
||||||
|
|||||||
@@ -127,19 +127,6 @@ private:
|
|||||||
int y_offset;
|
int y_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class MapSettingsButton : public QPushButton {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MapSettingsButton(QWidget *parent = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
QPixmap settings_img;
|
|
||||||
};
|
|
||||||
|
|
||||||
class PedalIcons : public QWidget {
|
class PedalIcons : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
@@ -170,9 +157,6 @@ public:
|
|||||||
void updateState(const UIState &s);
|
void updateState(const UIState &s);
|
||||||
void updateLaneEdgeColor(QColor &bgColor);
|
void updateLaneEdgeColor(QColor &bgColor);
|
||||||
|
|
||||||
MapSettingsButton *map_settings_btn;
|
|
||||||
MapSettingsButton *map_settings_btn_bottom;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
|
void drawText(QPainter &p, int x, int y, const QString &text, int alpha = 255);
|
||||||
|
|
||||||
@@ -221,13 +205,11 @@ private:
|
|||||||
QHBoxLayout *bottom_layout;
|
QHBoxLayout *bottom_layout;
|
||||||
|
|
||||||
bool alwaysOnLateralActive;
|
bool alwaysOnLateralActive;
|
||||||
bool bigMapOpen;
|
|
||||||
bool blindSpotLeft;
|
bool blindSpotLeft;
|
||||||
bool blindSpotRight;
|
bool blindSpotRight;
|
||||||
bool compass;
|
bool compass;
|
||||||
bool experimentalMode;
|
bool experimentalMode;
|
||||||
bool leadInfo;
|
bool leadInfo;
|
||||||
bool mapOpen;
|
|
||||||
bool onroadDistanceButton;
|
bool onroadDistanceButton;
|
||||||
bool roadNameUI;
|
bool roadNameUI;
|
||||||
bool showAlwaysOnLateralStatusBar;
|
bool showAlwaysOnLateralStatusBar;
|
||||||
@@ -297,11 +279,8 @@ class OnroadWindow : public QWidget {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
OnroadWindow(QWidget* parent = 0);
|
OnroadWindow(QWidget* parent = 0);
|
||||||
bool isMapVisible() const { return map && map->isVisible(); }
|
|
||||||
void showMapPanel(bool show) { if (map) map->setVisible(show); }
|
|
||||||
|
|
||||||
signals:
|
// signals:
|
||||||
void mapPanelRequested();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void paintEvent(QPaintEvent *event);
|
void paintEvent(QPaintEvent *event);
|
||||||
@@ -322,6 +301,5 @@ private:
|
|||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void offroadTransition(bool offroad);
|
void offroadTransition(bool offroad);
|
||||||
void primeChanged(bool prime);
|
|
||||||
void updateState(const UIState &s);
|
void updateState(const UIState &s);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,283 +0,0 @@
|
|||||||
#include "selfdrive/ui/qt/widgets/prime.h"
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QJsonDocument>
|
|
||||||
#include <QJsonObject>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QStackedWidget>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include <QrCode.hpp>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/request_repeater.h"
|
|
||||||
#include "selfdrive/ui/qt/util.h"
|
|
||||||
#include "selfdrive/ui/qt/qt_window.h"
|
|
||||||
#include "selfdrive/ui/qt/widgets/wifi.h"
|
|
||||||
|
|
||||||
using qrcodegen::QrCode;
|
|
||||||
|
|
||||||
PairingQRWidget::PairingQRWidget(QWidget* parent) : QWidget(parent) {
|
|
||||||
timer = new QTimer(this);
|
|
||||||
connect(timer, &QTimer::timeout, this, &PairingQRWidget::refresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PairingQRWidget::showEvent(QShowEvent *event) {
|
|
||||||
refresh();
|
|
||||||
timer->start(5 * 60 * 1000);
|
|
||||||
device()->setOffroadBrightness(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PairingQRWidget::hideEvent(QHideEvent *event) {
|
|
||||||
timer->stop();
|
|
||||||
device()->setOffroadBrightness(BACKLIGHT_OFFROAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PairingQRWidget::refresh() {
|
|
||||||
QString pairToken = CommaApi::create_jwt({{"pair", true}});
|
|
||||||
QString qrString = "https://connect.comma.ai/?pair=" + pairToken;
|
|
||||||
this->updateQrCode(qrString);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PairingQRWidget::updateQrCode(const QString &text) {
|
|
||||||
QrCode qr = QrCode::encodeText(text.toUtf8().data(), QrCode::Ecc::LOW);
|
|
||||||
qint32 sz = qr.getSize();
|
|
||||||
QImage im(sz, sz, QImage::Format_RGB32);
|
|
||||||
|
|
||||||
QRgb black = qRgb(0, 0, 0);
|
|
||||||
QRgb white = qRgb(255, 255, 255);
|
|
||||||
for (int y = 0; y < sz; y++) {
|
|
||||||
for (int x = 0; x < sz; x++) {
|
|
||||||
im.setPixel(x, y, qr.getModule(x, y) ? black : white);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Integer division to prevent anti-aliasing
|
|
||||||
int final_sz = ((width() / sz) - 1) * sz;
|
|
||||||
img = QPixmap::fromImage(im.scaled(final_sz, final_sz, Qt::KeepAspectRatio), Qt::MonoOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PairingQRWidget::paintEvent(QPaintEvent *e) {
|
|
||||||
QPainter p(this);
|
|
||||||
p.fillRect(rect(), Qt::white);
|
|
||||||
|
|
||||||
QSize s = (size() - img.size()) / 2;
|
|
||||||
p.drawPixmap(s.width(), s.height(), img);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PairingPopup::PairingPopup(QWidget *parent) : DialogBase(parent) {
|
|
||||||
QHBoxLayout *hlayout = new QHBoxLayout(this);
|
|
||||||
hlayout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
hlayout->setSpacing(0);
|
|
||||||
|
|
||||||
setStyleSheet("PairingPopup { background-color: #E0E0E0; }");
|
|
||||||
|
|
||||||
// text
|
|
||||||
QVBoxLayout *vlayout = new QVBoxLayout();
|
|
||||||
vlayout->setContentsMargins(85, 70, 50, 70);
|
|
||||||
vlayout->setSpacing(50);
|
|
||||||
hlayout->addLayout(vlayout, 1);
|
|
||||||
{
|
|
||||||
QPushButton *close = new QPushButton(QIcon(":/icons/close.svg"), "", this);
|
|
||||||
close->setIconSize(QSize(80, 80));
|
|
||||||
close->setStyleSheet("border: none;");
|
|
||||||
vlayout->addWidget(close, 0, Qt::AlignLeft);
|
|
||||||
QObject::connect(close, &QPushButton::clicked, this, &QDialog::reject);
|
|
||||||
|
|
||||||
vlayout->addSpacing(30);
|
|
||||||
|
|
||||||
QLabel *title = new QLabel(tr("Pair your device to your comma account"), this);
|
|
||||||
title->setStyleSheet("font-size: 75px; color: black;");
|
|
||||||
title->setWordWrap(true);
|
|
||||||
vlayout->addWidget(title);
|
|
||||||
|
|
||||||
QLabel *instructions = new QLabel(QString(R"(
|
|
||||||
<ol type='1' style='margin-left: 15px;'>
|
|
||||||
<li style='margin-bottom: 50px;'>%1</li>
|
|
||||||
<li style='margin-bottom: 50px;'>%2</li>
|
|
||||||
<li style='margin-bottom: 50px;'>%3</li>
|
|
||||||
</ol>
|
|
||||||
)").arg(tr("Go to https://connect.comma.ai on your phone"))
|
|
||||||
.arg(tr("Click \"add new device\" and scan the QR code on the right"))
|
|
||||||
.arg(tr("Bookmark connect.comma.ai to your home screen to use it like an app")), this);
|
|
||||||
|
|
||||||
instructions->setStyleSheet("font-size: 47px; font-weight: bold; color: black;");
|
|
||||||
instructions->setWordWrap(true);
|
|
||||||
vlayout->addWidget(instructions);
|
|
||||||
|
|
||||||
vlayout->addStretch();
|
|
||||||
}
|
|
||||||
|
|
||||||
// QR code
|
|
||||||
PairingQRWidget *qr = new PairingQRWidget(this);
|
|
||||||
hlayout->addWidget(qr, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PrimeUserWidget::PrimeUserWidget(QWidget *parent) : QFrame(parent) {
|
|
||||||
setObjectName("primeWidget");
|
|
||||||
QVBoxLayout *mainLayout = new QVBoxLayout(this);
|
|
||||||
mainLayout->setContentsMargins(56, 40, 56, 40);
|
|
||||||
mainLayout->setSpacing(20);
|
|
||||||
|
|
||||||
QLabel *subscribed = new QLabel(tr("✓ SUBSCRIBED"));
|
|
||||||
subscribed->setStyleSheet("font-size: 41px; font-weight: bold; color: #86FF4E;");
|
|
||||||
mainLayout->addWidget(subscribed);
|
|
||||||
|
|
||||||
QLabel *commaPrime = new QLabel(tr("comma prime"));
|
|
||||||
commaPrime->setStyleSheet("font-size: 75px; font-weight: bold;");
|
|
||||||
mainLayout->addWidget(commaPrime);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PrimeAdWidget::PrimeAdWidget(QWidget* parent) : QFrame(parent) {
|
|
||||||
QVBoxLayout *main_layout = new QVBoxLayout(this);
|
|
||||||
main_layout->setContentsMargins(80, 90, 80, 60);
|
|
||||||
main_layout->setSpacing(0);
|
|
||||||
|
|
||||||
QLabel *upgrade = new QLabel(tr("Upgrade Now"));
|
|
||||||
upgrade->setStyleSheet("font-size: 75px; font-weight: bold;");
|
|
||||||
main_layout->addWidget(upgrade, 0, Qt::AlignTop);
|
|
||||||
main_layout->addSpacing(50);
|
|
||||||
|
|
||||||
QLabel *description = new QLabel(tr("Become a comma prime member at connect.comma.ai"));
|
|
||||||
description->setStyleSheet("font-size: 56px; font-weight: light; color: white;");
|
|
||||||
description->setWordWrap(true);
|
|
||||||
main_layout->addWidget(description, 0, Qt::AlignTop);
|
|
||||||
|
|
||||||
main_layout->addStretch();
|
|
||||||
|
|
||||||
QLabel *features = new QLabel(tr("PRIME FEATURES:"));
|
|
||||||
features->setStyleSheet("font-size: 41px; font-weight: bold; color: #E5E5E5;");
|
|
||||||
main_layout->addWidget(features, 0, Qt::AlignBottom);
|
|
||||||
main_layout->addSpacing(30);
|
|
||||||
|
|
||||||
QVector<QString> bullets = {tr("Remote access"), tr("24/7 LTE connectivity"), tr("1 year of drive storage"), tr("Turn-by-turn navigation")};
|
|
||||||
for (auto &b : bullets) {
|
|
||||||
const QString check = "<b><font color='#465BEA'>✓</font></b> ";
|
|
||||||
QLabel *l = new QLabel(check + b);
|
|
||||||
l->setAlignment(Qt::AlignLeft);
|
|
||||||
l->setStyleSheet("font-size: 50px; margin-bottom: 15px;");
|
|
||||||
main_layout->addWidget(l, 0, Qt::AlignBottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
setStyleSheet(R"(
|
|
||||||
PrimeAdWidget {
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: #333333;
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SetupWidget::SetupWidget(QWidget* parent) : QFrame(parent) {
|
|
||||||
mainLayout = new QStackedWidget;
|
|
||||||
|
|
||||||
// Unpaired, registration prompt layout
|
|
||||||
|
|
||||||
QFrame* finishRegistration = new QFrame;
|
|
||||||
finishRegistration->setObjectName("primeWidget");
|
|
||||||
QVBoxLayout* finishRegistationLayout = new QVBoxLayout(finishRegistration);
|
|
||||||
finishRegistationLayout->setSpacing(38);
|
|
||||||
finishRegistationLayout->setContentsMargins(64, 48, 64, 48);
|
|
||||||
|
|
||||||
QLabel* registrationTitle = new QLabel(tr("Finish Setup"));
|
|
||||||
registrationTitle->setStyleSheet("font-size: 75px; font-weight: bold;");
|
|
||||||
finishRegistationLayout->addWidget(registrationTitle);
|
|
||||||
|
|
||||||
QLabel* registrationDescription = new QLabel(tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer."));
|
|
||||||
registrationDescription->setWordWrap(true);
|
|
||||||
registrationDescription->setStyleSheet("font-size: 50px; font-weight: light;");
|
|
||||||
finishRegistationLayout->addWidget(registrationDescription);
|
|
||||||
|
|
||||||
finishRegistationLayout->addStretch();
|
|
||||||
|
|
||||||
QPushButton* pair = new QPushButton(tr("Pair device"));
|
|
||||||
pair->setStyleSheet(R"(
|
|
||||||
QPushButton {
|
|
||||||
font-size: 55px;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: #465BEA;
|
|
||||||
padding: 64px;
|
|
||||||
}
|
|
||||||
QPushButton:pressed {
|
|
||||||
background-color: #3049F4;
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
finishRegistationLayout->addWidget(pair);
|
|
||||||
|
|
||||||
popup = new PairingPopup(this);
|
|
||||||
QObject::connect(pair, &QPushButton::clicked, popup, &PairingPopup::exec);
|
|
||||||
|
|
||||||
mainLayout->addWidget(finishRegistration);
|
|
||||||
|
|
||||||
// build stacked layout
|
|
||||||
QVBoxLayout *outer_layout = new QVBoxLayout(this);
|
|
||||||
outer_layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
outer_layout->addWidget(mainLayout);
|
|
||||||
|
|
||||||
QWidget *content = new QWidget;
|
|
||||||
QVBoxLayout *content_layout = new QVBoxLayout(content);
|
|
||||||
content_layout->setContentsMargins(0, 0, 0, 0);
|
|
||||||
content_layout->setSpacing(30);
|
|
||||||
|
|
||||||
primeUser = new PrimeUserWidget;
|
|
||||||
content_layout->addWidget(primeUser);
|
|
||||||
|
|
||||||
WiFiPromptWidget *wifi_prompt = new WiFiPromptWidget;
|
|
||||||
QObject::connect(wifi_prompt, &WiFiPromptWidget::openSettings, this, &SetupWidget::openSettings);
|
|
||||||
content_layout->addWidget(wifi_prompt);
|
|
||||||
content_layout->addStretch();
|
|
||||||
|
|
||||||
mainLayout->addWidget(content);
|
|
||||||
|
|
||||||
primeUser->setVisible(uiState()->primeType());
|
|
||||||
mainLayout->setCurrentIndex(1);
|
|
||||||
|
|
||||||
setStyleSheet(R"(
|
|
||||||
#primeWidget {
|
|
||||||
border-radius: 10px;
|
|
||||||
background-color: #333333;
|
|
||||||
}
|
|
||||||
)");
|
|
||||||
|
|
||||||
// Retain size while hidden
|
|
||||||
QSizePolicy sp_retain = sizePolicy();
|
|
||||||
sp_retain.setRetainSizeWhenHidden(true);
|
|
||||||
setSizePolicy(sp_retain);
|
|
||||||
|
|
||||||
// set up API requests
|
|
||||||
if (auto dongleId = getDongleId()) {
|
|
||||||
QString url = CommaApi::BASE_URL + "/v1.1/devices/" + *dongleId + "/";
|
|
||||||
RequestRepeater* repeater = new RequestRepeater(this, url, "ApiCache_Device", 5);
|
|
||||||
|
|
||||||
QObject::connect(repeater, &RequestRepeater::requestDone, this, &SetupWidget::replyFinished);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetupWidget::replyFinished(const QString &response, bool success) {
|
|
||||||
if (!success) return;
|
|
||||||
|
|
||||||
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
|
|
||||||
if (doc.isNull()) {
|
|
||||||
qDebug() << "JSON Parse failed on getting pairing and prime status";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QJsonObject json = doc.object();
|
|
||||||
PrimeType prime_type = static_cast<PrimeType>(json["prime_type"].toInt());
|
|
||||||
uiState()->setPrimeType(prime_type);
|
|
||||||
|
|
||||||
if (!json["is_paired"].toBool()) {
|
|
||||||
mainLayout->setCurrentIndex(0);
|
|
||||||
} else {
|
|
||||||
popup->reject();
|
|
||||||
|
|
||||||
primeUser->setVisible(prime_type);
|
|
||||||
mainLayout->setCurrentIndex(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QStackedWidget>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "selfdrive/ui/qt/widgets/input.h"
|
|
||||||
|
|
||||||
// pairing QR code
|
|
||||||
class PairingQRWidget : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit PairingQRWidget(QWidget* parent = 0);
|
|
||||||
void paintEvent(QPaintEvent*) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QPixmap img;
|
|
||||||
QTimer *timer;
|
|
||||||
void updateQrCode(const QString &text);
|
|
||||||
void showEvent(QShowEvent *event) override;
|
|
||||||
void hideEvent(QHideEvent *event) override;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// pairing popup widget
|
|
||||||
class PairingPopup : public DialogBase {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit PairingPopup(QWidget* parent);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// widget for paired users with prime
|
|
||||||
class PrimeUserWidget : public QFrame {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit PrimeUserWidget(QWidget* parent = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// widget for paired users without prime
|
|
||||||
class PrimeAdWidget : public QFrame {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit PrimeAdWidget(QWidget* parent = 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// container widget
|
|
||||||
class SetupWidget : public QFrame {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SetupWidget(QWidget* parent = 0);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void openSettings(int index = 0, const QString ¶m = "");
|
|
||||||
|
|
||||||
private:
|
|
||||||
PairingPopup *popup;
|
|
||||||
QStackedWidget *mainLayout;
|
|
||||||
PrimeUserWidget *primeUser;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void replyFinished(const QString &response, bool success);
|
|
||||||
};
|
|
||||||
@@ -74,13 +74,8 @@ void MainWindow::closeSettings() {
|
|||||||
main_layout->setCurrentWidget(homeWindow);
|
main_layout->setCurrentWidget(homeWindow);
|
||||||
|
|
||||||
if (uiState()->scene.started) {
|
if (uiState()->scene.started) {
|
||||||
// Map is always shown when using navigate on openpilot
|
|
||||||
if (uiState()->scene.navigate_on_openpilot) {
|
|
||||||
homeWindow->showMapPanel(true);
|
|
||||||
} else {
|
|
||||||
homeWindow->showSidebar(params.getBool("Sidebar"));
|
homeWindow->showSidebar(params.getBool("Sidebar"));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
|
bool MainWindow::eventFilter(QObject *obj, QEvent *event) {
|
||||||
|
|||||||
@@ -96,15 +96,9 @@ def setup_onroad(click, pm: PubMaster):
|
|||||||
server.send(cam_meta.stream, IMG_BYTES, cs.frameId, cs.timestampSof, cs.timestampEof)
|
server.send(cam_meta.stream, IMG_BYTES, cs.frameId, cs.timestampSof, cs.timestampEof)
|
||||||
|
|
||||||
@mock_messages(['liveLocationKalman'])
|
@mock_messages(['liveLocationKalman'])
|
||||||
def setup_onroad_map(click, pm: PubMaster):
|
|
||||||
setup_onroad(click, pm)
|
|
||||||
|
|
||||||
click(500, 500)
|
|
||||||
|
|
||||||
time.sleep(UI_DELAY) # give time for the map to render
|
|
||||||
|
|
||||||
def setup_onroad_sidebar(click, pm: PubMaster):
|
def setup_onroad_sidebar(click, pm: PubMaster):
|
||||||
setup_onroad_map(click, pm)
|
|
||||||
click(500, 500)
|
click(500, 500)
|
||||||
|
|
||||||
CASES = {
|
CASES = {
|
||||||
@@ -112,7 +106,6 @@ CASES = {
|
|||||||
"settings_device": setup_settings_device,
|
"settings_device": setup_settings_device,
|
||||||
"settings_network": setup_settings_network,
|
"settings_network": setup_settings_network,
|
||||||
"onroad": setup_onroad,
|
"onroad": setup_onroad,
|
||||||
"onroad_map": setup_onroad_map,
|
|
||||||
"onroad_sidebar": setup_onroad_sidebar
|
"onroad_sidebar": setup_onroad_sidebar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -305,7 +305,6 @@ static void update_state(UIState *s) {
|
|||||||
void ui_update_params(UIState *s) {
|
void ui_update_params(UIState *s) {
|
||||||
auto params = Params();
|
auto params = Params();
|
||||||
s->scene.is_metric = params.getBool("IsMetric");
|
s->scene.is_metric = params.getBool("IsMetric");
|
||||||
s->scene.map_on_left = params.getBool("NavSettingLeftSide");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_update_frogpilot_params(UIState *s) {
|
void ui_update_frogpilot_params(UIState *s) {
|
||||||
@@ -373,13 +372,10 @@ void ui_update_frogpilot_params(UIState *s) {
|
|||||||
scene.reverse_cruise_ui = scene.reverse_cruise && params.getBool("ReverseCruiseUI");
|
scene.reverse_cruise_ui = scene.reverse_cruise && params.getBool("ReverseCruiseUI");
|
||||||
|
|
||||||
bool quality_of_life_visuals = params.getBool("QOLVisuals");
|
bool quality_of_life_visuals = params.getBool("QOLVisuals");
|
||||||
scene.big_map = quality_of_life_visuals && params.getBool("BigMap");
|
|
||||||
scene.full_map = scene.big_map && params.getBool("FullMap");
|
|
||||||
scene.camera_view = quality_of_life_visuals ? params.getInt("CameraView") : 0;
|
scene.camera_view = quality_of_life_visuals ? params.getInt("CameraView") : 0;
|
||||||
scene.driver_camera = quality_of_life_visuals && params.getBool("DriverCamera");
|
scene.driver_camera = quality_of_life_visuals && params.getBool("DriverCamera");
|
||||||
scene.hide_speed = quality_of_life_visuals && params.getBool("HideSpeed");
|
scene.hide_speed = quality_of_life_visuals && params.getBool("HideSpeed");
|
||||||
scene.hide_speed_ui = scene.hide_speed && params.getBool("HideSpeedUI");
|
scene.hide_speed_ui = scene.hide_speed && params.getBool("HideSpeedUI");
|
||||||
scene.map_style = quality_of_life_visuals ? params.getInt("MapStyle") : 0;
|
|
||||||
scene.numerical_temp = quality_of_life_visuals && params.getBool("NumericalTemp");
|
scene.numerical_temp = quality_of_life_visuals && params.getBool("NumericalTemp");
|
||||||
scene.fahrenheit = scene.numerical_temp && params.getBool("Fahrenheit");
|
scene.fahrenheit = scene.numerical_temp && params.getBool("Fahrenheit");
|
||||||
scene.wheel_speed = quality_of_life_visuals && params.getBool("WheelSpeed");
|
scene.wheel_speed = quality_of_life_visuals && params.getBool("WheelSpeed");
|
||||||
@@ -387,7 +383,6 @@ void ui_update_frogpilot_params(UIState *s) {
|
|||||||
bool screen_management = params.getBool("ScreenManagement");
|
bool screen_management = params.getBool("ScreenManagement");
|
||||||
bool hide_ui_elements = screen_management && params.getBool("HideUIElements");
|
bool hide_ui_elements = screen_management && params.getBool("HideUIElements");
|
||||||
scene.hide_alerts = hide_ui_elements && params.getBool("HideAlerts");
|
scene.hide_alerts = hide_ui_elements && params.getBool("HideAlerts");
|
||||||
scene.hide_map_icon = hide_ui_elements && params.getBool("HideMapIcon");
|
|
||||||
scene.hide_max_speed = hide_ui_elements && params.getBool("HideMaxSpeed");
|
scene.hide_max_speed = hide_ui_elements && params.getBool("HideMaxSpeed");
|
||||||
scene.screen_brightness = screen_management ? params.getInt("ScreenBrightness") : 101;
|
scene.screen_brightness = screen_management ? params.getInt("ScreenBrightness") : 101;
|
||||||
scene.screen_brightness_onroad = screen_management ? params.getInt("ScreenBrightnessOnroad") : 101;
|
scene.screen_brightness_onroad = screen_management ? params.getInt("ScreenBrightnessOnroad") : 101;
|
||||||
@@ -437,16 +432,12 @@ UIState::UIState(QObject *parent) : QObject(parent) {
|
|||||||
sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({
|
sm = std::make_unique<SubMaster, const std::initializer_list<const char *>>({
|
||||||
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
|
"modelV2", "controlsState", "liveCalibration", "radarState", "deviceState",
|
||||||
"pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2",
|
"pandaStates", "carParams", "driverMonitoringState", "carState", "liveLocationKalman", "driverStateV2",
|
||||||
"wideRoadCameraState", "managerState", "navInstruction", "navRoute", "uiPlan", "liveTorqueParameters",
|
"wideRoadCameraState", "managerState", "uiPlan", "liveTorqueParameters",
|
||||||
"frogpilotCarControl", "frogpilotDeviceState", "frogpilotPlan",
|
"frogpilotCarControl", "frogpilotDeviceState", "frogpilotPlan",
|
||||||
});
|
});
|
||||||
|
|
||||||
Params params;
|
Params params;
|
||||||
language = QString::fromStdString(params.get("LanguageSetting"));
|
language = QString::fromStdString(params.get("LanguageSetting"));
|
||||||
auto prime_value = params.get("PrimeType");
|
|
||||||
if (!prime_value.empty()) {
|
|
||||||
prime_type = static_cast<PrimeType>(std::atoi(prime_value.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// update timer
|
// update timer
|
||||||
timer = new QTimer(this);
|
timer = new QTimer(this);
|
||||||
@@ -480,21 +471,6 @@ void UIState::update() {
|
|||||||
scene.driver_camera_timer = (scene.driver_camera && scene.reverse) ? scene.driver_camera_timer + 1 : 0;
|
scene.driver_camera_timer = (scene.driver_camera && scene.reverse) ? scene.driver_camera_timer + 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIState::setPrimeType(PrimeType type) {
|
|
||||||
if (type != prime_type) {
|
|
||||||
bool prev_prime = hasPrime();
|
|
||||||
|
|
||||||
prime_type = type;
|
|
||||||
Params().put("PrimeType", std::to_string(prime_type));
|
|
||||||
emit primeTypeChanged(prime_type);
|
|
||||||
|
|
||||||
bool prime = hasPrime();
|
|
||||||
if (prev_prime != prime) {
|
|
||||||
emit primeChanged(prime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) {
|
Device::Device(QObject *parent) : brightness_filter(BACKLIGHT_OFFROAD, BACKLIGHT_TS, BACKLIGHT_DT), QObject(parent) {
|
||||||
setAwake(true);
|
setAwake(true);
|
||||||
resetInteractiveTimeout();
|
resetInteractiveTimeout();
|
||||||
|
|||||||
@@ -155,16 +155,6 @@ struct Alert {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum PrimeType {
|
|
||||||
UNKNOWN = -1,
|
|
||||||
NONE = 0,
|
|
||||||
MAGENTA = 1,
|
|
||||||
LITE = 2,
|
|
||||||
BLUE = 3,
|
|
||||||
MAGENTA_NEW = 4,
|
|
||||||
PURPLE = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct UIScene {
|
typedef struct UIScene {
|
||||||
bool calibration_valid = false;
|
bool calibration_valid = false;
|
||||||
@@ -191,7 +181,6 @@ typedef struct UIScene {
|
|||||||
float driver_pose_coss[3];
|
float driver_pose_coss[3];
|
||||||
vec3 face_kpts_draw[std::size(default_face_kpts_3d)];
|
vec3 face_kpts_draw[std::size(default_face_kpts_3d)];
|
||||||
|
|
||||||
bool navigate_on_openpilot = false;
|
|
||||||
cereal::LongitudinalPersonality personality;
|
cereal::LongitudinalPersonality personality;
|
||||||
|
|
||||||
float light_sensor;
|
float light_sensor;
|
||||||
@@ -204,7 +193,6 @@ typedef struct UIScene {
|
|||||||
bool adjacent_path;
|
bool adjacent_path;
|
||||||
bool adjacent_path_metrics;
|
bool adjacent_path_metrics;
|
||||||
bool always_on_lateral_active;
|
bool always_on_lateral_active;
|
||||||
bool big_map;
|
|
||||||
bool blind_spot_left;
|
bool blind_spot_left;
|
||||||
bool blind_spot_path;
|
bool blind_spot_path;
|
||||||
bool blind_spot_right;
|
bool blind_spot_right;
|
||||||
@@ -219,7 +207,6 @@ typedef struct UIScene {
|
|||||||
bool experimental_mode_via_screen;
|
bool experimental_mode_via_screen;
|
||||||
bool fahrenheit;
|
bool fahrenheit;
|
||||||
bool fps_counter;
|
bool fps_counter;
|
||||||
bool full_map;
|
|
||||||
bool has_auto_tune;
|
bool has_auto_tune;
|
||||||
bool hide_alerts;
|
bool hide_alerts;
|
||||||
bool hide_lead_marker;
|
bool hide_lead_marker;
|
||||||
@@ -230,7 +217,6 @@ typedef struct UIScene {
|
|||||||
bool holiday_themes;
|
bool holiday_themes;
|
||||||
bool lead_info;
|
bool lead_info;
|
||||||
bool live_valid;
|
bool live_valid;
|
||||||
bool map_open;
|
|
||||||
bool model_ui;
|
bool model_ui;
|
||||||
bool numerical_temp;
|
bool numerical_temp;
|
||||||
bool online;
|
bool online;
|
||||||
@@ -328,10 +314,6 @@ public:
|
|||||||
return scene.started && (*sm)["controlsState"].getControlsState().getEnabled();
|
return scene.started && (*sm)["controlsState"].getControlsState().getEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setPrimeType(PrimeType type);
|
|
||||||
inline PrimeType primeType() const { return prime_type; }
|
|
||||||
inline bool hasPrime() const { return prime_type != PrimeType::UNKNOWN && prime_type != PrimeType::NONE; }
|
|
||||||
|
|
||||||
int fb_w = 0, fb_h = 0;
|
int fb_w = 0, fb_h = 0;
|
||||||
|
|
||||||
std::unique_ptr<SubMaster> sm;
|
std::unique_ptr<SubMaster> sm;
|
||||||
@@ -349,8 +331,6 @@ public:
|
|||||||
signals:
|
signals:
|
||||||
void uiUpdate(const UIState &s);
|
void uiUpdate(const UIState &s);
|
||||||
void offroadTransition(bool offroad);
|
void offroadTransition(bool offroad);
|
||||||
void primeChanged(bool prime);
|
|
||||||
void primeTypeChanged(PrimeType prime_type);
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void update();
|
void update();
|
||||||
@@ -358,7 +338,6 @@ private slots:
|
|||||||
private:
|
private:
|
||||||
QTimer *timer;
|
QTimer *timer;
|
||||||
bool started_prev = false;
|
bool started_prev = false;
|
||||||
PrimeType prime_type = PrimeType::UNKNOWN;
|
|
||||||
|
|
||||||
// FrogPilot variables
|
// FrogPilot variables
|
||||||
Params paramsMemory{"/dev/shm/params"};
|
Params paramsMemory{"/dev/shm/params"};
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ int main(int argc, char *argv[]) {
|
|||||||
{
|
{
|
||||||
QHBoxLayout *hlayout = new QHBoxLayout();
|
QHBoxLayout *hlayout = new QHBoxLayout();
|
||||||
layout->addLayout(hlayout);
|
layout->addLayout(hlayout);
|
||||||
hlayout->addWidget(new CameraWidget("navd", VISION_STREAM_MAP, false));
|
|
||||||
hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_ROAD, false));
|
hlayout->addWidget(new CameraWidget("camerad", VISION_STREAM_ROAD, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user