|
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 |