cabana: de-QT streams (#38718)

This commit is contained in:
Trey Moen
2026-08-28 10:18:10 -07:00
committed by GitHub
parent 46f612224c
commit 5419f57b3a
9 changed files with 50 additions and 36 deletions
+2 -1
View File
@@ -148,7 +148,7 @@ int main(int argc, char *argv[]) {
if (args.msgq) {
stream = new DeviceStream();
} else if (!args.zmq.empty()) {
stream = new DeviceStream(QString::fromStdString(args.zmq));
stream = new DeviceStream(args.zmq);
} else if (args.panda || !args.panda_serial.empty()) {
try {
stream = new PandaStream({.serial = args.panda_serial});
@@ -175,6 +175,7 @@ int main(int argc, char *argv[]) {
}
if (!route.isEmpty()) {
auto replay_stream = std::make_unique<ReplayStream>();
Connection err = replay_stream->error.connect([](const std::string &msg) { fprintf(stderr, "%s\n", msg.c_str()); });
if (!replay_stream->loadRoute(route.toStdString(), args.data_dir, replay_flags, args.auto_source)) {
return 0;
}
+3
View File
@@ -358,6 +358,9 @@ void MainWindow::startStream(AbstractStream *stream, QString dbc_file) {
delete video_splitter;
can = stream; // take ownership
stream_connections_.push_back(can->error.connect([this](const std::string &msg) {
QMessageBox::warning(this, tr("Error"), QString::fromStdString(msg));
}));
can->start();
loadFile(dbc_file);
@@ -64,6 +64,7 @@ public:
Observable<const std::optional<std::pair<double, double>> &> timeRangeChanged;
Observable<const MessageEventsMap &> eventsMerged;
Observable<const std::set<MessageId> *, bool> msgsReceived;
Observable<const std::string &> error;
SourceSet sources;
+10 -16
View File
@@ -9,17 +9,16 @@
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <unistd.h>
#include <sys/wait.h>
#include "openpilot/cereal/services.h"
#include <QCoreApplication>
#include <QMessageBox>
#include "tools/cabana/utils/util.h"
// DeviceStream
DeviceStream::DeviceStream(QString address) : zmq_address(address) {
DeviceStream::DeviceStream(std::string address) : zmq_address(std::move(address)) {
}
DeviceStream::~DeviceStream() {
@@ -46,19 +45,16 @@ void DeviceStream::stopBridge() {
}
void DeviceStream::start() {
if (!zmq_address.isEmpty()) {
if (!zmq_address.empty()) {
stopBridge();
const std::string path = (std::filesystem::path(QCoreApplication::applicationDirPath().toStdString()) /
"../../cereal/messaging/bridge").lexically_normal().string();
const std::string addr = zmq_address.toStdString();
const std::string path = (executableDir() / "../../cereal/messaging/bridge").lexically_normal().string();
const char *can_filter = "/\"can/\"";
// Self-pipe: write end is CLOEXEC so it closes on successful exec. If exec
// fails, the child writes errno and the parent aborts stream start.
int err_pipe[2] = {-1, -1};
if (::pipe(err_pipe) != 0) {
QMessageBox::warning(nullptr, "Error",
QString("Failed to start bridge: %1").arg(QString::fromLocal8Bit(strerror(errno))));
error(std::string("Failed to start bridge: ") + strerror(errno));
return;
}
@@ -66,7 +62,7 @@ void DeviceStream::start() {
if (pid == 0) {
::close(err_pipe[0]);
::fcntl(err_pipe[1], F_SETFD, FD_CLOEXEC);
execl(path.c_str(), path.c_str(), addr.c_str(), can_filter, static_cast<char *>(nullptr));
execl(path.c_str(), path.c_str(), zmq_address.c_str(), can_filter, static_cast<char *>(nullptr));
const int err = errno;
(void)!::write(err_pipe[1], &err, sizeof(err));
_exit(127);
@@ -75,8 +71,7 @@ void DeviceStream::start() {
::close(err_pipe[1]);
if (pid < 0) {
::close(err_pipe[0]);
QMessageBox::warning(nullptr, "Error",
QString("Failed to start bridge: %1").arg(QString::fromLocal8Bit(strerror(errno))));
error(std::string("Failed to start bridge: ") + strerror(errno));
return;
}
@@ -87,8 +82,7 @@ void DeviceStream::start() {
// Child failed to exec; reap and surface the error.
int status = 0;
::waitpid(pid, &status, 0);
QMessageBox::warning(nullptr, "Error",
QString("Failed to start bridge: %1").arg(QString::fromLocal8Bit(strerror(exec_errno))));
error(std::string("Failed to start bridge: ") + strerror(exec_errno));
return;
}
@@ -99,7 +93,7 @@ void DeviceStream::start() {
}
void DeviceStream::streamThread() {
zmq_address.isEmpty() ? unsetenv("ZMQ") : setenv("ZMQ", "1", 1);
zmq_address.empty() ? unsetenv("ZMQ") : setenv("ZMQ", "1", 1);
std::unique_ptr<Context> context(Context::create());
std::unique_ptr<SubSocket> sock(SubSocket::create(context.get(), "can", "127.0.0.1", false, true, services.at("can").queue_size));
@@ -2,14 +2,15 @@
#include "tools/cabana/streams/livestream.h"
#include <string>
#include <sys/types.h>
class DeviceStream : public LiveStream {
public:
DeviceStream(QString address = {});
DeviceStream(std::string address = {});
~DeviceStream();
inline std::string routeName() const override {
return "Live Streaming From " + (zmq_address.isEmpty() ? std::string("127.0.0.1") : zmq_address.toStdString());
return "Live Streaming From " + (zmq_address.empty() ? std::string("127.0.0.1") : zmq_address);
}
protected:
@@ -17,5 +18,5 @@ protected:
void streamThread() override;
void stopBridge();
pid_t bridge_pid = -1;
const QString zmq_address;
const std::string zmq_address;
};
+8 -10
View File
@@ -1,6 +1,6 @@
#include "tools/cabana/streams/replaystream.h"
#include <QMessageBox>
#include <string>
#include "common/timing.h"
#include "common/util.h"
@@ -59,27 +59,25 @@ bool ReplayStream::loadRoute(const std::string &route, const std::string &data_d
bool success = replay->load();
if (!success) {
std::string message;
if (replay->lastRouteError() == RouteLoadError::Unauthorized) {
auto auth_content = util::read_file(util::getenv("HOME") + "/.comma/auth.json");
QString message;
if (auth_content.empty()) {
message = "Authentication Required. Please run the following command to authenticate:\n\n"
"python3 openpilot/tools/lib/auth.py\n\n"
"This will grant access to routes from your comma account.";
} else {
message = QString("Access Denied. You do not have permission to access route:\n\n%1\n\n"
"This is likely a private route.").arg(QString::fromStdString(route));
message = "Access Denied. You do not have permission to access route:\n\n" + route + "\n\n"
"This is likely a private route.";
}
QMessageBox::warning(nullptr, "Access Denied", message);
} else if (replay->lastRouteError() == RouteLoadError::NetworkError) {
QMessageBox::warning(nullptr, "Network Error",
QString("Unable to load the route:\n\n %1.\n\nPlease check your network connection and try again.").arg(QString::fromStdString(route)));
message = "Unable to load the route:\n\n " + route + ".\n\nPlease check your network connection and try again.";
} else if (replay->lastRouteError() == RouteLoadError::FileNotFound) {
QMessageBox::warning(nullptr, "Route Not Found",
QString("The specified route could not be found:\n\n %1.\n\nPlease check the route name and try again.").arg(QString::fromStdString(route)));
message = "The specified route could not be found:\n\n " + route + ".\n\nPlease check the route name and try again.";
} else {
QMessageBox::warning(nullptr, "Route Load Failed", QString("Failed to load route: '%1'").arg(QString::fromStdString(route)));
message = "Failed to load route: '" + route + "'";
}
error(message);
}
return success;
}
+4 -1
View File
@@ -63,6 +63,9 @@ AbstractStream *OpenReplayWidget::open() {
QMessageBox::warning(nullptr, tr("Warning"), tr("Invalid route format: '%1'").arg(route));
} else {
auto replay_stream = std::make_unique<ReplayStream>();
Connection err = replay_stream->error.connect([](const std::string &msg) {
QMessageBox::warning(nullptr, tr("Error"), QString::fromStdString(msg));
});
uint32_t flags = REPLAY_FLAG_NONE;
if (cameras[1]->isChecked()) flags |= REPLAY_FLAG_CABIN_CAMERA;
if (cameras[2]->isChecked()) flags |= REPLAY_FLAG_WIDE_ROAD;
@@ -211,7 +214,7 @@ OpenDeviceWidget::OpenDeviceWidget(QWidget *parent) : AbstractOpenStreamWidget(p
}
AbstractStream *OpenDeviceWidget::open() {
QString ip = ip_address->text().isEmpty() ? "127.0.0.1" : ip_address->text();
std::string ip = ip_address->text().isEmpty() ? "127.0.0.1" : ip_address->text().toStdString();
bool msgq = group->checkedId() == 0;
return new DeviceStream(msgq ? "" : ip);
}
+17 -5
View File
@@ -16,6 +16,9 @@
#include <sys/socket.h>
#include <sys/wait.h>
#include <unistd.h>
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
#include <QColor>
#include <QFontDatabase>
@@ -454,27 +457,36 @@ void sigTermHandler(int s) {
qApp->quit();
}
std::filesystem::path executableDir() {
#ifdef __APPLE__
char buf[PATH_MAX];
uint32_t size = sizeof(buf);
if (_NSGetExecutablePath(buf, &size) != 0) return {};
std::error_code ec;
auto path = std::filesystem::canonical(buf, ec);
return (ec ? std::filesystem::path(buf) : path).parent_path();
#else
return std::filesystem::path(util::readlink("/proc/self/exe")).parent_path();
#endif
}
void initApp(int argc, char *argv[], bool disable_hidpi) {
// setup signal handlers to exit gracefully
std::signal(SIGINT, sigTermHandler);
std::signal(SIGTERM, sigTermHandler);
std::filesystem::path app_dir;
#ifdef __APPLE__
// Get the devicePixelRatio, and scale accordingly to maintain 1:1 rendering
QApplication tmp(argc, argv);
app_dir = QCoreApplication::applicationDirPath().toStdString();
if (disable_hidpi) {
qputenv("QT_SCALE_FACTOR", QString::number(1.0 / tmp.devicePixelRatio()).toLocal8Bit());
}
#else
app_dir = std::filesystem::path(util::readlink("/proc/self/exe")).parent_path();
#endif
qputenv("QT_DBL_CLICK_DIST", "150");
// ensure the current dir matches the exectuable's directory
std::error_code ec;
std::filesystem::current_path(app_dir, ec);
std::filesystem::current_path(executableDir(), ec);
}
// embedded at build time from the bootstrap_icons package (see SConscript)
+1
View File
@@ -219,5 +219,6 @@ private:
};
int num_decimals(double num);
std::filesystem::path executableDir();
void initApp(int argc, char *argv[], bool disable_hidpi = true);
QPixmap bootstrapPixmap(const QString &id);