diff --git a/RELEASES.md b/RELEASES.md index 34ca0338ed..a61e8b4ebd 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,6 +5,7 @@ Version 0.9.2 (2023-03-XX) * Chevrolet Trailblazer 2021-22 support thanks to TurboCE! * Buick LaCrosse 2017-19 support thanks to koch-cf! * Kia Niro EV 2023 support thanks to JosselinLecocq! +* Lexus ES 2017-18 support * Škoda Fabia 2022-23 support thanks to jyoung8607! Version 0.9.1 (2023-02-28) diff --git a/docs/CARS.md b/docs/CARS.md index 96b30fcf1c..56d909a64a 100644 --- a/docs/CARS.md +++ b/docs/CARS.md @@ -4,7 +4,7 @@ A supported vehicle is one that just works when you install a comma three. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified. -# 241 Supported Cars +# 242 Supported Cars |Make|Model|Supported Package|ACC|No ACC accel below|No ALC below|Steering Torque|Resume from stop|Harness|Video| |---|---|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:| @@ -127,6 +127,7 @@ A supported vehicle is one that just works when you install a comma three. All s |Kia|Stinger 2022|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai K|| |Kia|Telluride 2020-22|All|openpilot available[1](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Hyundai H|| |Lexus|CT Hybrid 2017-18|Lexus Safety System+|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| +|Lexus|ES 2017-18|All|openpilot available[2](#footnotes)|19 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-empty.svg)](##)|Toyota|| |Lexus|ES 2019-22|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Lexus|ES Hybrid 2017-18|All|openpilot available[2](#footnotes)|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| |Lexus|ES Hybrid 2019-23|All|openpilot|0 mph|0 mph|[![star](assets/icon-star-full.svg)](##)|[![star](assets/icon-star-full.svg)](##)|Toyota|| diff --git a/selfdrive/car/tests/routes.py b/selfdrive/car/tests/routes.py index 921bed5406..32acacdd4f 100644 --- a/selfdrive/car/tests/routes.py +++ b/selfdrive/car/tests/routes.py @@ -164,6 +164,7 @@ routes = [ CarTestRoute("a5c341bb250ca2f0|2022-05-18--16-05-17", TOYOTA.RAV4_TSS2_2022), CarTestRoute("7e34a988419b5307|2019-12-18--19-13-30", TOYOTA.RAV4H_TSS2), CarTestRoute("2475fb3eb2ffcc2e|2022-04-29--12-46-23", TOYOTA.RAV4H_TSS2_2022), + CarTestRoute("7a31f030957b9c85|2023-04-01--14-12-51", TOYOTA.LEXUS_ES), CarTestRoute("e6a24be49a6cd46e|2019-10-29--10-52-42", TOYOTA.LEXUS_ES_TSS2), CarTestRoute("da23c367491f53e2|2021-05-21--09-09-11", TOYOTA.LEXUS_CTH, segment=3), CarTestRoute("f49e8041283f2939|2019-05-30--11-51-51", TOYOTA.LEXUS_ESH_TSS2), diff --git a/selfdrive/car/torque_data/substitute.yaml b/selfdrive/car/torque_data/substitute.yaml index 5feef12206..5ea84e5591 100644 --- a/selfdrive/car/torque_data/substitute.yaml +++ b/selfdrive/car/torque_data/substitute.yaml @@ -12,6 +12,7 @@ TOYOTA C-HR HYBRID 2018: TOYOTA C-HR 2018 TOYOTA C-HR HYBRID 2022: TOYOTA C-HR 2021 LEXUS IS 2018: LEXUS NX 2018 LEXUS CT HYBRID 2018 : LEXUS NX 2018 +LEXUS ES 2018: TOYOTA CAMRY HYBRID 2018 LEXUS ES HYBRID 2018: TOYOTA CAMRY HYBRID 2018 LEXUS NX HYBRID 2020: LEXUS NX 2020 LEXUS RC 2020: LEXUS NX 2020 diff --git a/selfdrive/car/toyota/interface.py b/selfdrive/car/toyota/interface.py index 6e8664c340..1886535133 100644 --- a/selfdrive/car/toyota/interface.py +++ b/selfdrive/car/toyota/interface.py @@ -138,8 +138,9 @@ class CarInterface(CarInterfaceBase): tire_stiffness_factor = 0.444 # not optimized yet ret.mass = 3060. * CV.LB_TO_KG + STD_CARGO_KG - elif candidate in (CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2, CAR.LEXUS_ESH): - stop_and_go = True + elif candidate in (CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_ES_TSS2, CAR.LEXUS_ESH_TSS2): + if candidate not in (CAR.LEXUS_ES,): # TODO: LEXUS_ES may have sng + stop_and_go = True ret.wheelbase = 2.8702 ret.steerRatio = 16.0 # not optimized tire_stiffness_factor = 0.444 # not optimized yet diff --git a/selfdrive/car/toyota/values.py b/selfdrive/car/toyota/values.py index 60450b9fe5..a54e5511db 100644 --- a/selfdrive/car/toyota/values.py +++ b/selfdrive/car/toyota/values.py @@ -77,6 +77,7 @@ class CAR: # Lexus LEXUS_CTH = "LEXUS CT HYBRID 2018" + LEXUS_ES = "LEXUS ES 2018" LEXUS_ESH = "LEXUS ES HYBRID 2018" LEXUS_ES_TSS2 = "LEXUS ES 2019" LEXUS_ESH_TSS2 = "LEXUS ES HYBRID 2019" @@ -169,6 +170,7 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { # Lexus CAR.LEXUS_CTH: ToyotaCarInfo("Lexus CT Hybrid 2017-18", "Lexus Safety System+"), + CAR.LEXUS_ES: ToyotaCarInfo("Lexus ES 2017-18"), CAR.LEXUS_ESH: ToyotaCarInfo("Lexus ES Hybrid 2017-18"), CAR.LEXUS_ES_TSS2: ToyotaCarInfo("Lexus ES 2019-22"), CAR.LEXUS_ESH_TSS2: ToyotaCarInfo("Lexus ES Hybrid 2019-23", video_link="https://youtu.be/BZ29osRVJeg?t=12"), @@ -193,23 +195,24 @@ CAR_INFO: Dict[str, Union[ToyotaCarInfo, List[ToyotaCarInfo]]] = { # (addr, cars, bus, 1/freq*100, vl) STATIC_DSU_MSGS = [ (0x128, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.AVALON), 1, 3, b'\xf4\x01\x90\x83\x00\x37'), - (0x128, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH), 1, 3, b'\x03\x00\x20\x00\x00\x52'), - (0x141, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 2, b'\x00\x00\x00\x46'), - (0x160, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 7, b'\x00\x00\x08\x12\x01\x31\x9c\x51'), - (0x161, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.AVALON, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 7, b'\x00\x1e\x00\x00\x00\x80\x07'), + (0x128, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH), 1, 3, b'\x03\x00\x20\x00\x00\x52'), + (0x141, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 2, b'\x00\x00\x00\x46'), + (0x160, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 1, 7, b'\x00\x00\x08\x12\x01\x31\x9c\x51'), + (0x161, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.AVALON, CAR.LEXUS_RX, CAR.PRIUS_V, CAR.LEXUS_ES), 1, 7, b'\x00\x1e\x00\x00\x00\x80\x07'), (0X161, (CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH), 1, 7, b'\x00\x1e\x00\xd4\x00\x00\x5b'), - (0x283, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 3, b'\x00\x00\x00\x00\x00\x00\x8c'), + (0x283, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 3, b'\x00\x00\x00\x00\x00\x00\x8c'), (0x2E6, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 3, b'\xff\xf8\x00\x08\x7f\xe0\x00\x4e'), (0x2E7, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 3, b'\xa8\x9c\x31\x9c\x00\x00\x00\x02'), (0x33E, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH), 0, 20, b'\x0f\xff\x26\x40\x00\x1f\x00'), - (0x344, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 5, b'\x00\x00\x01\x00\x00\x00\x00\x50'), + (0x344, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 5, b'\x00\x00\x01\x00\x00\x00\x00\x50'), (0x365, (CAR.PRIUS, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.HIGHLANDERH), 0, 20, b'\x00\x00\x00\x80\x03\x00\x08'), - (0x365, (CAR.RAV4, CAR.RAV4H, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 20, b'\x00\x00\x00\x80\xfc\x00\x08'), + (0x365, (CAR.RAV4, CAR.RAV4H, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 20, b'\x00\x00\x00\x80\xfc\x00\x08'), (0x366, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.HIGHLANDERH), 0, 20, b'\x00\x00\x4d\x82\x40\x02\x00'), (0x366, (CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 20, b'\x00\x72\x07\xff\x09\xfe\x00'), + (0x366, (CAR.LEXUS_ES,), 0, 20, b'\x00\x95\x07\xfe\x08\x05\x00'), (0x470, (CAR.PRIUS, CAR.LEXUS_RXH), 1, 100, b'\x00\x00\x02\x7a'), - (0x470, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.RAV4H, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.PRIUS_V), 1, 100, b'\x00\x00\x01\x79'), - (0x4CB, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 100, b'\x0c\x00\x00\x00\x00\x00\x00\x00'), + (0x470, (CAR.HIGHLANDER, CAR.HIGHLANDERH, CAR.RAV4H, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.PRIUS_V), 1, 100, b'\x00\x00\x01\x79'), + (0x4CB, (CAR.PRIUS, CAR.RAV4H, CAR.LEXUS_RXH, CAR.LEXUS_NXH, CAR.LEXUS_NX, CAR.RAV4, CAR.COROLLA, CAR.HIGHLANDERH, CAR.HIGHLANDER, CAR.AVALON, CAR.SIENNA, CAR.LEXUS_CTH, CAR.LEXUS_ES, CAR.LEXUS_ESH, CAR.LEXUS_RX, CAR.PRIUS_V), 0, 100, b'\x0c\x00\x00\x00\x00\x00\x00\x00'), ] TOYOTA_VERSION_REQUEST = b'\x1a\x88\x01' @@ -1731,6 +1734,26 @@ FW_VERSIONS = { b'\x028646F3309100\x00\x00\x00\x008646G3304000\x00\x00\x00\x00', ], }, + CAR.LEXUS_ES: { + (Ecu.engine, 0x7e0, None): [ + b'\x02333R0000\x00\x00\x00\x00\x00\x00\x00\x00A0C01000\x00\x00\x00\x00\x00\x00\x00\x00', + ], + (Ecu.abs, 0x7b0, None): [ + b'F152606202\x00\x00\x00\x00\x00\x00', + ], + (Ecu.dsu, 0x791, None): [ + b'881513309500\x00\x00\x00\x00', + ], + (Ecu.eps, 0x7a1, None): [ + b'8965B33502\x00\x00\x00\x00\x00\x00', + ], + (Ecu.fwdRadar, 0x750, 0xf): [ + b'8821F4701200\x00\x00\x00\x00', + ], + (Ecu.fwdCamera, 0x750, 0x6d): [ + b'8646F3302200\x00\x00\x00\x00', + ], + }, CAR.LEXUS_ESH: { (Ecu.engine, 0x7e0, None): [ b'\x02333M4200\x00\x00\x00\x00\x00\x00\x00\x00A4701000\x00\x00\x00\x00\x00\x00\x00\x00', @@ -2161,6 +2184,7 @@ DBC = { CAR.RAV4_TSS2_2023: dbc_dict('toyota_nodsu_pt_generated', None), CAR.COROLLA_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.COROLLAH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), + CAR.LEXUS_ES: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), CAR.LEXUS_ES_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_ESH_TSS2: dbc_dict('toyota_nodsu_pt_generated', 'toyota_tss2_adas'), CAR.LEXUS_ESH: dbc_dict('toyota_new_mc_pt_generated', 'toyota_adas'), diff --git a/tools/cabana/dbc/dbcfile.cc b/tools/cabana/dbc/dbcfile.cc index 8b4f98508c..c4025725ac 100644 --- a/tools/cabana/dbc/dbcfile.cc +++ b/tools/cabana/dbc/dbcfile.cc @@ -186,6 +186,19 @@ QStringList DBCFile::signalNames() const { return ret; } +int DBCFile::signalCount(const MessageId &id) const { + if (msgs.count(id.address) == 0) return 0; + return msgs.at(id.address).sigs.size(); +} + +int DBCFile::signalCount() const { + int total = 0; + for (auto const& [_, msg] : msgs) { + total += msg.sigs.size(); + } + return total; +} + int DBCFile::msgCount() const { return msgs.size(); } @@ -194,6 +207,10 @@ QString DBCFile::name() const { return name_; } +bool DBCFile::isEmpty() const { + return (signalCount() == 0) && name().isEmpty(); +} + void DBCFile::parseExtraInfo(const QString &content) { static QRegularExpression bo_regexp(R"(^BO_ (\w+) (\w+) *: (\w+) (\w+))"); static QRegularExpression sg_regexp(R"(^SG_ (\w+) : (\d+)\|(\d+)@(\d+)([\+|\-]) \(([0-9.+\-eE]+),([0-9.+\-eE]+)\) \[([0-9.+\-eE]+)\|([0-9.+\-eE]+)\] \"(.*)\" (.*))"); diff --git a/tools/cabana/dbc/dbcfile.h b/tools/cabana/dbc/dbcfile.h index 082ee773b1..96fbe6a9f1 100644 --- a/tools/cabana/dbc/dbcfile.h +++ b/tools/cabana/dbc/dbcfile.h @@ -43,8 +43,11 @@ public: const cabana::Msg *msg(uint32_t address) const; const cabana::Msg* msg(const QString &name); QStringList signalNames() const; + int signalCount(const MessageId &id) const; + int signalCount() const; int msgCount() const; QString name() const; + bool isEmpty() const; QString filename; diff --git a/tools/cabana/dbc/dbcmanager.cc b/tools/cabana/dbc/dbcmanager.cc index 24803a303f..123d783986 100644 --- a/tools/cabana/dbc/dbcmanager.cc +++ b/tools/cabana/dbc/dbcmanager.cc @@ -180,6 +180,26 @@ QStringList DBCManager::signalNames() const { return ret; } +int DBCManager::signalCount(const MessageId &id) const { + auto sources_dbc_file = findDBCFile(id); + if (!sources_dbc_file) { + return 0; + } + + auto [_, dbc_file] = *sources_dbc_file; + return dbc_file->signalCount(id); +} + +int DBCManager::signalCount() const { + int ret = 0; + + for (auto &[_, dbc_file] : dbc_files) { + ret += dbc_file->signalCount(); + } + + return ret; +} + int DBCManager::msgCount() const { int ret = 0; @@ -194,6 +214,16 @@ int DBCManager::dbcCount() const { return dbc_files.size(); } +int DBCManager::nonEmptyDBCCount() const { + int cnt = 0; + for (auto &[_, dbc_file] : dbc_files) { + if (!dbc_file->isEmpty()) { + cnt++; + } + } + return cnt; +} + void DBCManager::updateSources(const SourceSet &s) { sources = s; } diff --git a/tools/cabana/dbc/dbcmanager.h b/tools/cabana/dbc/dbcmanager.h index f9af96516c..60d8a072ec 100644 --- a/tools/cabana/dbc/dbcmanager.h +++ b/tools/cabana/dbc/dbcmanager.h @@ -38,8 +38,11 @@ public: const cabana::Msg* msg(uint8_t source, const QString &name); QStringList signalNames() const; + int signalCount(const MessageId &id) const; + int signalCount() const; int msgCount() const; int dbcCount() const; + int nonEmptyDBCCount() const; std::optional> findDBCFile(const uint8_t source) const; std::optional> findDBCFile(const MessageId &id) const; diff --git a/tools/cabana/mainwin.cc b/tools/cabana/mainwin.cc index c979a20f16..5b6d436dca 100644 --- a/tools/cabana/mainwin.cc +++ b/tools/cabana/mainwin.cc @@ -232,6 +232,7 @@ void MainWindow::undoStackIndexChanged(int index) { prev_undostack_index = index; prev_undostack_count = count; autoSave(); + updateLoadSaveMenus(); } void MainWindow::undoStackCleanChanged(bool clean) { @@ -411,7 +412,7 @@ void MainWindow::saveFile() { if (!dbc_file->filename.isEmpty()) { dbc_file->save(); updateRecentFiles(dbc_file->filename); - } else { + } else if (!dbc_file->isEmpty()) { QString fn = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); if (!fn.isEmpty()) { dbc_file->saveAs(fn); @@ -426,22 +427,22 @@ void MainWindow::saveFile() { void MainWindow::saveAs() { // Assume only one file is open - assert(dbc()->dbcCount() > 0); - auto &[_, dbc_file] = dbc()->dbc_files.first(); - - QString fn = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); - if (!fn.isEmpty()) { - dbc_file->saveAs(fn); + for (auto &[s, dbc_file] : dbc()->dbc_files) { + if (dbc_file->isEmpty()) continue; + QString fn = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::cleanPath(settings.last_dir + "/untitled.dbc"), tr("DBC (*.dbc)")); + if (!fn.isEmpty()) { + dbc_file->saveAs(fn); + } } } void MainWindow::saveDBCToClipboard() { // Assume only one file is open - assert(dbc()->dbcCount() > 0); - - auto &[_, dbc_file] = dbc()->dbc_files.first(); - QGuiApplication::clipboard()->setText(dbc_file->generateDBC()); - QMessageBox::information(this, tr("Copy To Clipboard"), tr("DBC Successfully copied!")); + for (auto &[s, dbc_file] : dbc()->dbc_files) { + if (dbc_file->isEmpty()) continue; + QGuiApplication::clipboard()->setText(dbc_file->generateDBC()); + QMessageBox::information(this, tr("Copy To Clipboard"), tr("DBC Successfully copied!")); + } } void MainWindow::updateSources(const SourceSet &s) { @@ -450,17 +451,20 @@ void MainWindow::updateSources(const SourceSet &s) { } void MainWindow::updateLoadSaveMenus() { - if (dbc()->dbcCount() > 1) { + int cnt = dbc()->nonEmptyDBCCount(); + save_dbc->setEnabled(cnt > 0); + + if (cnt > 1) { save_dbc->setText(tr("Save %1 DBCs...").arg(dbc()->dbcCount())); } else { save_dbc->setText(tr("Save DBC...")); } // TODO: Support save as for multiple files - save_dbc_as->setEnabled(dbc()->dbcCount() == 1); + save_dbc_as->setEnabled(cnt == 1); // TODO: Support clipboard for multiple files - copy_dbc_to_clipboard->setEnabled(dbc()->dbcCount() == 1); + copy_dbc_to_clipboard->setEnabled(cnt == 1); QList sources_sorted = sources.toList(); diff --git a/tools/cabana/signalview.cc b/tools/cabana/signalview.cc index f404dd3346..ef05decda6 100644 --- a/tools/cabana/signalview.cc +++ b/tools/cabana/signalview.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -351,7 +352,7 @@ bool SignalItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, c void SignalItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const { auto item = (SignalModel::Item *)index.internalPointer(); if (editor && item->type == SignalModel::Item::Sig && index.column() == 1) { - QRect geom = option.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &option); + QRect geom = option.rect; geom.setLeft(geom.right() - editor->sizeHint().width()); editor->setGeometry(geom); return;