1+ #include < cstdio>
2+ #include < cstring>
3+ #include < memory>
4+ #include < string>
5+
16#include < QApplication>
2- #include < QCommandLineParser>
37
48#include " tools/cabana/mainwin.h"
59#include " tools/cabana/streams/devicestream.h"
913#include " tools/cabana/streams/socketcanstream.h"
1014#endif
1115
16+ namespace {
17+
18+ struct CabanaArgs {
19+ bool demo = false ;
20+ bool auto_source = false ;
21+ bool qcam = false ;
22+ bool ecam = false ;
23+ bool dcam = false ;
24+ bool msgq = false ;
25+ bool panda = false ;
26+ bool no_vipc = false ;
27+ std::string panda_serial;
28+ std::string socketcan;
29+ std::string zmq;
30+ std::string data_dir;
31+ std::string dbc;
32+ std::string route;
33+ };
34+
35+ void printUsage (const char *argv0) {
36+ fprintf (stderr,
37+ " Usage: %s [options] [route]\n "
38+ " \n "
39+ " route the drive to replay. find your drives at connect.comma.ai\n "
40+ " \n "
41+ " Options:\n "
42+ " --help show this help\n "
43+ " --demo use a demo route instead of providing your own\n "
44+ " --auto Auto load the route from the best available source (no video):\n "
45+ " internal, openpilotci, comma_api, car_segments, testing_closet\n "
46+ " --qcam load qcamera\n "
47+ " --ecam load wide road camera\n "
48+ " --dcam load driver camera\n "
49+ " --msgq read can messages from the msgq\n "
50+ " --panda read can messages from panda\n "
51+ " --panda-serial <serial> read can messages from panda with given serial\n "
52+ #ifdef __linux__
53+ " --socketcan <device> read can messages from given SocketCAN device\n "
54+ #endif
55+ " --zmq <ip-address> read can messages from zmq at the specified ip-address\n "
56+ " --data_dir <dir> local directory with routes\n "
57+ " --no-vipc do not output video\n "
58+ " --dbc <file> dbc file to open\n " ,
59+ argv0);
60+ }
61+
62+ // Returns true if value was consumed from argv[i+1].
63+ bool takeValue (int argc, char *argv[], int &i, std::string &out) {
64+ if (i + 1 >= argc) {
65+ fprintf (stderr, " error: %s requires a value\n " , argv[i]);
66+ return false ;
67+ }
68+ out = argv[++i];
69+ return true ;
70+ }
71+
72+ // Returns 0 to continue, or a process exit code (0 for --help, 1 for errors).
73+ int parseArgs (int argc, char *argv[], CabanaArgs &args, bool &ok) {
74+ ok = false ;
75+ for (int i = 1 ; i < argc; ++i) {
76+ const char *a = argv[i];
77+ if (std::strcmp (a, " --help" ) == 0 || std::strcmp (a, " -h" ) == 0 ) {
78+ printUsage (argv[0 ]);
79+ return 0 ;
80+ } else if (std::strcmp (a, " --demo" ) == 0 ) {
81+ args.demo = true ;
82+ } else if (std::strcmp (a, " --auto" ) == 0 ) {
83+ args.auto_source = true ;
84+ } else if (std::strcmp (a, " --qcam" ) == 0 ) {
85+ args.qcam = true ;
86+ } else if (std::strcmp (a, " --ecam" ) == 0 ) {
87+ args.ecam = true ;
88+ } else if (std::strcmp (a, " --dcam" ) == 0 ) {
89+ args.dcam = true ;
90+ } else if (std::strcmp (a, " --msgq" ) == 0 ) {
91+ args.msgq = true ;
92+ } else if (std::strcmp (a, " --panda" ) == 0 ) {
93+ args.panda = true ;
94+ } else if (std::strcmp (a, " --panda-serial" ) == 0 ) {
95+ if (!takeValue (argc, argv, i, args.panda_serial )) return 1 ;
96+ args.panda = true ;
97+ } else if (std::strcmp (a, " --socketcan" ) == 0 ) {
98+ if (!takeValue (argc, argv, i, args.socketcan )) return 1 ;
99+ #ifdef __linux__
100+ #else
101+ fprintf (stderr, " error: --socketcan is only supported on Linux\n " );
102+ return 1 ;
103+ #endif
104+ } else if (std::strcmp (a, " --zmq" ) == 0 ) {
105+ if (!takeValue (argc, argv, i, args.zmq )) return 1 ;
106+ } else if (std::strcmp (a, " --data_dir" ) == 0 ) {
107+ if (!takeValue (argc, argv, i, args.data_dir )) return 1 ;
108+ } else if (std::strcmp (a, " --no-vipc" ) == 0 ) {
109+ args.no_vipc = true ;
110+ } else if (std::strcmp (a, " --dbc" ) == 0 ) {
111+ if (!takeValue (argc, argv, i, args.dbc )) return 1 ;
112+ } else if (a[0 ] == ' -' ) {
113+ fprintf (stderr, " error: unknown option %s\n " , a);
114+ printUsage (argv[0 ]);
115+ return 1 ;
116+ } else if (args.route .empty ()) {
117+ args.route = a;
118+ } else {
119+ fprintf (stderr, " error: unexpected argument %s\n " , a);
120+ printUsage (argv[0 ]);
121+ return 1 ;
122+ }
123+ }
124+ ok = true ;
125+ return 0 ;
126+ }
127+
128+ } // namespace
129+
12130int main (int argc, char *argv[]) {
13131 QCoreApplication::setApplicationName (" Cabana" );
14132 QCoreApplication::setAttribute (Qt::AA_ShareOpenGLContexts);
@@ -20,69 +138,51 @@ int main(int argc, char *argv[]) {
20138 UnixSignalHandler signalHandler;
21139 utils::setTheme (settings.theme );
22140
23- QCommandLineParser cmd_parser;
24- cmd_parser.addHelpOption ();
25- cmd_parser.addPositionalArgument (" route" , " the drive to replay. find your drives at connect.comma.ai" );
26- cmd_parser.addOption ({" demo" , " use a demo route instead of providing your own" });
27- cmd_parser.addOption ({" auto" , " Auto load the route from the best available source (no video): internal, openpilotci, comma_api, car_segments, testing_closet" });
28- cmd_parser.addOption ({" qcam" , " load qcamera" });
29- cmd_parser.addOption ({" ecam" , " load wide road camera" });
30- cmd_parser.addOption ({" dcam" , " load driver camera" });
31- cmd_parser.addOption ({" msgq" , " read can messages from the msgq" });
32- cmd_parser.addOption ({" panda" , " read can messages from panda" });
33- cmd_parser.addOption ({" panda-serial" , " read can messages from panda with given serial" , " panda-serial" });
34- #ifdef __linux__
35- if (SocketCanStream::available ()) {
36- cmd_parser.addOption ({" socketcan" , " read can messages from given SocketCAN device" , " socketcan" });
141+ CabanaArgs args;
142+ bool args_ok = false ;
143+ if (const int code = parseArgs (argc, argv, args, args_ok); !args_ok) {
144+ return code;
37145 }
38- #endif
39- cmd_parser.addOption ({" zmq" , " read can messages from zmq at the specified ip-address" , " ip-address" });
40- cmd_parser.addOption ({" data_dir" , " local directory with routes" , " data_dir" });
41- cmd_parser.addOption ({" no-vipc" , " do not output video" });
42- cmd_parser.addOption ({" dbc" , " dbc file to open" , " dbc" });
43- cmd_parser.process (app);
44146
45147 AbstractStream *stream = nullptr ;
46148
47- if (cmd_parser. isSet ( " msgq" ) ) {
149+ if (args. msgq ) {
48150 stream = new DeviceStream (&app);
49- } else if (cmd_parser. isSet ( " zmq" )) {
50- stream = new DeviceStream (&app, cmd_parser. value ( " zmq" ));
51- } else if (cmd_parser. isSet ( " panda" ) || cmd_parser. isSet ( " panda-serial " )) {
151+ } else if (!args. zmq . empty ( )) {
152+ stream = new DeviceStream (&app, QString::fromStdString (args. zmq ));
153+ } else if (args. panda || !args. panda_serial . empty ( )) {
52154 try {
53- stream = new PandaStream (&app, {.serial = cmd_parser. value ( " panda-serial " ). toStdString () });
155+ stream = new PandaStream (&app, {.serial = args. panda_serial });
54156 } catch (std::exception &e) {
55- qWarning () << e.what ();
157+ fprintf (stderr, " %s \n " , e.what () );
56158 return 0 ;
57159 }
58160#ifdef __linux__
59- } else if (SocketCanStream::available () && cmd_parser. isSet ( " socketcan" )) {
60- stream = new SocketCanStream (&app, {.device = cmd_parser. value ( " socketcan" ). toStdString () });
161+ } else if (SocketCanStream::available () && !args. socketcan . empty ( )) {
162+ stream = new SocketCanStream (&app, {.device = args. socketcan });
61163#endif
62164 } else {
63165 uint32_t replay_flags = REPLAY_FLAG_NONE ;
64- if (cmd_parser. isSet ( " ecam" ) ) replay_flags |= REPLAY_FLAG_ECAM ;
65- if (cmd_parser. isSet ( " qcam" ) ) replay_flags |= REPLAY_FLAG_QCAMERA ;
66- if (cmd_parser. isSet ( " dcam" ) ) replay_flags |= REPLAY_FLAG_DCAM ;
67- if (cmd_parser. isSet ( " no-vipc " ) ) replay_flags |= REPLAY_FLAG_NO_VIPC ;
166+ if (args. ecam ) replay_flags |= REPLAY_FLAG_ECAM ;
167+ if (args. qcam ) replay_flags |= REPLAY_FLAG_QCAMERA ;
168+ if (args. dcam ) replay_flags |= REPLAY_FLAG_DCAM ;
169+ if (args. no_vipc ) replay_flags |= REPLAY_FLAG_NO_VIPC ;
68170
69- const QStringList args = cmd_parser.positionalArguments ();
70171 QString route;
71- if (args.size () > 0 ) {
72- route = args.first ( );
73- } else if (cmd_parser. isSet ( " demo" ) ) {
172+ if (! args.route . empty () ) {
173+ route = QString::fromStdString ( args.route );
174+ } else if (args. demo ) {
74175 route = DEMO_ROUTE ;
75176 }
76177 if (!route.isEmpty ()) {
77178 auto replay_stream = std::make_unique<ReplayStream>(&app);
78- bool auto_source = cmd_parser.isSet (" auto" );
79- if (!replay_stream->loadRoute (route.toStdString (), cmd_parser.value (" data_dir" ).toStdString (), replay_flags, auto_source)) {
179+ if (!replay_stream->loadRoute (route.toStdString (), args.data_dir , replay_flags, args.auto_source )) {
80180 return 0 ;
81181 }
82182 stream = replay_stream.release ();
83183 }
84184 }
85185
86- MainWindow w (stream, cmd_parser. value ( " dbc" ));
186+ MainWindow w (stream, QString::fromStdString (args. dbc ));
87187 return app.exec ();
88188}
0 commit comments