diff --git a/selfdrive/assets/offroad/fcc.html b/selfdrive/assets/offroad/fcc.html new file mode 100644 index 0000000000..0eb6e777bc --- /dev/null +++ b/selfdrive/assets/offroad/fcc.html @@ -0,0 +1,54 @@ + + +

Unique Identifier

+

comma three

+ +

Authorized Components

+ +
Thundersoft TurboX D845 SOM
+

FCC ID: 2AOHHTURBOXSOMD845

+ +
Quectel/EG25-G
+

FCC ID: XMR201903EG25GM

+

+ This device complies with Part 15 of the FCC Rules. + Operation is subject to the following two conditions: + +

(1) this device may not cause harmful interference, and +

(2) this device must accept any interference received, including interference that may cause undesired operation.

+ + The following test reports are subject to this declaration: + Test report number: HR20191001605 + Issue date: 2019-2-21 + + The following manufacturer/importer/entity (located in the USA) is responsible for this declaration: + Company name: Quectel Wireless Solutions Co., Ltd. + Name/Title (legal representative): Yin JiXiong + Address: 7th Floor, Hongye Building, No.1801 Hongmei Road, Xuhui District, Shanghai 200233, China + Phone: +8602150086326 Extension: 800 + Fax: +862153253668 + E-mail: johnny.xiang@quectel.com +

+ +

Responsible Party - U.S. Contact Information

+

comma.ai

+

501 W Broadway St

+

STE A #403

+

San Diego, California

+

92101

+

United States

+

support@comma.ai

+ +

Responsible Party - U.S. Contact Information

+

This device complies with part 15 of the FCC Rules. Operation is subject to the following two conditions: (1) This device may not cause harmful interference, and (2) this device must accept any interference received, including interference that may cause undesired operation. Note: This equipment has been tested and found to comply with the limits for a Class B digital device, pursuant to part 15 of the FCC Rules. These limits are designed to provide reasonable protection against harmful interference in a residential installation.

+

This equipment generates, uses, and can radiate radio frequency energy and, if not installed and used in accordance with the instructions, may cause harmful interference to radio communications. However, there is no guarantee that interference will not occur in a particular installation.

+

If this equipment does cause harmful interference to radio or television reception, which can be determined by turning the equipment off and on, the user is encouraged to try to correct the interference by one or more of the following measures:

+

Reorient or relocate the receiving antenna.

+

Increase the separation between the equipment and receiver.

+

Connect the equipment to an outlet on a circuit different from that to which the receiver is connected.

+

Consult the dealer or an experienced radio/TV technician for help.

+

Changes or modifications to this product not expressly approved by the party responsible for compliance could void the electromagnetic compatibility (EMC) and wireless compliance and negate your authority to operate the product.

+

This product has demonstrated EMC compliance under conditions that included the use of compliant peripheral devices and shielded cables between system components. It is important that you use compliant peripheral devices and shielded cables between system components to reduce the possibility of causing interference to radios, televisions, and other electronic devices.

+

The radiated output power of this device meets the limits of FCC/IC radio frequency exposure limits. This device should be operated with a minimum separation distance of 20 cm (8 inches) between the equipment and a person's body.

