#include #include #include #include #include #include "tools/cabana/mainwin.h" #include "tools/cabana/streams/devicestream.h" #include "tools/cabana/streams/pandastream.h" #include "tools/cabana/streams/replaystream.h" #ifdef __linux__ #include "tools/cabana/streams/socketcanstream.h" #endif namespace { struct CabanaArgs { bool demo = false; bool auto_source = false; bool qcam = false; bool ecam = false; bool dcam = false; bool msgq = false; bool panda = false; bool no_vipc = false; std::string panda_serial; std::string socketcan; std::string zmq; std::string bridge; std::string data_dir; std::string dbc; std::string route; }; void printUsage(const char *argv0) { fprintf(stderr, "Usage: %s [options] [route]\n" "\n" " route the drive to replay. find your drives at konn3kt.com\n" "\n" "Options:\n" " --help show this help\n" " --demo use a demo route instead of providing your own\n" " --auto Auto load the route from the best available source (no video):\n" " internal, openpilotci, comma_api, car_segments, testing_closet\n" " --qcam load qcamera\n" " --ecam load wide road camera\n" " --dcam load driver camera\n" " --msgq read can messages from the msgq\n" " --panda read can messages from panda\n" " --panda-serial read can messages from panda with given serial\n" #ifdef __linux__ " --socketcan read can messages from given SocketCAN device\n" #endif " --zmq subscribe to a zmq 'can' publisher at the specified ip-address\n" " (a device running cereal/messaging/bridge, or\n" " tools/cabana/konn3kt_canproxy.py on 127.0.0.1)\n" " --bridge run cereal/messaging/bridge locally against the device\n" " --data_dir local directory with routes\n" " --no-vipc do not output video\n" " --dbc dbc file to open\n", argv0); } // Returns true if value was consumed from argv[i+1]. bool takeValue(int argc, char *argv[], int &i, std::string &out) { if (i + 1 >= argc) { fprintf(stderr, "error: %s requires a value\n", argv[i]); return false; } out = argv[++i]; return true; } // Returns 0 to continue, or a process exit code (0 for --help, 1 for errors). int parseArgs(int argc, char *argv[], CabanaArgs &args, bool &ok) { ok = false; for (int i = 1; i < argc; ++i) { const char *a = argv[i]; if (std::strcmp(a, "--help") == 0 || std::strcmp(a, "-h") == 0) { printUsage(argv[0]); return 0; } else if (std::strcmp(a, "--demo") == 0) { args.demo = true; } else if (std::strcmp(a, "--auto") == 0) { args.auto_source = true; } else if (std::strcmp(a, "--qcam") == 0) { args.qcam = true; } else if (std::strcmp(a, "--ecam") == 0) { args.ecam = true; } else if (std::strcmp(a, "--dcam") == 0) { args.dcam = true; } else if (std::strcmp(a, "--msgq") == 0) { args.msgq = true; } else if (std::strcmp(a, "--panda") == 0) { args.panda = true; } else if (std::strcmp(a, "--panda-serial") == 0) { if (!takeValue(argc, argv, i, args.panda_serial)) return 1; args.panda = true; } else if (std::strcmp(a, "--socketcan") == 0) { if (!takeValue(argc, argv, i, args.socketcan)) return 1; #ifdef __linux__ #else fprintf(stderr, "error: --socketcan is only supported on Linux\n"); return 1; #endif } else if (std::strcmp(a, "--zmq") == 0) { if (!takeValue(argc, argv, i, args.zmq)) return 1; } else if (std::strcmp(a, "--bridge") == 0) { if (!takeValue(argc, argv, i, args.bridge)) return 1; } else if (std::strcmp(a, "--data_dir") == 0) { if (!takeValue(argc, argv, i, args.data_dir)) return 1; } else if (std::strcmp(a, "--no-vipc") == 0) { args.no_vipc = true; } else if (std::strcmp(a, "--dbc") == 0) { if (!takeValue(argc, argv, i, args.dbc)) return 1; } else if (a[0] == '-') { fprintf(stderr, "error: unknown option %s\n", a); printUsage(argv[0]); return 1; } else if (args.route.empty()) { args.route = a; } else { fprintf(stderr, "error: unexpected argument %s\n", a); printUsage(argv[0]); return 1; } } ok = true; return 0; } } // namespace int main(int argc, char *argv[]) { QCoreApplication::setApplicationName("Cabana"); initApp(argc, argv, false); QApplication app(argc, argv); app.setApplicationDisplayName("Cabana"); //app.setWindowIcon(QIcon(":cabana-icon.png")); // TODO: do this in imgui UnixSignalHandler signalHandler; utils::setTheme(settings.theme); CabanaArgs args; bool args_ok = false; if (const int code = parseArgs(argc, argv, args, args_ok); !args_ok) { return code; } AbstractStream *stream = nullptr; if (args.msgq) { stream = new DeviceStream(&app, DeviceStream::Mode::Msgq); } else if (!args.zmq.empty()) { stream = new DeviceStream(&app, DeviceStream::Mode::Zmq, QString::fromStdString(args.zmq)); } else if (!args.bridge.empty()) { stream = new DeviceStream(&app, DeviceStream::Mode::Bridge, QString::fromStdString(args.bridge)); } else if (args.panda || !args.panda_serial.empty()) { try { stream = new PandaStream(&app, {.serial = args.panda_serial}); } catch (std::exception &e) { fprintf(stderr, "%s\n", e.what()); return 0; } #ifdef __linux__ } else if (SocketCanStream::available() && !args.socketcan.empty()) { stream = new SocketCanStream(&app, {.device = args.socketcan}); #endif } else { uint32_t replay_flags = REPLAY_FLAG_NONE; if (args.ecam) replay_flags |= REPLAY_FLAG_ECAM; if (args.qcam) replay_flags |= REPLAY_FLAG_QCAMERA; if (args.dcam) replay_flags |= REPLAY_FLAG_DCAM; if (args.no_vipc) replay_flags |= REPLAY_FLAG_NO_VIPC; QString route; if (!args.route.empty()) { route = QString::fromStdString(args.route); } else if (args.demo) { route = DEMO_ROUTE; } if (!route.isEmpty()) { auto replay_stream = std::make_unique(&app); if (!replay_stream->loadRoute(route.toStdString(), args.data_dir, replay_flags, args.auto_source)) { return 0; } stream = replay_stream.release(); } } MainWindow w(stream, QString::fromStdString(args.dbc)); return app.exec(); }