50 #include <QtCore/QMap> |
50 #include <QtCore/QMap> |
51 #include <QtCore/QThread> |
51 #include <QtCore/QThread> |
52 #include <QtCore/QMutex> |
52 #include <QtCore/QMutex> |
53 #include <QtCore/QWaitCondition> |
53 #include <QtCore/QWaitCondition> |
54 #include <QtCore/QSharedPointer> |
54 #include <QtCore/QSharedPointer> |
|
55 #include <QtCore/QScopedPointer> |
55 #include <QtCore/QMetaType> |
56 #include <QtCore/QMetaType> |
56 |
57 |
57 #ifdef Q_OS_WIN |
58 #ifdef Q_OS_WIN |
58 # include <windows.h> |
59 # include <windows.h> |
59 #else |
60 #else |
153 // can use the helper invokeNoopMessage() to trigger its callback. |
159 // can use the helper invokeNoopMessage() to trigger its callback. |
154 // |
160 // |
155 /////////////////////////////////////////////////////////////////////// |
161 /////////////////////////////////////////////////////////////////////// |
156 |
162 |
157 class TrkWriteQueue |
163 class TrkWriteQueue |
158 { |
164 { |
159 Q_DISABLE_COPY(TrkWriteQueue) |
165 Q_DISABLE_COPY(TrkWriteQueue) |
160 public: |
166 public: |
161 explicit TrkWriteQueue(); |
167 explicit TrkWriteQueue(); |
|
168 void clear(); |
162 |
169 |
163 // Enqueue messages. |
170 // Enqueue messages. |
164 void queueTrkMessage(byte code, TrkCallback callback, |
171 void queueTrkMessage(byte code, TrkCallback callback, |
165 const QByteArray &data, const QVariant &cookie); |
172 const QByteArray &data, const QVariant &cookie); |
166 void queueTrkInitialPing(); |
173 void queueTrkInitialPing(); |
206 m_trkWriteToken(0), |
213 m_trkWriteToken(0), |
207 m_trkWriteBusy(false) |
214 m_trkWriteBusy(false) |
208 { |
215 { |
209 } |
216 } |
210 |
217 |
|
218 void TrkWriteQueue::clear() |
|
219 { |
|
220 m_trkWriteToken = 0; |
|
221 m_trkWriteBusy = false; |
|
222 m_trkWriteQueue.clear(); |
|
223 const int discarded = m_writtenTrkMessages.size(); |
|
224 m_writtenTrkMessages.clear(); |
|
225 if (verboseTrk) |
|
226 qDebug() << "TrkWriteQueue::clear: discarded " << discarded; |
|
227 } |
|
228 |
211 byte TrkWriteQueue::nextTrkWriteToken() |
229 byte TrkWriteQueue::nextTrkWriteToken() |
212 { |
230 { |
213 ++m_trkWriteToken; |
231 ++m_trkWriteToken; |
214 if (m_trkWriteToken == 0) |
232 if (m_trkWriteToken == 0) |
215 ++m_trkWriteToken; |
233 ++m_trkWriteToken; |
216 if (verboseTrk) |
234 if (verboseTrk) |
217 qDebug() << "Write token: " << m_trkWriteToken; |
235 qDebug() << "nextTrkWriteToken:" << m_trkWriteToken; |
218 return m_trkWriteToken; |
236 return m_trkWriteToken; |
219 } |
237 } |
220 |
238 |
221 void TrkWriteQueue::queueTrkMessage(byte code, TrkCallback callback, |
239 void TrkWriteQueue::queueTrkMessage(byte code, TrkCallback callback, |
222 const QByteArray &data, const QVariant &cookie) |
240 const QByteArray &data, const QVariant &cookie) |
347 |
365 |
348 class WriterThread : public QThread |
366 class WriterThread : public QThread |
349 { |
367 { |
350 Q_OBJECT |
368 Q_OBJECT |
351 Q_DISABLE_COPY(WriterThread) |
369 Q_DISABLE_COPY(WriterThread) |
352 public: |
370 public: |
353 explicit WriterThread(const QSharedPointer<DeviceContext> &context); |
371 explicit WriterThread(const QSharedPointer<DeviceContext> &context); |
354 |
372 |
355 // Enqueue messages. |
373 // Enqueue messages. |
356 void queueTrkMessage(byte code, TrkCallback callback, |
374 void queueTrkMessage(byte code, TrkCallback callback, |
357 const QByteArray &data, const QVariant &cookie); |
375 const QByteArray &data, const QVariant &cookie); |
358 void queueTrkInitialPing(); |
376 void queueTrkInitialPing(); |
359 |
377 |
|
378 void clearWriteQueue(); |
|
379 |
360 // Call this from the device read notification with the results. |
380 // Call this from the device read notification with the results. |
361 void slotHandleResult(const TrkResult &result); |
381 void slotHandleResult(const TrkResult &result); |
362 |
382 |
363 virtual void run(); |
383 virtual void run(); |
364 |
384 |
559 m_queue.queueTrkMessage(code, callback, data, cookie); |
580 m_queue.queueTrkMessage(code, callback, data, cookie); |
560 m_dataMutex.unlock(); |
581 m_dataMutex.unlock(); |
561 tryWrite(); |
582 tryWrite(); |
562 } |
583 } |
563 |
584 |
|
585 void WriterThread::clearWriteQueue() |
|
586 { |
|
587 m_dataMutex.lock(); |
|
588 m_queue.clear(); |
|
589 m_dataMutex.unlock(); |
|
590 } |
|
591 |
564 void WriterThread::queueTrkInitialPing() |
592 void WriterThread::queueTrkInitialPing() |
565 { |
593 { |
566 m_dataMutex.lock(); |
594 m_dataMutex.lock(); |
567 m_queue.queueTrkInitialPing(); |
595 m_queue.queueTrkInitialPing(); |
568 m_dataMutex.unlock(); |
596 m_dataMutex.unlock(); |
690 // Check if there are already bytes waiting. If not, wait for first byte |
720 // Check if there are already bytes waiting. If not, wait for first byte |
691 COMSTAT comStat; |
721 COMSTAT comStat; |
692 if (!ClearCommError(m_context->device, NULL, &comStat)){ |
722 if (!ClearCommError(m_context->device, NULL, &comStat)){ |
693 emit error(QString::fromLatin1("ClearCommError failed: %1").arg(winErrorMessage(GetLastError()))); |
723 emit error(QString::fromLatin1("ClearCommError failed: %1").arg(winErrorMessage(GetLastError()))); |
694 return -7; |
724 return -7; |
695 } |
725 } |
696 const DWORD bytesToRead = qMax(DWORD(1), qMin(comStat.cbInQue, DWORD(BufSize))); |
726 const DWORD bytesToRead = qMax(DWORD(1), qMin(comStat.cbInQue, DWORD(BufSize))); |
697 // Trigger read |
727 // Trigger read |
698 DWORD bytesRead = 0; |
728 DWORD bytesRead = 0; |
699 if (ReadFile(m_context->device, &buffer, bytesToRead, &bytesRead, &m_context->readOverlapped)) { |
729 if (ReadFile(m_context->device, &buffer, bytesToRead, &bytesRead, &m_context->readOverlapped)) { |
700 if (bytesRead == 1) { |
730 if (bytesRead == 1) { |
706 } |
736 } |
707 const DWORD readError = GetLastError(); |
737 const DWORD readError = GetLastError(); |
708 if (readError != ERROR_IO_PENDING) { |
738 if (readError != ERROR_IO_PENDING) { |
709 emit error(QString::fromLatin1("Read error: %1").arg(winErrorMessage(readError))); |
739 emit error(QString::fromLatin1("Read error: %1").arg(winErrorMessage(readError))); |
710 return -1; |
740 return -1; |
711 } |
741 } |
712 // Wait for either termination or data |
742 // Wait for either termination or data |
713 const DWORD wr = WaitForMultipleObjects(HandleCount, m_handles, false, INFINITE); |
743 const DWORD wr = WaitForMultipleObjects(HandleCount, m_handles, false, INFINITE); |
714 if (wr == WAIT_FAILED) { |
744 if (wr == WAIT_FAILED) { |
715 emit error(QString::fromLatin1("Wait failed: %1").arg(winErrorMessage(GetLastError()))); |
745 emit error(QString::fromLatin1("Wait failed: %1").arg(winErrorMessage(GetLastError()))); |
716 return -2; |
746 return -2; |
781 inline int tryRead(); |
811 inline int tryRead(); |
782 |
812 |
783 int m_terminatePipeFileDescriptors[2]; |
813 int m_terminatePipeFileDescriptors[2]; |
784 }; |
814 }; |
785 |
815 |
786 UnixReaderThread::UnixReaderThread(const QSharedPointer<DeviceContext> &context) : |
816 UnixReaderThread::UnixReaderThread(const QSharedPointer<DeviceContext> &context) : |
787 ReaderThreadBase(context) |
817 ReaderThreadBase(context) |
788 { |
818 { |
789 m_terminatePipeFileDescriptors[0] = m_terminatePipeFileDescriptors[1] = -1; |
819 m_terminatePipeFileDescriptors[0] = m_terminatePipeFileDescriptors[1] = -1; |
790 // Set up pipes for termination. Should not fail |
820 // Set up pipes for termination. Should not fail |
791 if (pipe(m_terminatePipeFileDescriptors) < 0) |
821 if (pipe(m_terminatePipeFileDescriptors) < 0) |
915 delete d; |
945 delete d; |
916 } |
946 } |
917 |
947 |
918 bool TrkDevice::open(QString *errorMessage) |
948 bool TrkDevice::open(QString *errorMessage) |
919 { |
949 { |
920 if (d->verbose) |
950 if (d->verbose || verboseTrk) |
921 qDebug() << "Opening" << port() << "is open: " << isOpen() << " serialFrame=" << serialFrame(); |
951 qDebug() << "Opening" << port() << "is open: " << isOpen() << " serialFrame=" << serialFrame(); |
|
952 if (isOpen()) |
|
953 return true; |
922 if (d->port.isEmpty()) { |
954 if (d->port.isEmpty()) { |
923 *errorMessage = QLatin1String("Internal error: No port set on TrkDevice"); |
955 *errorMessage = QLatin1String("Internal error: No port set on TrkDevice"); |
924 return false; |
956 return false; |
925 } |
957 } |
926 |
|
927 close(); |
|
928 #ifdef Q_OS_WIN |
958 #ifdef Q_OS_WIN |
929 const QString fullPort = QLatin1String("\\\\.\\") + d->port; |
959 const QString fullPort = QLatin1String("\\\\.\\") + d->port; |
930 d->deviceContext->device = CreateFile(reinterpret_cast<const WCHAR*>(fullPort.utf16()), |
960 d->deviceContext->device = CreateFile(reinterpret_cast<const WCHAR*>(fullPort.utf16()), |
931 GENERIC_READ | GENERIC_WRITE, |
961 GENERIC_READ | GENERIC_WRITE, |
932 0, |
962 0, |
973 if (tcsetattr(d->deviceContext->file.handle(), TCSAFLUSH, &termInfo) < 0) { |
1003 if (tcsetattr(d->deviceContext->file.handle(), TCSAFLUSH, &termInfo) < 0) { |
974 *errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno))); |
1004 *errorMessage = QString::fromLatin1("Unable to apply terminal settings: %1 %2").arg(errno).arg(QString::fromAscii(strerror(errno))); |
975 return false; |
1005 return false; |
976 } |
1006 } |
977 #endif |
1007 #endif |
978 d->readerThread = QSharedPointer<ReaderThread>(new ReaderThread(d->deviceContext)); |
1008 d->readerThread.reset(new ReaderThread(d->deviceContext)); |
979 connect(d->readerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)), |
1009 connect(d->readerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)), |
980 Qt::QueuedConnection); |
1010 Qt::QueuedConnection); |
981 connect(d->readerThread.data(), SIGNAL(messageReceived(trk::TrkResult,QByteArray)), |
1011 connect(d->readerThread.data(), SIGNAL(messageReceived(trk::TrkResult,QByteArray)), |
982 this, SLOT(slotMessageReceived(trk::TrkResult,QByteArray)), |
1012 this, SLOT(slotMessageReceived(trk::TrkResult,QByteArray)), |
983 Qt::QueuedConnection); |
1013 Qt::QueuedConnection); |
984 d->readerThread->start(); |
1014 d->readerThread->start(); |
985 |
1015 |
986 d->writerThread = QSharedPointer<WriterThread>(new WriterThread(d->deviceContext)); |
1016 d->writerThread.reset(new WriterThread(d->deviceContext)); |
987 connect(d->writerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)), |
1017 connect(d->writerThread.data(), SIGNAL(error(QString)), this, SLOT(emitError(QString)), |
988 Qt::QueuedConnection); |
1018 Qt::QueuedConnection); |
989 d->writerThread->start(); |
1019 d->writerThread->start(); |
990 |
1020 |
991 if (d->verbose) |
1021 if (d->verbose || verboseTrk) |
992 qDebug() << "Opened" << d->port; |
1022 qDebug() << "Opened" << d->port << d->readerThread.data() << d->writerThread.data(); |
993 return true; |
1023 return true; |
994 } |
1024 } |
995 |
1025 |
996 void TrkDevice::close() |
1026 void TrkDevice::close() |
997 { |
1027 { |
|
1028 if (verboseTrk) |
|
1029 qDebug() << "close" << d->port << " is open: " << isOpen() |
|
1030 << " read pending " << (d->readerThread.isNull() ? 0 : d->readerThread->bytesPending()) |
|
1031 << sender(); |
998 if (!isOpen()) |
1032 if (!isOpen()) |
999 return; |
1033 return; |
1000 if (d->readerThread) |
1034 if (d->readerThread) |
1001 d->readerThread->terminate(); |
1035 d->readerThread->terminate(); |
1002 if (d->writerThread) |
1036 if (d->writerThread) |
1058 d->verbose = b; |
1097 d->verbose = b; |
1059 } |
1098 } |
1060 |
1099 |
1061 void TrkDevice::slotMessageReceived(const trk::TrkResult &result, const QByteArray &rawData) |
1100 void TrkDevice::slotMessageReceived(const trk::TrkResult &result, const QByteArray &rawData) |
1062 { |
1101 { |
1063 d->writerThread->slotHandleResult(result); |
1102 if (isOpen()) { // Might receive bytes after closing due to queued connections. |
1064 if (d->verbose > 1) |
1103 d->writerThread->slotHandleResult(result); |
1065 qDebug() << "Received: " << result.toString(); |
1104 if (d->verbose > 1) |
1066 emit messageReceived(result); |
1105 qDebug() << "Received: " << result.toString(); |
1067 if (!rawData.isEmpty()) |
1106 emit messageReceived(result); |
1068 emit rawDataReceived(rawData); |
1107 if (!rawData.isEmpty()) |
|
1108 emit rawDataReceived(rawData); |
|
1109 } |
1069 } |
1110 } |
1070 |
1111 |
1071 void TrkDevice::emitError(const QString &s) |
1112 void TrkDevice::emitError(const QString &s) |
1072 { |
1113 { |
1073 d->errorString = s; |
1114 d->errorString = s; |
1074 qWarning("%s\n", qPrintable(s)); |
1115 qWarning("%s\n", qPrintable(s)); |
1075 emit error(s); |
1116 emit error(s); |
1076 } |
1117 } |
1077 |
1118 |
|
1119 void TrkDevice::clearWriteQueue() |
|
1120 { |
|
1121 if (isOpen()) |
|
1122 d->writerThread->clearWriteQueue(); |
|
1123 } |
|
1124 |
1078 void TrkDevice::sendTrkMessage(byte code, TrkCallback callback, |
1125 void TrkDevice::sendTrkMessage(byte code, TrkCallback callback, |
1079 const QByteArray &data, const QVariant &cookie) |
1126 const QByteArray &data, const QVariant &cookie) |
1080 { |
1127 { |
|
1128 if (!isOpen()) { |
|
1129 emitError(msgAccessingClosedDevice(d->port)); |
|
1130 return; |
|
1131 } |
1081 if (!d->writerThread.isNull()) { |
1132 if (!d->writerThread.isNull()) { |
1082 if (d->verbose > 1) { |
1133 if (d->verbose > 1) { |
1083 QByteArray msg = "Sending: "; |
1134 QByteArray msg = "Sending: 0x"; |
1084 msg += QByteArray::number(code, 16); |
1135 msg += QByteArray::number(code, 16); |
1085 msg += ": "; |
1136 msg += ": "; |
1086 msg += stringFromArray(data).toLatin1(); |
1137 msg += stringFromArray(data).toLatin1(); |
|
1138 if (cookie.isValid()) |
|
1139 msg += " Cookie: " + cookie.toString().toLatin1(); |
1087 qDebug("%s", msg.data()); |
1140 qDebug("%s", msg.data()); |
1088 } |
1141 } |
1089 d->writerThread->queueTrkMessage(code, callback, data, cookie); |
1142 d->writerThread->queueTrkMessage(code, callback, data, cookie); |
1090 } |
1143 } |
1091 } |
1144 } |
1092 |
1145 |
1093 void TrkDevice::sendTrkInitialPing() |
1146 void TrkDevice::sendTrkInitialPing() |
1094 { |
1147 { |
|
1148 if (!isOpen()) { |
|
1149 emitError(msgAccessingClosedDevice(d->port)); |
|
1150 return; |
|
1151 } |
1095 if (!d->writerThread.isNull()) |
1152 if (!d->writerThread.isNull()) |
1096 d->writerThread->queueTrkInitialPing(); |
1153 d->writerThread->queueTrkInitialPing(); |
1097 } |
1154 } |
1098 |
1155 |
1099 bool TrkDevice::sendTrkAck(byte token) |
1156 bool TrkDevice::sendTrkAck(byte token) |
1100 { |
1157 { |
|
1158 if (!isOpen()) { |
|
1159 emitError(msgAccessingClosedDevice(d->port)); |
|
1160 return false; |
|
1161 } |
1101 if (d->writerThread.isNull()) |
1162 if (d->writerThread.isNull()) |
1102 return false; |
1163 return false; |
1103 // The acknowledgement must not be queued! |
1164 // The acknowledgement must not be queued! |
1104 TrkMessage msg(0x80, token); |
1165 TrkMessage msg(0x80, token); |
1105 msg.token = token; |
1166 msg.token = token; |