tools/runonphone/symbianutils/tcftrkdevice.cpp
changeset 33 3e2da88830cd
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the tools applications of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    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 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "tcftrkdevice.h"
       
    43 #include "json.h"
       
    44 
       
    45 #include <QtNetwork/QAbstractSocket>
       
    46 #include <QtCore/QDebug>
       
    47 #include <QtCore/QVector>
       
    48 #include <QtCore/QQueue>
       
    49 #include <QtCore/QTextStream>
       
    50 #include <QtCore/QDateTime>
       
    51 #include <QtCore/QFileInfo>
       
    52 
       
    53 enum { debug = 0 };
       
    54 
       
    55 static const char messageTerminatorC[] = "\003\001";
       
    56 
       
    57 namespace tcftrk {
       
    58 // ------------- TcfTrkCommandError
       
    59 
       
    60 TcfTrkCommandError::TcfTrkCommandError() : timeMS(0), code(0), alternativeCode(0)
       
    61 {
       
    62 }
       
    63 
       
    64 void TcfTrkCommandError::clear()
       
    65 {
       
    66     timeMS = 0;
       
    67     code = alternativeCode = 0;
       
    68     format.clear();
       
    69     alternativeOrganization.clear();
       
    70 }
       
    71 
       
    72 void TcfTrkCommandError::write(QTextStream &str) const
       
    73 {
       
    74     if (timeMS) {
       
    75         const QDateTime time(QDate(1970, 1, 1));
       
    76         str << time.addMSecs(timeMS).toString(Qt::ISODate) << ": Error code: " << code
       
    77                 << " '" << format << '\'';
       
    78         if (!alternativeOrganization.isEmpty())
       
    79             str << " ('" << alternativeOrganization << "', code: " << alternativeCode << ')';
       
    80     } else{
       
    81         str << "<No error>";
       
    82     }
       
    83 }
       
    84 
       
    85 QString TcfTrkCommandError::toString() const
       
    86 {
       
    87     QString rc;
       
    88     QTextStream str(&rc);
       
    89     write(str);
       
    90     return rc;
       
    91 }
       
    92 
       
    93 /* {"Time":1277459762255,"Code":1,"AltCode":-6,"AltOrg":"POSIX","Format":"Unknown error: -6"} */
       
    94 bool TcfTrkCommandError::parse(const QVector<JsonValue> &values)
       
    95 {
       
    96     // Parse an arbitrary hash (that could as well be a command response)
       
    97     // and check for error elements.
       
    98     unsigned errorKeyCount = 0;
       
    99     clear();
       
   100     do {
       
   101         if (values.isEmpty() || values.front().type() != JsonValue::Object)
       
   102             break;
       
   103         foreach (const JsonValue &c, values.front().children()) {
       
   104             if (c.name() == "Time") {
       
   105                 timeMS = c.data().toULongLong();
       
   106                 errorKeyCount++;
       
   107             } else if (c.name() == "Code") {
       
   108                 code = c.data().toInt();
       
   109                 errorKeyCount++;
       
   110             } else if (c.name() == "Format") {
       
   111                 format = c.data();
       
   112                 errorKeyCount++;
       
   113             } else if (c.name() == "AltCode") {
       
   114                 alternativeCode = c.data().toInt();
       
   115                 errorKeyCount++;
       
   116             } else if (c.name() == "AltOrg") {
       
   117                 alternativeOrganization = c.data();
       
   118                 errorKeyCount++;
       
   119             }
       
   120         }
       
   121     } while (false);
       
   122     const bool errorFound = errorKeyCount >= 2u; // Should be at least 'Time', 'Code'.
       
   123     if (!errorFound)
       
   124         clear();
       
   125     if (debug) {
       
   126         qDebug() << "TcfTrkCommandError::parse: Found error: " << errorFound;
       
   127         if (!values.isEmpty())
       
   128             qDebug() << values.front().toString();
       
   129     }
       
   130     return errorFound;
       
   131 }
       
   132 
       
   133 // ------------ TcfTrkCommandResult
       
   134 
       
   135 TcfTrkCommandResult::TcfTrkCommandResult(Type t) :
       
   136     type(t), service(LocatorService)
       
   137 {
       
   138 }
       
   139 
       
   140 TcfTrkCommandResult::TcfTrkCommandResult(char typeChar, Services s,
       
   141                                          const QByteArray &r,
       
   142                                          const QVector<JsonValue> &v,
       
   143                                          const QVariant &ck) :
       
   144     type(FailReply), service(s), request(r), values(v), cookie(ck)
       
   145 {
       
   146     switch (typeChar) {
       
   147     case 'N':
       
   148         type = FailReply;
       
   149         break;
       
   150     case 'P':
       
   151         type = ProgressReply;
       
   152         break;
       
   153     case 'R':
       
   154         type = commandError.parse(values) ? CommandErrorReply : SuccessReply;
       
   155         break;
       
   156     default:
       
   157         qWarning("Unknown TCF reply type '%c'", typeChar);
       
   158     }
       
   159 }
       
   160 
       
   161 QString TcfTrkCommandResult::errorString() const
       
   162 {
       
   163     QString rc;
       
   164     QTextStream str(&rc);
       
   165 
       
   166     switch (type) {
       
   167     case SuccessReply:
       
   168     case ProgressReply:
       
   169         str << "<No error>";
       
   170         return rc;
       
   171     case FailReply:
       
   172         str << "NAK";
       
   173     case CommandErrorReply:
       
   174         commandError.write(str);
       
   175         break;
       
   176     }
       
   177     // Append the failed command for reference
       
   178     str << " (Command was: '";
       
   179     QByteArray printableRequest = request;
       
   180     printableRequest.replace('\0', '|');
       
   181     str << printableRequest << "')";
       
   182     return rc;
       
   183 }
       
   184 
       
   185 QString TcfTrkCommandResult::toString() const
       
   186 {
       
   187     QString rc;
       
   188     QTextStream str(&rc);
       
   189     str << "Command answer ";
       
   190     switch (type) {
       
   191     case SuccessReply:
       
   192         str << "[success]";
       
   193         break;
       
   194     case CommandErrorReply:
       
   195         str << "[command error]";
       
   196         break;
       
   197     case FailReply:
       
   198         str << "[fail (NAK)]";
       
   199         break;
       
   200     case ProgressReply:
       
   201         str << "[progress]";
       
   202         break;
       
   203     }
       
   204     str << ", " << values.size() << " values(s) to request: '";
       
   205     QByteArray printableRequest = request;
       
   206     printableRequest.replace('\0', '|');
       
   207     str << printableRequest << "' ";
       
   208     if (cookie.isValid())
       
   209         str << " cookie: " << cookie.toString();
       
   210     str << '\n';
       
   211     for (int i = 0, count = values.size(); i < count; i++)
       
   212         str << '#' << i << ' ' << values.at(i).toString() << '\n';
       
   213     if (type == CommandErrorReply)
       
   214         str << "Error: " << errorString();
       
   215     return rc;
       
   216 }
       
   217 
       
   218 struct TcfTrkSendQueueEntry
       
   219 {
       
   220     typedef TcfTrkDevice::MessageType MessageType;
       
   221 
       
   222     explicit TcfTrkSendQueueEntry(MessageType mt,
       
   223                                   int tok,
       
   224                                   Services s,
       
   225                            const QByteArray &d,
       
   226                            const TcfTrkCallback &cb= TcfTrkCallback(),
       
   227                            const QVariant &ck = QVariant()) :
       
   228     messageType(mt), service(s), data(d), token(tok), cookie(ck), callback(cb)  {}
       
   229 
       
   230     MessageType messageType;
       
   231     Services service;
       
   232     QByteArray data;
       
   233     int token;
       
   234     QVariant cookie;
       
   235     TcfTrkCallback callback;
       
   236 };
       
   237 
       
   238 struct TcfTrkDevicePrivate {
       
   239     typedef TcfTrkDevice::IODevicePtr IODevicePtr;
       
   240     typedef QHash<int, TcfTrkSendQueueEntry> TokenWrittenMessageMap;
       
   241 
       
   242     TcfTrkDevicePrivate();
       
   243 
       
   244     const QByteArray m_messageTerminator;
       
   245 
       
   246     IODevicePtr m_device;
       
   247     unsigned m_verbose;
       
   248     QByteArray m_readBuffer;
       
   249     int m_token;
       
   250     QQueue<TcfTrkSendQueueEntry> m_sendQueue;
       
   251     TokenWrittenMessageMap m_writtenMessages;
       
   252     QVector<QByteArray> m_registerNames;
       
   253 };
       
   254 
       
   255 TcfTrkDevicePrivate::TcfTrkDevicePrivate() :
       
   256     m_messageTerminator(messageTerminatorC),
       
   257     m_verbose(0), m_token(0)
       
   258 {
       
   259 }
       
   260 
       
   261 TcfTrkDevice::TcfTrkDevice(QObject *parent) :
       
   262     QObject(parent), d(new TcfTrkDevicePrivate)
       
   263 {
       
   264 }
       
   265 
       
   266 TcfTrkDevice::~TcfTrkDevice()
       
   267 {
       
   268     delete d;
       
   269 }
       
   270 
       
   271 QVector<QByteArray> TcfTrkDevice::registerNames() const
       
   272 {
       
   273     return d->m_registerNames;
       
   274 }
       
   275 
       
   276 void TcfTrkDevice::setRegisterNames(const QVector<QByteArray>& n)
       
   277 {
       
   278     d->m_registerNames = n;
       
   279     if (d->m_verbose) {
       
   280         QString msg;
       
   281         QTextStream str(&msg);
       
   282         const int count = n.size();
       
   283         str << "Registers (" << count << "): ";
       
   284         for (int i = 0; i < count; i++)
       
   285             str << '#' << i << '=' << n.at(i) << ' ';
       
   286         emitLogMessage(msg);
       
   287     }
       
   288 }
       
   289 
       
   290 TcfTrkDevice::IODevicePtr TcfTrkDevice::device() const
       
   291 {
       
   292     return d->m_device;
       
   293 }
       
   294 
       
   295 TcfTrkDevice::IODevicePtr TcfTrkDevice::takeDevice()
       
   296 {
       
   297     const IODevicePtr old = d->m_device;
       
   298     if (!old.isNull()) {
       
   299         old.data()->disconnect(this);
       
   300         d->m_device = IODevicePtr();
       
   301     }
       
   302     d->m_readBuffer.clear();
       
   303     d->m_token = 0;
       
   304     d->m_sendQueue.clear();
       
   305     return old;
       
   306 }
       
   307 
       
   308 void TcfTrkDevice::setDevice(const IODevicePtr &dp)
       
   309 {
       
   310     if (dp.data() == d->m_device.data())
       
   311         return;
       
   312     if (dp.isNull()) {
       
   313         emitLogMessage(QLatin1String("Internal error: Attempt to set NULL device."));
       
   314         return;
       
   315     }
       
   316     takeDevice();
       
   317     d->m_device = dp;
       
   318     connect(dp.data(), SIGNAL(readyRead()), this, SLOT(slotDeviceReadyRead()));
       
   319     if (QAbstractSocket *s = qobject_cast<QAbstractSocket *>(dp.data())) {
       
   320         connect(s, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotDeviceError()));
       
   321         connect(s, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(slotDeviceSocketStateChanged()));
       
   322     }
       
   323 }
       
   324 
       
   325 void TcfTrkDevice::slotDeviceError()
       
   326 {
       
   327     const QString message = d->m_device->errorString();
       
   328     emitLogMessage(message);
       
   329     emit error(message);
       
   330 }
       
   331 
       
   332 void TcfTrkDevice::slotDeviceSocketStateChanged()
       
   333 {
       
   334     if (const QAbstractSocket *s = qobject_cast<const QAbstractSocket *>(d->m_device.data())) {
       
   335         const QAbstractSocket::SocketState st = s->state();
       
   336         switch (st) {
       
   337         case QAbstractSocket::UnconnectedState:
       
   338             emitLogMessage(QLatin1String("Unconnected"));
       
   339             break;
       
   340         case QAbstractSocket::HostLookupState:
       
   341             emitLogMessage(QLatin1String("HostLookupState"));
       
   342             break;
       
   343         case QAbstractSocket::ConnectingState:
       
   344             emitLogMessage(QLatin1String("Connecting"));
       
   345             break;
       
   346         case QAbstractSocket::ConnectedState:
       
   347             emitLogMessage(QLatin1String("Connected"));
       
   348             break;
       
   349         case QAbstractSocket::ClosingState:
       
   350             emitLogMessage(QLatin1String("Closing"));
       
   351             break;
       
   352         default:
       
   353             emitLogMessage(QString::fromLatin1("State %1").arg(st));
       
   354             break;
       
   355         }
       
   356     }
       
   357 }
       
   358 
       
   359 static inline QString debugMessage(QByteArray  message, const char *prefix = 0)
       
   360 {
       
   361     message.replace('\0', '|');
       
   362     const QString messageS = QString::fromLatin1(message);
       
   363     return prefix ?
       
   364             (QLatin1String(prefix) + messageS) :  messageS;
       
   365 }
       
   366 
       
   367 void TcfTrkDevice::slotDeviceReadyRead()
       
   368 {
       
   369     d->m_readBuffer += d->m_device->readAll();
       
   370     // Take complete message off front of readbuffer.
       
   371     do {
       
   372         const int messageEndPos = d->m_readBuffer.indexOf(d->m_messageTerminator);
       
   373         if (messageEndPos == -1)
       
   374             break;
       
   375         const QByteArray message = d->m_readBuffer.left(messageEndPos);
       
   376         if (debug)
       
   377             qDebug("Read:\n%s", qPrintable(formatData(message)));
       
   378         if (const int errorCode = parseMessage(message)) {
       
   379             emitLogMessage(QString::fromLatin1("Parse error %1 for: %2").arg(errorCode).arg(debugMessage(message)));
       
   380         }
       
   381         d->m_readBuffer.remove(0, messageEndPos + d->m_messageTerminator.size());
       
   382     } while (!d->m_readBuffer.isEmpty());
       
   383     checkSendQueue(); // Send off further message
       
   384 }
       
   385 
       
   386 // Split \0-terminated message into tokens, skipping the initial type character
       
   387 static inline QVector<QByteArray> splitMessage(const QByteArray &message)
       
   388 {
       
   389     QVector<QByteArray> tokens;
       
   390     tokens.reserve(7);
       
   391     const int messageSize = message.size();
       
   392     for (int pos = 2; pos < messageSize; ) {
       
   393         const int nextPos = message.indexOf('\0', pos);
       
   394         if (nextPos == -1)
       
   395             break;
       
   396         tokens.push_back(message.mid(pos, nextPos - pos));
       
   397         pos = nextPos + 1;
       
   398     }
       
   399     return tokens;
       
   400 }
       
   401 
       
   402 int TcfTrkDevice::parseMessage(const QByteArray &message)
       
   403 {
       
   404     if (d->m_verbose)
       
   405         emitLogMessage(debugMessage(message, "TCF ->"));
       
   406     // Special JSON parse error message or protocol format error.
       
   407     // The port is usually closed after receiving it.
       
   408     // "\3\2{"Time":1276096098255,"Code":3,"Format": "Protocol format error"}"
       
   409     if (message.startsWith("\003\002")) {
       
   410         QByteArray text = message.mid(2);
       
   411         const QString errorMessage = QString::fromLatin1("Parse error received: %1").arg(QString::fromAscii(text));
       
   412         emit error(errorMessage);
       
   413         return 0;
       
   414     }
       
   415     if (message.size() < 4 || message.at(1) != '\0')
       
   416         return 1;
       
   417     // Split into tokens
       
   418     const char type = message.at(0);
       
   419     const QVector<QByteArray> tokens = splitMessage(message);
       
   420     switch (type) {
       
   421     case 'E':
       
   422         return parseTcfEvent(tokens);
       
   423     case 'R': // Command replies
       
   424     case 'N':
       
   425     case 'P':
       
   426         return parseTcfCommandReply(type, tokens);
       
   427     default:
       
   428         emitLogMessage(QString::fromLatin1("Unhandled message type: %1").arg(debugMessage(message)));
       
   429         return 756;
       
   430     }
       
   431     return 0;
       
   432 }
       
   433 
       
   434 int TcfTrkDevice::parseTcfCommandReply(char type, const QVector<QByteArray> &tokens)
       
   435 {
       
   436     typedef TcfTrkDevicePrivate::TokenWrittenMessageMap::iterator TokenWrittenMessageMapIterator;
       
   437     // Find the corresponding entry in the written messages hash.
       
   438     const int tokenCount = tokens.size();
       
   439     if (tokenCount < 1)
       
   440         return 234;
       
   441     bool tokenOk;
       
   442     const int token = tokens.at(0).toInt(&tokenOk);
       
   443     if (!tokenOk)
       
   444         return 235;
       
   445     const TokenWrittenMessageMapIterator it = d->m_writtenMessages.find(token);
       
   446     if (it == d->m_writtenMessages.end()) {
       
   447         qWarning("TcfTrkDevice: Internal error: token %d not found for '%s'",
       
   448                  token, qPrintable(joinByteArrays(tokens)));
       
   449         return 236;
       
   450     }
       
   451     // No callback: remove entry from map, happy
       
   452     if (!it.value().callback) {
       
   453         d->m_writtenMessages.erase(it);
       
   454         return 0;
       
   455     }
       
   456     // Parse values into JSON
       
   457     QVector<JsonValue> values;
       
   458     values.reserve(tokenCount);
       
   459     for (int i = 1; i < tokenCount; i++) {
       
   460         if (!tokens.at(i).isEmpty()) { // Strange: Empty tokens occur.
       
   461             const JsonValue value(tokens.at(i));
       
   462             if (value.isValid()) {
       
   463                 values.push_back(value);
       
   464             } else {
       
   465                 qWarning("JSON parse error for reply to command token %d: #%d '%s'",
       
   466                          token, i, tokens.at(i).constData());
       
   467                 d->m_writtenMessages.erase(it);
       
   468                 return -1;
       
   469             }
       
   470         }
       
   471     }
       
   472 
       
   473     // Construct result and invoke callback, remove entry from map.
       
   474     TcfTrkCallback callback = it.value().callback;
       
   475     TcfTrkCommandResult result(type, it.value().service, it.value().data,
       
   476                                values, it.value().cookie);
       
   477     d->m_writtenMessages.erase(it);
       
   478     callback(result);
       
   479     return 0;
       
   480 }
       
   481 
       
   482 static const char locatorAnswerC[] = "E\0Locator\0Hello\0[\"Locator\"]";
       
   483 
       
   484 int TcfTrkDevice::parseTcfEvent(const QVector<QByteArray> &tokens)
       
   485 {
       
   486     // Event: Ignore the periodical heartbeat event, answer 'Hello',
       
   487     // emit signal for the rest
       
   488     if (tokens.size() < 3)
       
   489         return 433;
       
   490     const Services service = serviceFromName(tokens.at(0).constData());
       
   491     if (service == LocatorService && tokens.at(1) == "peerHeartBeat")
       
   492         return 0;
       
   493     QVector<JsonValue> values;
       
   494     for (int i = 2; i < tokens.size(); i++) {
       
   495         const JsonValue value(tokens.at(i));
       
   496         if (!value.isValid())
       
   497             return 434;
       
   498         values.push_back(value);
       
   499     }
       
   500     // Parse known events, emit signals
       
   501     QScopedPointer<TcfTrkEvent> knownEvent(TcfTrkEvent::parseEvent(service, tokens.at(1), values));
       
   502     if (!knownEvent.isNull()) {
       
   503         // Answer hello event.
       
   504         if (knownEvent->type() == TcfTrkEvent::LocatorHello)
       
   505             writeMessage(QByteArray(locatorAnswerC, sizeof(locatorAnswerC)));
       
   506         emit tcfEvent(*knownEvent);
       
   507     }
       
   508     emit genericTcfEvent(service, tokens.at(1), values);
       
   509 
       
   510     if (debug || d->m_verbose) {
       
   511         QString msg;
       
   512         QTextStream str(&msg);
       
   513         if (knownEvent.isNull()) {
       
   514             str << "Event: " << tokens.at(0) << ' ' << tokens.at(1) << '\n';
       
   515             foreach(const JsonValue &val, values)
       
   516                 str << "  " << val.toString() << '\n';
       
   517         } else {
       
   518             str << knownEvent->toString();
       
   519         }
       
   520         emitLogMessage(msg);
       
   521     }
       
   522 
       
   523     return 0;
       
   524 }
       
   525 
       
   526 unsigned TcfTrkDevice::verbose() const
       
   527 {
       
   528     return d->m_verbose;
       
   529 }
       
   530 
       
   531 void TcfTrkDevice::setVerbose(unsigned v)
       
   532 {
       
   533     d->m_verbose = v;
       
   534 }
       
   535 
       
   536 void TcfTrkDevice::emitLogMessage(const QString &m)
       
   537 {
       
   538     if (debug)
       
   539         qWarning("%s", qPrintable(m));
       
   540     emit logMessage(m);
       
   541 }
       
   542 
       
   543 bool TcfTrkDevice::checkOpen()
       
   544 {
       
   545     if (d->m_device.isNull()) {
       
   546         emitLogMessage(QLatin1String("Internal error: No device set on TcfTrkDevice."));
       
   547         return false;
       
   548     }
       
   549     if (!d->m_device->isOpen()) {
       
   550         emitLogMessage(QLatin1String("Internal error: Device not open in TcfTrkDevice."));
       
   551         return false;
       
   552     }
       
   553     return true;
       
   554 }
       
   555 
       
   556 void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
       
   557                                      const char *commandParameters, int commandParametersLength,
       
   558                                      const TcfTrkCallback &callBack,
       
   559                                      const QVariant &cookie)
       
   560 
       
   561 {
       
   562     if (!checkOpen())
       
   563         return;
       
   564     // Format the message
       
   565     const int  token = d->m_token++;
       
   566     QByteArray data;
       
   567     data.reserve(30 + commandParametersLength);
       
   568     data.append('C');
       
   569     data.append('\0');
       
   570     data.append(QByteArray::number(token));
       
   571     data.append('\0');
       
   572     data.append(serviceName(service));
       
   573     data.append('\0');
       
   574     data.append(command);
       
   575     data.append('\0');
       
   576     if (commandParametersLength)
       
   577         data.append(commandParameters, commandParametersLength);
       
   578     const TcfTrkSendQueueEntry entry(mt, token, service, data, callBack, cookie);
       
   579     d->m_sendQueue.enqueue(entry);
       
   580     checkSendQueue();
       
   581 }
       
   582 
       
   583 void TcfTrkDevice::sendTcfTrkMessage(MessageType mt, Services service, const char *command,
       
   584                                      const QByteArray &commandParameters,
       
   585                                      const TcfTrkCallback &callBack,
       
   586                                      const QVariant &cookie)
       
   587 {
       
   588     sendTcfTrkMessage(mt, service, command, commandParameters.constData(), commandParameters.size(),
       
   589                       callBack, cookie);
       
   590 }
       
   591 
       
   592 // Enclose in message frame and write.
       
   593 void TcfTrkDevice::writeMessage(QByteArray data)
       
   594 {
       
   595     if (!checkOpen())
       
   596         return;
       
   597 
       
   598     if (d->m_verbose)
       
   599         emitLogMessage(debugMessage(data, "TCF <-"));
       
   600 
       
   601     // Ensure \0-termination which easily gets lost in QByteArray CT.
       
   602     if (!data.endsWith('\0'))
       
   603         data.append('\0');
       
   604     data += d->m_messageTerminator;
       
   605 
       
   606     if (debug > 1)
       
   607         qDebug("Writing:\n%s", qPrintable(formatData(data)));
       
   608 
       
   609     d->m_device->write(data);
       
   610     if (QAbstractSocket *as = qobject_cast<QAbstractSocket *>(d->m_device.data()))
       
   611         as->flush();
       
   612 }
       
   613 
       
   614 void TcfTrkDevice::checkSendQueue()
       
   615 {
       
   616     // Fire off messages or invoke noops until a message with reply is found
       
   617     // and an entry to writtenMessages is made.
       
   618     while (d->m_writtenMessages.empty()) {
       
   619         if (d->m_sendQueue.isEmpty())
       
   620             break;
       
   621         TcfTrkSendQueueEntry entry = d->m_sendQueue.dequeue();
       
   622         switch (entry.messageType) {
       
   623         case MessageWithReply:
       
   624             d->m_writtenMessages.insert(entry.token, entry);
       
   625             writeMessage(entry.data);
       
   626             break;
       
   627         case MessageWithoutReply:
       
   628             writeMessage(entry.data);
       
   629             break;
       
   630         case NoopMessage: // Invoke the noop-callback for synchronization
       
   631             if (entry.callback) {
       
   632                 TcfTrkCommandResult noopResult(TcfTrkCommandResult::SuccessReply);
       
   633                 noopResult.cookie = entry.cookie;
       
   634                 entry.callback(noopResult);
       
   635             }
       
   636             break;
       
   637         }
       
   638     }
       
   639 }
       
   640 
       
   641 // Fix slashes
       
   642 static inline QString fixFileName(QString in)
       
   643 {
       
   644     in.replace(QLatin1Char('/'), QLatin1Char('\\'));
       
   645     return in;
       
   646 }
       
   647 
       
   648 // Start a process (consisting of a non-reply setSettings and start).
       
   649 void TcfTrkDevice::sendProcessStartCommand(const TcfTrkCallback &callBack,
       
   650                                                  const QString &binaryIn,
       
   651                                                  unsigned uid,
       
   652                                                  QStringList arguments,
       
   653                                                  QString workingDirectory,
       
   654                                                  bool debugControl,
       
   655                                                  const QStringList &additionalLibraries,
       
   656                                                  const QVariant &cookie)
       
   657 {
       
   658     // Obtain the bin directory, expand by c:/sys/bin if missing
       
   659     const QChar backSlash('\\');
       
   660     int slashPos = binaryIn.lastIndexOf(QLatin1Char('/'));
       
   661     if (slashPos == -1)
       
   662         slashPos = binaryIn.lastIndexOf(backSlash);
       
   663     const QString sysBin = QLatin1String("c:/sys/bin");
       
   664     const QString binaryFileName  = slashPos == -1 ? binaryIn : binaryIn.mid(slashPos + 1);
       
   665     const QString binaryDirectory = slashPos == -1 ? sysBin : binaryIn.left(slashPos);
       
   666     const QString binary = fixFileName(binaryDirectory + QLatin1Char('/') + binaryFileName);
       
   667 
       
   668     // Fixup: Does argv[0] convention exist on Symbian?
       
   669     arguments.push_front(binary);
       
   670     if (workingDirectory.isEmpty())
       
   671         workingDirectory = sysBin;
       
   672 
       
   673     // Format settings with empty dummy parameter
       
   674     QByteArray setData;
       
   675     JsonInputStream setStr(setData);
       
   676     setStr << "" << '\0'
       
   677             << '[' << "exeToLaunch" << ',' << "addExecutables" << ',' << "addLibraries" << ']'
       
   678             << '\0' << '['
       
   679                 << binary << ','
       
   680                 << '{' << binaryFileName << ':' << QString::number(uid, 16) << '}' << ','
       
   681                 << additionalLibraries
       
   682             << ']';
       
   683     sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData);
       
   684 
       
   685     QByteArray startData;
       
   686     JsonInputStream startStr(startData);
       
   687     startStr << fixFileName(workingDirectory)
       
   688             << '\0' << binary << '\0' << arguments << '\0'
       
   689             << QStringList() << '\0' // Env is an array ["PATH=value"] (non-standard)
       
   690             << debugControl;
       
   691     sendTcfTrkMessage(MessageWithReply, ProcessesService, "start", startData, callBack, cookie);
       
   692 }
       
   693 
       
   694 void TcfTrkDevice::sendProcessTerminateCommand(const TcfTrkCallback &callBack,
       
   695                                                const QByteArray &id,
       
   696                                                const QVariant &cookie)
       
   697 {
       
   698     QByteArray data;
       
   699     JsonInputStream str(data);
       
   700     str << id;
       
   701     sendTcfTrkMessage(MessageWithReply, ProcessesService, "terminate", data, callBack, cookie);
       
   702 }
       
   703 
       
   704 void TcfTrkDevice::sendRunControlTerminateCommand(const TcfTrkCallback &callBack,
       
   705                                                   const QByteArray &id,
       
   706                                                   const QVariant &cookie)
       
   707 {
       
   708     QByteArray data;
       
   709     JsonInputStream str(data);
       
   710     str << id;
       
   711     sendTcfTrkMessage(MessageWithReply, RunControlService, "terminate", data, callBack, cookie);
       
   712 }
       
   713 
       
   714 // Non-standard: Remove executable from settings
       
   715 void TcfTrkDevice::sendSettingsRemoveExecutableCommand(const QString &binaryIn,
       
   716                                                        unsigned uid,
       
   717                                                        const QStringList &additionalLibraries,
       
   718                                                        const QVariant &cookie)
       
   719 {
       
   720     QByteArray setData;
       
   721     JsonInputStream setStr(setData);
       
   722     setStr << "" << '\0'
       
   723             << '[' << "removedExecutables" << ',' << "removedLibraries" << ']'
       
   724             << '\0' << '['
       
   725                 << '{' << QFileInfo(binaryIn).fileName() << ':' << QString::number(uid, 16) << '}' << ','
       
   726                 << additionalLibraries
       
   727             << ']';
       
   728     sendTcfTrkMessage(MessageWithoutReply, SettingsService, "set", setData, TcfTrkCallback(), cookie);
       
   729 }
       
   730 
       
   731 void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
       
   732                                                const QByteArray &id,
       
   733                                                RunControlResumeMode mode,
       
   734                                                unsigned count,
       
   735                                                quint64 rangeStart,
       
   736                                                quint64 rangeEnd,
       
   737                                                const QVariant &cookie)
       
   738 {
       
   739     QByteArray resumeData;
       
   740     JsonInputStream str(resumeData);
       
   741     str << id << '\0' << int(mode) << '\0' << count;
       
   742     switch (mode) {
       
   743     case RM_STEP_OVER_RANGE:
       
   744     case RM_STEP_INTO_RANGE:
       
   745     case RM_REVERSE_STEP_OVER_RANGE:
       
   746     case RM_REVERSE_STEP_INTO_RANGE:
       
   747         str << '\0' << '{' << "RANGE_START" << ':' << rangeStart
       
   748                 << ',' << "RANGE_END" << ':' << rangeEnd << '}';
       
   749         break;
       
   750     default:
       
   751         break;
       
   752     }
       
   753     sendTcfTrkMessage(MessageWithReply, RunControlService, "resume", resumeData, callBack, cookie);
       
   754 }
       
   755 
       
   756 void TcfTrkDevice::sendRunControlSuspendCommand(const TcfTrkCallback &callBack,
       
   757                                                 const QByteArray &id,
       
   758                                                 const QVariant &cookie)
       
   759 {
       
   760     QByteArray data;
       
   761     JsonInputStream str(data);
       
   762     str << id;
       
   763     sendTcfTrkMessage(MessageWithReply, RunControlService, "suspend", data, callBack, cookie);
       
   764 }
       
   765 
       
   766 void TcfTrkDevice::sendRunControlResumeCommand(const TcfTrkCallback &callBack,
       
   767                                                const QByteArray &id,
       
   768                                                const QVariant &cookie)
       
   769 {
       
   770     sendRunControlResumeCommand(callBack, id, RM_RESUME, 1, 0, 0, cookie);
       
   771 }
       
   772 
       
   773 void TcfTrkDevice::sendBreakpointsAddCommand(const TcfTrkCallback &callBack,
       
   774                                              const Breakpoint &bp,
       
   775                                              const QVariant &cookie)
       
   776 {
       
   777     QByteArray data;
       
   778     JsonInputStream str(data);
       
   779     str << bp;
       
   780     sendTcfTrkMessage(MessageWithReply, BreakpointsService, "add", data, callBack, cookie);
       
   781 }
       
   782 
       
   783 void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
       
   784                                                 const QByteArray &id,
       
   785                                                 const QVariant &cookie)
       
   786 {
       
   787     sendBreakpointsRemoveCommand(callBack, QVector<QByteArray>(1, id), cookie);
       
   788 }
       
   789 
       
   790 void TcfTrkDevice::sendBreakpointsRemoveCommand(const TcfTrkCallback &callBack,
       
   791                                                 const QVector<QByteArray> &ids,
       
   792                                                 const QVariant &cookie)
       
   793 {
       
   794     QByteArray data;
       
   795     JsonInputStream str(data);
       
   796     str << ids;
       
   797     sendTcfTrkMessage(MessageWithReply, BreakpointsService, "remove", data, callBack, cookie);
       
   798 }
       
   799 
       
   800 void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
       
   801                                                 const QByteArray &id,
       
   802                                                 bool enable,
       
   803                                                 const QVariant &cookie)
       
   804 {
       
   805     sendBreakpointsEnableCommand(callBack, QVector<QByteArray>(1, id), enable, cookie);
       
   806 }
       
   807 
       
   808 void TcfTrkDevice::sendBreakpointsEnableCommand(const TcfTrkCallback &callBack,
       
   809                                                 const QVector<QByteArray> &ids,
       
   810                                                 bool enable,
       
   811                                                 const QVariant &cookie)
       
   812 {
       
   813     QByteArray data;
       
   814     JsonInputStream str(data);
       
   815     str << ids;
       
   816     sendTcfTrkMessage(MessageWithReply, BreakpointsService,
       
   817                       enable ? "enable" : "disable",
       
   818                       data, callBack, cookie);
       
   819 }
       
   820 
       
   821 void TcfTrkDevice::sendMemorySetCommand(const TcfTrkCallback &callBack,
       
   822                                         const QByteArray &contextId,
       
   823                                         quint64 start, const QByteArray& data,
       
   824                                         const QVariant &cookie)
       
   825 {
       
   826     QByteArray getData;
       
   827     JsonInputStream str(getData);
       
   828     // start/word size/mode. Mode should ideally be 1 (continue on error?)
       
   829     str << contextId << '\0' << start << '\0' << 1 << '\0' << data.size() << '\0' << 1
       
   830         << '\0' << data.toBase64();
       
   831     sendTcfTrkMessage(MessageWithReply, MemoryService, "set", getData, callBack, cookie);
       
   832 }
       
   833 
       
   834 void TcfTrkDevice::sendMemoryGetCommand(const TcfTrkCallback &callBack,
       
   835                                         const QByteArray &contextId,
       
   836                                         quint64 start, quint64 size,
       
   837                                         const QVariant &cookie)
       
   838 {
       
   839     QByteArray data;
       
   840     JsonInputStream str(data);
       
   841     // start/word size/mode. Mode should ideally be 1 (continue on error?)
       
   842     str << contextId << '\0' << start << '\0' << 1 << '\0' << size << '\0' << 1;
       
   843     sendTcfTrkMessage(MessageWithReply, MemoryService, "get", data, callBack, cookie);
       
   844 }
       
   845 
       
   846 QByteArray TcfTrkDevice::parseMemoryGet(const TcfTrkCommandResult &r)
       
   847 {
       
   848     if (r.type != TcfTrkCommandResult::SuccessReply || r.values.size() < 1)
       
   849         return QByteArray();
       
   850     const JsonValue &memoryV = r.values.front();
       
   851 
       
   852     if (memoryV.type() != JsonValue::String || memoryV.data().size() < 2
       
   853         || !memoryV.data().endsWith('='))
       
   854         return QByteArray();
       
   855     // Catch errors reported as hash:
       
   856     // R.4."TlVMTA==".{"Time":1276786871255,"Code":1,"AltCode":-38,"AltOrg":"POSIX","Format":"BadDescriptor"}
       
   857     // Not sure what to make of it.
       
   858     if (r.values.size() >= 2 && r.values.at(1).type() == JsonValue::Object)
       
   859         qWarning("Error retrieving memory: %s", r.values.at(1).toString(false).constData());
       
   860     // decode
       
   861     const QByteArray memory = QByteArray::fromBase64(memoryV.data());
       
   862     if (memory.isEmpty())
       
   863         qWarning("Base64 decoding of %s failed.", memoryV.data().constData());
       
   864     if (debug)
       
   865         qDebug("TcfTrkDevice::parseMemoryGet: received %d bytes", memory.size());
       
   866     return memory;
       
   867 }
       
   868 
       
   869 void TcfTrkDevice::sendRegistersGetMCommand(const TcfTrkCallback &callBack,
       
   870                                             const QByteArray &contextId,
       
   871                                             const QVector<QByteArray> &ids,
       
   872                                             const QVariant &cookie)
       
   873 {
       
   874     // TODO: use "Registers" (which uses base64-encoded values)
       
   875     QByteArray data;
       
   876     JsonInputStream str(data);
       
   877     str << contextId << '\0' << ids;
       
   878     sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "get", data, callBack, cookie);
       
   879 }
       
   880 
       
   881 void TcfTrkDevice::sendRegistersGetMRangeCommand(const TcfTrkCallback &callBack,
       
   882                                                  const QByteArray &contextId,
       
   883                                                  unsigned start, unsigned count)
       
   884 {
       
   885     const unsigned end = start + count;
       
   886     if (end > (unsigned)d->m_registerNames.size()) {
       
   887         qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", end, d->m_registerNames.size());
       
   888         return;
       
   889     }
       
   890 
       
   891     QVector<QByteArray> ids;
       
   892     ids.reserve(count);
       
   893     for (unsigned i = start; i < end; i++)
       
   894         ids.push_back(d->m_registerNames.at(i));
       
   895     sendRegistersGetMCommand(callBack, contextId, ids, QVariant(start));
       
   896 }
       
   897 
       
   898 // Set register
       
   899 void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
       
   900                                            const QByteArray &contextId,
       
   901                                            const QByteArray &id,
       
   902                                            unsigned value,
       
   903                                            const QVariant &cookie)
       
   904 {
       
   905     // TODO: use "Registers" (which uses base64-encoded values)
       
   906     QByteArray data;
       
   907     JsonInputStream str(data);
       
   908     str << contextId << '\0' << QVector<QByteArray>(1, id)
       
   909             << '\0' << QVector<QByteArray>(1, QByteArray::number(value, 16));
       
   910     sendTcfTrkMessage(MessageWithReply, SimpleRegistersService, "set", data, callBack, cookie);
       
   911 }
       
   912 
       
   913 // Set register
       
   914 void TcfTrkDevice::sendRegistersSetCommand(const TcfTrkCallback &callBack,
       
   915                                            const QByteArray &contextId,
       
   916                                            unsigned registerNumber,
       
   917                                            unsigned value,
       
   918                                            const QVariant &cookie)
       
   919 {
       
   920     if (registerNumber >= (unsigned)d->m_registerNames.size()) {
       
   921         qWarning("TcfTrkDevice: No register name set for index %u (size: %d).", registerNumber, d->m_registerNames.size());
       
   922         return;
       
   923     }
       
   924     sendRegistersSetCommand(callBack, contextId,
       
   925                             d->m_registerNames[registerNumber],
       
   926                             value, cookie);
       
   927 }
       
   928 
       
   929 } // namespace tcftrk