+ + diff --git a/selfdrive/ui/qt/offroad/settings.cc b/selfdrive/ui/qt/offroad/settings.cc index ab70a6106c..c1915c21f5 100644 --- a/selfdrive/ui/qt/offroad/settings.cc +++ b/selfdrive/ui/qt/offroad/settings.cc @@ -23,6 +23,7 @@ #include "selfdrive/ui/qt/widgets/toggle.h" #include "selfdrive/ui/ui.h" #include "selfdrive/ui/qt/util.h" +#include "selfdrive/ui/qt/qt_window.h" TogglesPanel::TogglesPanel(QWidget *parent) : QWidget(parent) { QVBoxLayout *main_layout = new QVBoxLayout(this); @@ -147,14 +148,16 @@ DevicePanel::DevicePanel(QWidget* parent) : QWidget(parent) { }); } - auto uninstallBtn = new ButtonControl("Uninstall " + getBrand(), "UNINSTALL"); - connect(uninstallBtn, &ButtonControl::clicked, [=]() { - if (ConfirmationDialog::confirm("Are you sure you want to uninstall?", this)) { - Params().putBool("DoUninstall", true); - } - }); + ButtonControl *regulatoryBtn = nullptr; + if (Hardware::TICI() || true) { + regulatoryBtn = new ButtonControl("Regulatory", "VIEW", ""); + connect(regulatoryBtn, &ButtonControl::clicked, [=]() { + const std::string txt = util::read_file(ASSET_PATH.toStdString() + "/offroad/fcc.html"); + RichTextDialog::alert(QString::fromStdString(txt), this); + }); + } - for (auto btn : {dcamBtn, resetCalibBtn, retrainingBtn, uninstallBtn}) { + for (auto btn : {dcamBtn, resetCalibBtn, retrainingBtn, regulatoryBtn}) { if (btn) { main_layout->addWidget(horizontal_line()); connect(parent, SIGNAL(offroadTransition(bool)), btn, SLOT(setEnabled(bool))); @@ -219,11 +222,18 @@ SoftwarePanel::SoftwarePanel(QWidget* parent) : QWidget(parent) { QWidget *widgets[] = {versionLbl, lastUpdateLbl, updateBtn, gitBranchLbl, gitCommitLbl, osVersionLbl}; for (int i = 0; i < std::size(widgets); ++i) { main_layout->addWidget(widgets[i]); - if (i < std::size(widgets) - 1) { - main_layout->addWidget(horizontal_line()); - } + main_layout->addWidget(horizontal_line()); } + auto uninstallBtn = new ButtonControl("Uninstall " + getBrand(), "UNINSTALL"); + connect(uninstallBtn, &ButtonControl::clicked, [=]() { + if (ConfirmationDialog::confirm("Are you sure you want to uninstall?", this)) { + Params().putBool("DoUninstall", true); + } + }); + connect(parent, SIGNAL(offroadTransition(bool)), uninstallBtn, SLOT(setEnabled(bool))); + main_layout->addWidget(uninstallBtn); + fs_watch = new QFileSystemWatcher(this); QObject::connect(fs_watch, &QFileSystemWatcher::fileChanged, [=](const QString path) { int update_failed_count = params.get("UpdateFailedCount").value_or(0); diff --git a/selfdrive/ui/qt/widgets/input.cc b/selfdrive/ui/qt/widgets/input.cc index 986c7b2fa2..8e978ca33d 100644 --- a/selfdrive/ui/qt/widgets/input.cc +++ b/selfdrive/ui/qt/widgets/input.cc @@ -2,14 +2,39 @@ #include +#include "selfdrive/hardware/hw.h" #include "selfdrive/ui/qt/util.h" #include "selfdrive/ui/qt/qt_window.h" -#include "selfdrive/hardware/hw.h" +#include "selfdrive/ui/qt/widgets/scrollview.h" QDialogBase::QDialogBase(QWidget *parent) : QDialog(parent) { Q_ASSERT(parent != nullptr); parent->installEventFilter(this); + + setWindowFlags(Qt::Popup); + setStyleSheet(R"( + * { + outline: none; + color: white; + font-family: Inter; + } + QDialogBase { + background-color: black; + } + QPushButton { + height: 160; + font-size: 55px; + font-weight: 400; + border-radius: 10px; + color: white; + background-color: #333333; + } + QPushButton:pressed { + background-color: #444444; + } + + )"); } bool QDialogBase::eventFilter(QObject *o, QEvent *e) { @@ -19,6 +44,11 @@ bool QDialogBase::eventFilter(QObject *o, QEvent *e) { return QDialog::eventFilter(o, e); } +int QDialogBase::exec() { + setMainWindow(this); + return QDialog::exec(); +} + InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &subtitle, bool secret) : QDialogBase(parent) { main_layout = new QVBoxLayout(this); main_layout->setContentsMargins(50, 55, 50, 50); @@ -112,15 +142,6 @@ InputDialog::InputDialog(const QString &title, QWidget *parent, const QString &s }); main_layout->addWidget(k, 2, Qt::AlignBottom); - - setStyleSheet(R"( - * { - outline: none; - color: white; - font-family: Inter; - background-color: black; - } - )"); } QString InputDialog::getText(const QString &prompt, QWidget *parent, const QString &subtitle, @@ -136,11 +157,6 @@ QString InputDialog::text() { return line->text(); } -int InputDialog::exec() { - setMainWindow(this); - return QDialog::exec(); -} - void InputDialog::show() { setMainWindow(this); } @@ -165,9 +181,12 @@ void InputDialog::setMinLength(int length) { minLength = length; } +// ConfirmationDialog + ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString &confirm_text, const QString &cancel_text, QWidget *parent) : QDialogBase(parent) { QFrame *container = new QFrame(this); + container->setStyleSheet("QFrame { border-radius: 0; background-color: #ECECEC; }"); QVBoxLayout *main_layout = new QVBoxLayout(container); main_layout->setContentsMargins(32, 120, 32, 32); @@ -197,28 +216,6 @@ ConfirmationDialog::ConfirmationDialog(const QString &prompt_text, const QString QVBoxLayout *outer_layout = new QVBoxLayout(this); outer_layout->setContentsMargins(210, 170, 210, 170); outer_layout->addWidget(container); - - setWindowFlags(Qt::Popup); - setStyleSheet(R"( - ConfirmationDialog { - background-color: black; - } - QFrame { - border-radius: 0; - background-color: #ECECEC; - } - QPushButton { - height: 160; - font-size: 55px; - font-weight: 400; - border-radius: 10px; - color: white; - background-color: #333333; - } - QPushButton:pressed { - background-color: #444444; - } - )"); } bool ConfirmationDialog::alert(const QString &prompt_text, QWidget *parent) { @@ -231,7 +228,34 @@ bool ConfirmationDialog::confirm(const QString &prompt_text, QWidget *parent) { return d.exec(); } -int ConfirmationDialog::exec() { - setMainWindow(this); - return QDialog::exec(); + +// RichTextDialog + +RichTextDialog::RichTextDialog(const QString &prompt_text, const QString &btn_text, + QWidget *parent) : QDialogBase(parent) { + QFrame *container = new QFrame(this); + container->setStyleSheet("QFrame { background-color: #1B1B1B; }"); + QVBoxLayout *main_layout = new QVBoxLayout(container); + main_layout->setContentsMargins(32, 32, 32, 32); + + QLabel *prompt = new QLabel(prompt_text, this); + prompt->setWordWrap(true); + prompt->setAlignment(Qt::AlignLeft); + prompt->setTextFormat(Qt::RichText); + prompt->setStyleSheet("font-size: 42px; font-weight: light; color: #C9C9C9; margin: 45px;"); + main_layout->addWidget(new ScrollView(prompt, this), 1, Qt::AlignTop); + + // confirm button + QPushButton* confirm_btn = new QPushButton(btn_text); + main_layout->addWidget(confirm_btn); + QObject::connect(confirm_btn, &QPushButton::clicked, this, &QDialog::accept); + + QVBoxLayout *outer_layout = new QVBoxLayout(this); + outer_layout->setContentsMargins(100, 100, 100, 100); + outer_layout->addWidget(container); +} + +bool RichTextDialog::alert(const QString &prompt_text, QWidget *parent) { + auto d = RichTextDialog(prompt_text, "Ok", parent); + return d.exec(); } diff --git a/selfdrive/ui/qt/widgets/input.h b/selfdrive/ui/qt/widgets/input.h index 3532d426b5..f81211d0ee 100644 --- a/selfdrive/ui/qt/widgets/input.h +++ b/selfdrive/ui/qt/widgets/input.h @@ -16,6 +16,9 @@ class QDialogBase : public QDialog { protected: QDialogBase(QWidget *parent); bool eventFilter(QObject *o, QEvent *e) override; + +public slots: + int exec() override; }; class InputDialog : public QDialogBase { @@ -39,9 +42,6 @@ private: QVBoxLayout *main_layout; QPushButton *eye_btn; -public slots: - int exec() override; - private slots: void handleEnter(); @@ -58,7 +58,13 @@ public: const QString &cancel_text, QWidget* parent); static bool alert(const QString &prompt_text, QWidget *parent); static bool confirm(const QString &prompt_text, QWidget *parent); +}; -public slots: - int exec() override; +// larger ConfirmationDialog for rich text +class RichTextDialog : public QDialogBase { + Q_OBJECT + +public: + explicit RichTextDialog(const QString &prompt_text, const QString &btn_text, QWidget* parent); + static bool alert(const QString &prompt_text, QWidget *parent); };