OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / tests / tools / codaclient / codaclientapplication.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "codaclientapplication.h"
35
36 #ifdef HAS_SERIALPORT
37 #    include <qextserialport/qextserialport.h>
38 #endif
39
40 #include "tcftrkdevice.h"
41 #include <QtNetwork/QTcpSocket>
42 #include <QtCore/QFile>
43 #include <QtCore/QFileInfo>
44
45 #include <cstdio>
46
47 static const char usageC[] =
48 "\n%1 v0.1 built "__DATE__"\n\n"
49 "Test client for Symbian CODA\n\n"
50 "Usage:\n"
51 "%1 ping            connection   Note: For serial connections ONLY.\n"
52 "%1 launch [-d]     connection binary uid [--] [arguments]\n"
53 "%1 install[-s]     connection remote-sis-file [targetdrive]\n"
54 "%1 put    [c size] connection local-file remote-file\n"
55 "%1 stat            connection remote-file\n\n"
56 "'connection': address[:port] or serial-port\n\n"
57 "Options:\n"
58 "-d            Launch: Launch under Debug control (wait for termination)\n"
59 "-c [size]     Put: Chunk size in KB (default %2KB)\n"
60 "-s            Install: Silent installation\n\n"
61 "Notes:\n"
62 "UIDs take the form '0xfdaa278'. The target directory for sis-files on a\n"
63 "device typically is 'c:\\data'. CODA's default port is %3.\n\n"
64 "Example session:\n"
65 "%1 put     192.168.0.42 test.sis c:\\data\\test.sis\n"
66 "%1 stat    192.168.0.42 c:\\data\\test.sis\n"
67 "%1 install 192.168.0.42 c:\\data\\test.sis c:\n"
68 "%1 launch  192.168.0.42 c:\\sys\\bin\\test.exe  0x34f2b\n"
69 "%1 ping    /dev/ttyUSB1\n";
70
71 static const unsigned short defaultPort = 65029;
72 static const quint64  defaultChunkSize = 10240;
73
74 static inline bool isSerialPort(const QString &address)
75 {
76     return address.startsWith(QLatin1String("/dev"))
77         || address.startsWith(QLatin1String("com"), Qt::CaseInsensitive)
78         || address.startsWith(QLatin1String("tty"), Qt::CaseInsensitive)
79         || address.startsWith(QLatin1Char('\\'));
80 }
81
82 static inline QString fixSlashes(QString s)
83 {
84     s.replace(QLatin1Char('/'), QLatin1Char('\\'));
85     return s;
86 }
87
88 CodaClientApplication::CodaClientApplication(int &argc, char **argv) :
89     QCoreApplication(argc, argv),
90     m_mode(Invalid),
91     m_port(defaultPort),
92     m_launchUID(0),
93     m_launchDebug(false),
94     m_installTargetDrive(QLatin1String("C:")),
95     m_installSilently(false),
96     m_putWriteOk(false),
97     m_statFstatOk(false),
98     m_putLastChunkSize(0),
99     m_putChunkSize(defaultChunkSize),
100     m_verbose(0)
101 {
102     setApplicationName(QLatin1String("codaclient"));
103 }
104
105 CodaClientApplication::~CodaClientApplication()
106 {
107 }
108
109 QString CodaClientApplication::usage()
110 {
111     return QString::fromLatin1(usageC)
112             .arg(QCoreApplication::applicationName())
113             .arg(defaultChunkSize / 1024).arg(defaultPort);
114 }
115
116 static inline CodaClientApplication::Mode modeArg(const QString &a)
117 {
118     if (a == QLatin1String("launch"))
119         return CodaClientApplication::Launch;
120     if (a == QLatin1String("ping"))
121         return CodaClientApplication::Ping;
122     if (a == QLatin1String("install"))
123         return CodaClientApplication::Install;
124     if (a == QLatin1String("put"))
125         return CodaClientApplication::Put;
126     if (a == QLatin1String("stat"))
127         return CodaClientApplication::Stat;
128     return CodaClientApplication::Invalid;
129 }
130
131 bool CodaClientApplication::parseArgument(const QString &a, int argNumber, QString *errorMessage)
132 {
133     switch (argNumber) {
134     case 1:
135         m_mode = modeArg(a);
136         if (m_mode == Invalid) {
137             *errorMessage = QLatin1String("Invalid mode");
138             return false;
139         }
140         return true;
141     case 2:  { // Address/port
142         m_address = a;
143         const int colonPos = m_address.indexOf(':');
144         if (colonPos != -1) {
145             m_port = m_address.mid(colonPos + 1).toUInt();
146             m_address.truncate(colonPos);
147         }
148     }
149         break;
150     case 3:
151         switch (m_mode) {
152         case Launch:
153             m_launchBinary = fixSlashes(a);
154             break;
155         case Install:
156             m_installSisFile = a;
157             break;
158         case Put:
159             m_putLocalFile = a;
160             break;
161         case Stat:
162             m_statRemoteFile = fixSlashes(a);
163             break;
164         default:
165             break;
166         }
167         return true;
168     case 4:
169         switch (m_mode) {
170         case Launch:
171             m_launchUID = a.toUInt(0, 0);
172             if (!m_launchUID) {
173                 *errorMessage = QLatin1String("Invalid UID");
174                 return false;
175             }
176             break;
177         case Install:
178             m_installTargetDrive = a;
179             break;
180         case Put:
181             m_putRemoteFile = fixSlashes(a);
182             break;
183         default:
184             break;
185         }
186         return true;
187
188     default:
189         if (m_mode == Launch)
190             m_launchArgs.push_back(a);
191         break;
192     }
193     return true;
194 }
195
196 CodaClientApplication::ParseArgsResult CodaClientApplication::parseArguments(QString *errorMessage)
197 {
198     int argNumber = 1;
199     const QStringList args = QCoreApplication::arguments();
200     const QStringList::const_iterator cend = args.constEnd();
201     QStringList::const_iterator it = args.constBegin();
202     bool optionsEnd = false;
203     for (++it; it != cend; ++it) {
204         if (!optionsEnd  && *it == QLatin1String("--")) {
205             optionsEnd = true;
206             continue;
207         }
208         if (!optionsEnd &&  it->startsWith(QLatin1Char('-')) && it->size() == 2) {
209             switch (it->at(1).toAscii()) {
210             case 'v':
211                 m_verbose++;
212                 break;
213             case 'd':
214                 m_launchDebug = true;
215                 break;
216             case 's':
217                 m_installSilently = true;
218                 break;
219             case 'c':
220                 if (++it == cend) {
221                     *errorMessage = QString::fromLatin1("Parameter missing for -c");
222                     return ParseArgsError;
223                 }
224                 m_putChunkSize = it->toULongLong() * 1024;
225                 if (!m_putChunkSize) {
226                     *errorMessage = QString::fromLatin1("Invalid chunk size.");
227                     return ParseArgsError;
228                 }
229                 break;
230             default:
231                 *errorMessage = QString::fromLatin1("Invalid option %1").arg(*it);
232                 return ParseArgsError;
233             }
234         } else {
235             if (!parseArgument(*it, argNumber++, errorMessage))
236                 return ParseArgsError;
237         }
238     } //for loop
239     // Basic Check & init
240     switch (m_mode) {
241     case Invalid:
242         return ParseArgsError;
243     case Launch:
244         if (m_address.isEmpty() || !m_launchUID || m_launchBinary.isEmpty()) {
245             *errorMessage = QString::fromLatin1("Not enough parameters for launch.");
246             return ParseInitError;
247         }
248         break;
249     case Ping:
250         if (m_address.isEmpty()) {
251             *errorMessage = QString::fromLatin1("Not enough parameters for ping.");
252             return ParseInitError;
253         }
254         if (!isSerialPort(m_address)) {
255             *errorMessage = QString::fromLatin1("'ping' not supported for TCP/IP.");
256             return ParseInitError;
257         }
258         break;
259     case Install:
260         if (m_address.isEmpty() || m_installSisFile.isEmpty()) {
261             *errorMessage = QString::fromLatin1("Not enough parameters for install.");
262             return ParseInitError;
263         }
264         break;
265     case Put: {
266         if (m_address.isEmpty() || m_putLocalFile.isEmpty() || m_putRemoteFile.isEmpty()) {
267             *errorMessage = QString::fromLatin1("Not enough parameters for put.");
268             return ParseInitError;
269         }
270         const QFileInfo fi(m_putLocalFile);
271         if (!fi.isFile() || !fi.isReadable()) {
272             *errorMessage = QString::fromLatin1("Local file '%1' not readable.").arg(m_putLocalFile);
273             return ParseInitError;
274         }
275     }
276         break;
277     default:
278         break;
279     }
280     return ParseArgsOk;
281 }
282
283 bool CodaClientApplication::start()
284 {
285     m_startTime.start();
286     switch (m_mode) {
287     case Ping:
288         break;
289     case Launch: {
290         const QString args = m_launchArgs.join(QString(QLatin1Char(' ')));
291         std::printf("Launching 0x%x '%s '%s' (debug: %d)\n",
292                     m_launchUID, qPrintable(m_launchBinary),
293                     qPrintable(args), m_launchDebug);
294     }
295         break;
296     case Install:
297         std::printf("Installing '%s' to '%s'\n",
298                     qPrintable(m_installSisFile), qPrintable(m_installTargetDrive));
299         break;
300     case Put:
301         std::printf("Copying '%s' to '%s' in chunks of %lluKB\n",
302                     qPrintable(m_putLocalFile), qPrintable(m_putRemoteFile),
303                     m_putChunkSize / 1024);
304         break;
305     case Stat:
306         std::printf("Retrieving attributes of '%s'\n", qPrintable(m_statRemoteFile));
307         break;
308     case Invalid:
309         break;
310     }
311     // Start connection
312     m_trkDevice.reset(new tcftrk::TcfTrkDevice);
313     m_trkDevice->setVerbose(m_verbose);
314     connect(m_trkDevice.data(), SIGNAL(error(QString)),
315         this, SLOT(slotError(QString)));
316     connect(m_trkDevice.data(), SIGNAL(logMessage(QString)),
317         this, SLOT(slotTrkLogMessage(QString)));
318     connect(m_trkDevice.data(), SIGNAL(tcfEvent(tcftrk::TcfTrkEvent)),
319         this, SLOT(slotTcftrkEvent(tcftrk::TcfTrkEvent)));
320     connect(m_trkDevice.data(), SIGNAL(serialPong(QString)),
321             this, SLOT(slotSerialPong(QString)));
322     if (isSerialPort(m_address)) {
323 #ifdef HAS_SERIALPORT
324         // Serial
325 #ifdef Q_OS_WIN
326         const QString fullPort = QextSerialPort::fullPortNameWin(m_address);
327 #else
328         const QString fullPort = m_address;
329 #endif
330         const QSharedPointer<QextSerialPort>
331                 serialPort(new QextSerialPort(fullPort, QextSerialPort::EventDriven));
332         std::printf("Opening port %s...\n", qPrintable(fullPort));
333
334         // Magic USB serial parameters
335         serialPort->setTimeout(2000);
336         serialPort->setBaudRate(BAUD115200);
337         serialPort->setFlowControl(FLOW_OFF);
338         serialPort->setParity(PAR_NONE);
339         serialPort->setDataBits(DATA_8);
340         serialPort->setStopBits(STOP_1);
341
342         m_trkDevice->setSerialFrame(true);
343         m_trkDevice->setDevice(serialPort); // Grab all data from start
344         if (!serialPort->open(QIODevice::ReadWrite|QIODevice::Unbuffered)) {
345             std::fprintf(stderr, "Cannot open port: %s", qPrintable(serialPort->errorString()));
346             return false;
347         }
348         // Initiate communication
349         m_trkDevice->sendSerialPing(m_mode == Ping);
350         serialPort->flush();
351 #else
352         std::fprintf(stderr, "Not implemented\n");
353         return false;
354 #endif
355     } else {
356         // TCP/IP
357         const QSharedPointer<QTcpSocket> tcfTrkSocket(new QTcpSocket);
358         m_trkDevice->setDevice(tcfTrkSocket);
359         tcfTrkSocket->connectToHost(m_address, m_port);
360         std::printf("Connecting to %s:%hu...\n", qPrintable(m_address), m_port);
361     }
362     return true;
363 }
364
365 void CodaClientApplication::slotError(const QString &e)
366 {
367     std::fprintf(stderr, "Error: %s\n", qPrintable(e));
368     doExit(-1);
369 }
370
371 void CodaClientApplication::slotTrkLogMessage(const QString &m)
372 {
373     printTimeStamp();
374     std::printf("%s\n", qPrintable(m));
375 }
376
377 void CodaClientApplication::handleCreateProcess(const tcftrk::TcfTrkCommandResult &result)
378 {
379     const bool ok = result.type == tcftrk::TcfTrkCommandResult::SuccessReply;
380     if (ok) {
381         printTimeStamp();
382         std::printf("Launch succeeded: %s\n", qPrintable(result.toString()));
383         if (!m_launchDebug)
384             doExit(0);
385     } else {
386         std::fprintf(stderr, "Launch failed: %s\n", qPrintable(result.toString()));
387         doExit(-1);
388     }
389 }
390
391 void CodaClientApplication::handleFileSystemOpen(const tcftrk::TcfTrkCommandResult &result)
392 {
393     if (result.type != tcftrk::TcfTrkCommandResult::SuccessReply) {
394         std::fprintf(stderr, "Open remote file failed: %s\n", qPrintable(result.toString()));
395         doExit(-1);
396         return;
397     }
398
399     if (result.values.size() < 1 || result.values.at(0).data().isEmpty()) {
400         std::fprintf(stderr, "Internal error: No filehandle obtained\n");
401         doExit(-1);
402         return;
403     }
404
405     m_remoteFileHandle = result.values.at(0).data();
406
407     if (m_mode == Stat) {
408         m_trkDevice->sendFileSystemFstatCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemFStat),
409                                                m_remoteFileHandle);
410         return;
411     }
412     // Put.
413     m_putFile.reset(new QFile(m_putLocalFile));
414     if (!m_putFile->open(QIODevice::ReadOnly)) { // Should not fail, was checked before
415         std::fprintf(stderr, "Open local file failed: %s\n", qPrintable(m_putFile->errorString()));
416         doExit(-1);
417         return;
418     }
419     putSendNextChunk();
420 }
421
422 void CodaClientApplication::putSendNextChunk()
423 {
424     // Read and send off next chunk
425     const quint64 pos = m_putFile->pos();
426     const QByteArray data = m_putFile->read(m_putChunkSize);
427     if (data.isEmpty()) {
428         m_putWriteOk = true;
429         closeRemoteFile();
430     } else {
431         m_putLastChunkSize = data.size();
432         printTimeStamp();
433         std::printf("Writing %llu bytes to remote file '%s' at %llu\n",
434                     m_putLastChunkSize,
435                     m_remoteFileHandle.constData(), pos);
436         m_trkDevice->sendFileSystemWriteCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemWrite),
437                                                 m_remoteFileHandle, data, unsigned(pos));
438     }
439 }
440
441 void CodaClientApplication::closeRemoteFile()
442 {
443     m_trkDevice->sendFileSystemCloseCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemClose),
444                                             m_remoteFileHandle);
445 }
446
447 void CodaClientApplication::handleFileSystemWrite(const tcftrk::TcfTrkCommandResult &result)
448 {
449     // Close remote file even if copy fails
450     m_putWriteOk = result;
451     if (!m_putWriteOk)
452         std::fprintf(stderr, "Writing data failed: %s\n", qPrintable(result.toString()));
453     if (!m_putWriteOk || m_putLastChunkSize < m_putChunkSize) {
454         closeRemoteFile();
455     } else {
456         putSendNextChunk();
457     }
458 }
459
460 void CodaClientApplication::handleFileSystemFStat(const tcftrk::TcfTrkCommandResult &result)
461 {
462     m_statFstatOk = result.type == tcftrk::TcfTrkCommandResult::SuccessReply;
463     // Close remote file even if copy fails
464     if (m_statFstatOk) {
465         const tcftrk::TcfTrkStatResponse statr = tcftrk::TcfTrkDevice::parseStat(result);
466         printTimeStamp();
467         std::printf("File: %s\nSize: %llu bytes\nAccessed: %s\nModified: %s\n",
468                     qPrintable(m_statRemoteFile), statr.size,
469                     qPrintable(statr.accessTime.toString(Qt::LocalDate)),
470                     qPrintable(statr.modTime.toString(Qt::LocalDate)));
471     } else {
472         std::fprintf(stderr, "FStat failed: %s\n", qPrintable(result.toString()));
473     }
474     closeRemoteFile();
475 }
476
477 void CodaClientApplication::handleFileSystemClose(const tcftrk::TcfTrkCommandResult &result)
478 {
479     if (result.type == tcftrk::TcfTrkCommandResult::SuccessReply) {
480         printTimeStamp();
481         std::printf("File closed.\n");
482         const bool ok = m_mode == Put ? m_putWriteOk : m_statFstatOk;
483         doExit(ok ? 0 : -1);
484     } else {
485         std::fprintf(stderr, "File close failed: %s\n", qPrintable(result.toString()));
486         doExit(-1);
487     }
488 }
489
490 void CodaClientApplication::handleSymbianInstall(const tcftrk::TcfTrkCommandResult &result)
491 {
492     if (result.type == tcftrk::TcfTrkCommandResult::SuccessReply) {
493         printTimeStamp();
494         std::printf("Installation succeeded\n.");
495         doExit(0);
496     } else {
497         std::fprintf(stderr, "Installation failed: %s\n", qPrintable(result.toString()));
498         doExit(-1);
499     }
500 }
501
502 void CodaClientApplication::slotTcftrkEvent (const tcftrk::TcfTrkEvent &ev)
503 {
504     printTimeStamp();
505     std::printf("Event: %s\n", qPrintable(ev.toString()));
506     switch (ev.type()) {
507     case tcftrk::TcfTrkEvent::LocatorHello: // Commands accepted now
508         switch (m_mode) {
509         case Ping:
510             break;
511         case Launch:
512             m_trkDevice->sendProcessStartCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleCreateProcess),
513                                                  m_launchBinary, m_launchUID, m_launchArgs, QString(), m_launchDebug);
514             break;
515         case Install:
516             if (m_installSilently) {
517                 m_trkDevice->sendSymbianInstallSilentInstallCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleSymbianInstall),
518                                                                     m_installSisFile.toAscii(), m_installTargetDrive.toAscii());
519             } else {
520                 m_trkDevice->sendSymbianInstallUIInstallCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleSymbianInstall),
521                                                                 m_installSisFile.toAscii());
522             }
523             break;
524         case Put: {
525             const unsigned flags =
526                     tcftrk::TcfTrkDevice::FileSystem_TCF_O_WRITE
527                     |tcftrk::TcfTrkDevice::FileSystem_TCF_O_CREAT
528                     |tcftrk::TcfTrkDevice::FileSystem_TCF_O_TRUNC;
529             m_putWriteOk = false;
530             m_trkDevice->sendFileSystemOpenCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemOpen),
531                                                    m_putRemoteFile.toAscii(), flags);
532 }
533             break;
534         case Stat: {
535             const unsigned flags = tcftrk::TcfTrkDevice::FileSystem_TCF_O_READ;
536             m_statFstatOk = false;
537             m_trkDevice->sendFileSystemOpenCommand(tcftrk::TcfTrkCallback(this, &CodaClientApplication::handleFileSystemOpen),
538                                                    m_statRemoteFile.toAscii(), flags);
539 }
540             break;
541         case Invalid:
542             break;
543         }
544         break;
545     case tcftrk::TcfTrkEvent::RunControlModuleLoadSuspended:  {
546         // Debug mode start: Continue:
547         const tcftrk::TcfTrkRunControlModuleLoadContextSuspendedEvent &me =
548                 static_cast<const tcftrk::TcfTrkRunControlModuleLoadContextSuspendedEvent &>(ev);
549         if (me.info().requireResume) {
550             printTimeStamp();
551             std::printf("Continuing...\n");
552             m_trkDevice->sendRunControlResumeCommand(tcftrk::TcfTrkCallback(), me.id());
553         }
554     }
555         break;
556     case tcftrk::TcfTrkEvent::RunControlContextRemoved: // App terminated in debug mode
557         doExit(0);
558         break;
559     default:
560         break;
561     }
562 }
563
564 void CodaClientApplication::slotSerialPong(const QString &v)
565 {
566     printTimeStamp();
567     std::printf("Pong from '%s'\n", qPrintable(v));
568     if (m_mode == Ping)
569         doExit(0);
570 }
571
572 void CodaClientApplication::doExit(int ex)
573 {
574     if (!m_trkDevice.isNull()) {
575         const QSharedPointer<QIODevice> dev = m_trkDevice->device();
576         if (!dev.isNull()) {
577             if (QAbstractSocket *socket = qobject_cast<QAbstractSocket *>(dev.data())) {
578                 if (socket->state() == QAbstractSocket::ConnectedState)
579                     socket->disconnectFromHost();
580             } else {
581                 if (dev->isOpen())
582                     dev->close();
583             }
584         }
585     }
586     printTimeStamp();
587     std::printf("Exiting (%d)\n", ex);
588     exit(ex);
589 }
590
591 void CodaClientApplication::printTimeStamp()
592 {
593     const int elapsedMS = m_startTime.elapsed();
594     const int secs = elapsedMS / 1000;
595     const int msecs = elapsedMS % 1000;
596     std::printf("%4d.%03ds: ", secs, msecs);
597 }