diff --git a/openpilot/tools/cabana/core/message_id.h b/openpilot/tools/cabana/core/message_id.h index ecac279631..e7f4e3c0b4 100644 --- a/openpilot/tools/cabana/core/message_id.h +++ b/openpilot/tools/cabana/core/message_id.h @@ -1,7 +1,9 @@ #pragma once +#include #include #include #include +#include #include #include @@ -11,11 +13,18 @@ struct MessageId { uint8_t source = 0; uint32_t address = 0; std::string toString() const { char b[64]; snprintf(b, sizeof(b), "%u:%X", source, address); return b; } - static MessageId fromString(const std::string &s) { - const auto p = s.find(':'); - if (p == std::string::npos) return {}; - return {.source = static_cast(std::stoul(s.substr(0, p))), .address = static_cast(std::stoul(s.substr(p + 1), nullptr, 16))}; + // strict "bus:HEX" parser + static std::optional parse(const std::string &s) { + const auto colon = s.find(':'); + if (colon == std::string::npos) return std::nullopt; + const char *begin = s.data(), *end = begin + s.size(); + uint32_t source = 0, address = 0; + auto bus = std::from_chars(begin, begin + colon, source); + auto addr = std::from_chars(begin + colon + 1, end, address, 16); + if (bus.ec != std::errc() || bus.ptr != begin + colon || source > 255 || addr.ec != std::errc() || addr.ptr != end) return std::nullopt; + return MessageId{static_cast(source), address}; } + static MessageId fromString(const std::string &s) { return parse(s).value_or(MessageId{}); } bool operator==(const MessageId &o) const { return source == o.source && address == o.address; } bool operator!=(const MessageId &o) const { return !(*this == o); } bool operator<(const MessageId &o) const { return std::tie(source, address) < std::tie(o.source, o.address); } diff --git a/openpilot/tools/cabana/tests/test_cabana.cc b/openpilot/tools/cabana/tests/test_cabana.cc index e1c98fcc8e..983a80a459 100644 --- a/openpilot/tools/cabana/tests/test_cabana.cc +++ b/openpilot/tools/cabana/tests/test_cabana.cc @@ -15,6 +15,18 @@ const std::string TEST_RLOG_URL = "https://commadataci.blob.core.windows.net/openpilotci/0c94aa1e1296d7c6/2021-05-05--19-48-37/0/rlog.bz2"; +void test_message_id_parsing() { + for (const auto &text : {"", "1", ":123", "1:", "-1:123", "256:1", "1:100000000", "1:1junk", "1junk:1", "1:1:1"}) { + REQUIRE(!MessageId::parse(text)); + REQUIRE(MessageId::fromString(text) == MessageId{}); + } + const MessageId expected{255, 0xffffffff}; + REQUIRE(MessageId::parse("255:FFFFFFFF") == expected); + REQUIRE(MessageId::parse("255:ffffffff") == expected); + REQUIRE(MessageId::parse(expected.toString()) == expected); + REQUIRE(MessageId::parse("0:0") == MessageId{}); +} + void test_generate_dbc() { std::string fn = std::string(OPENDBC_FILE_PATH) + "/tesla_can.dbc"; DBCFile dbc_origin(fn); @@ -381,6 +393,7 @@ void test_cabana_core() { test_pixel_envelope(); test_format_seconds(); test_to_hex(); + test_message_id_parsing(); test_signal_tooltip(); test_generate_dbc(); test_comment_order(); diff --git a/openpilot/tools/cabana/ui/chart/signalselector.cc b/openpilot/tools/cabana/ui/chart/signalselector.cc index cab1de0816..2733a65f33 100644 --- a/openpilot/tools/cabana/ui/chart/signalselector.cc +++ b/openpilot/tools/cabana/ui/chart/signalselector.cc @@ -11,7 +11,10 @@ #include "tools/cabana/utils/strings.h" SignalSelector::SignalSelector(std::string title) : title_(std::move(title)) { - for (const auto &[id, _] : can->lastMessages()) { + std::set ids; + for (const auto &[id, _] : can->eventsMap()) ids.insert(id); + for (const auto &[id, _] : can->lastMessages()) ids.insert(id); + for (const auto &id : ids) { if (auto m = dbc()->msg(id)) { msgs_combo_.push_back({m->name + " (" + id.toString() + ")", id}); }