summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Lemire <paul.lemire@kdab.com>2016-06-08 12:36:11 +0200
committerPaul Lemire <paul.lemire@kdab.com>2016-07-05 05:47:48 +0000
commit8e317698fd58d7703332d5aae36a266d2efc026c (patch)
tree41c6b4538b77a3d82b5f558c7fd8bc5fe3a84ced
parent0b85e7ad6a962ad86b5c6a74ec20980e72ef9a57 (diff)
Add AspectCommandDebugger
Will allow to send command and replies from remote applications to Qt3D. Also added the class AsynchronousCommandReply which will behave basically like QNetworkReply for internal commands; Change-Id: Ia130f96387fb200658eb46a05e581abe9ef78f63 Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
-rw-r--r--src/core/aspects/aspectcommanddebugger.cpp205
-rw-r--r--src/core/aspects/aspectcommanddebugger_p.h107
-rw-r--r--src/core/aspects/aspects.pri6
-rw-r--r--src/core/aspects/qabstractaspect.cpp23
-rw-r--r--src/core/aspects/qabstractaspect_p.h26
-rw-r--r--src/core/core.pro2
6 files changed, 366 insertions, 3 deletions
diff --git a/src/core/aspects/aspectcommanddebugger.cpp b/src/core/aspects/aspectcommanddebugger.cpp
new file mode 100644
index 000000000..e38ffc844
--- /dev/null
+++ b/src/core/aspects/aspectcommanddebugger.cpp
@@ -0,0 +1,205 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef QT3D_JOBS_RUN_STATS
+
+#include "aspectcommanddebugger_p.h"
+#include <Qt3DCore/qaspectengine.h>
+#include <Qt3DCore/private/qabstractaspect_p.h>
+#include <QTcpSocket>
+#include <QJsonDocument>
+#include <QJsonObject>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+namespace Debug {
+
+namespace {
+
+const qint32 MagicNumber = 0x454;
+
+struct CommandHeader
+{
+ qint32 magic;
+ qint32 size;
+};
+
+} // anonymous
+
+AspectCommandDebugger::ReadBuffer::ReadBuffer()
+ : buffer(4096, 0)
+ , startIdx(0)
+ , endIdx(0)
+{
+}
+
+AspectCommandDebugger::AspectCommandDebugger(QObject *parent)
+ : QTcpServer(parent)
+ , m_aspectEngine(nullptr)
+{
+}
+
+void AspectCommandDebugger::initialize()
+{
+ QObject::connect(this, &QTcpServer::newConnection, [this] {
+ QTcpSocket *socket = nextPendingConnection();
+ m_connections.push_back(socket);
+
+ QObject::connect(socket, &QTcpSocket::disconnected, [this, socket] {
+ m_connections.removeOne(socket);
+ // Destroy object to make sure all QObject connection are removed
+ socket->deleteLater();
+ });
+
+ QObject::connect(socket, &QTcpSocket::readyRead, [this, socket] {
+ onCommandReceived(socket);
+ });
+ });
+ const bool listening = listen(QHostAddress::Any, 8883);
+ if (!listening)
+ qWarning() << Q_FUNC_INFO << "failed to listen on port 8883";
+}
+
+void AspectCommandDebugger::setAspectEngine(QAspectEngine *engine)
+{
+ m_aspectEngine = engine;
+}
+
+void AspectCommandDebugger::asynchronousReplyFinished(AsynchronousCommandReply *reply)
+{
+ Q_ASSERT(reply->isFinished());
+ QTcpSocket *socket = m_asyncCommandToSocketEntries.take(reply);
+ if (m_connections.contains(socket))
+ sendReply(socket, reply->data());
+ reply->deleteLater();
+}
+
+
+// Expects to receive commands in the form
+// CommandHeader { MagicNumber; size }
+// JSON {
+// command: "commandName"
+// data: JSON Obj
+// }
+void AspectCommandDebugger::onCommandReceived(QTcpSocket *socket)
+{
+ const QByteArray newData = socket->readAll();
+ m_readBuffer.buffer.insert(m_readBuffer.endIdx, newData);
+ m_readBuffer.endIdx += newData.size();
+
+ const int commandPacketSize = sizeof(CommandHeader);
+ while ((m_readBuffer.endIdx - m_readBuffer.startIdx) >= commandPacketSize) {
+ CommandHeader *header = reinterpret_cast<CommandHeader *>(m_readBuffer.buffer.data() + m_readBuffer.startIdx);
+ if (header->magic == MagicNumber &&
+ (m_readBuffer.endIdx - (m_readBuffer.startIdx + commandPacketSize)) >= header->size) {
+ // We have a valid command
+ // We expect command to be a CommandHeader + some json text
+ const QJsonDocument doc = QJsonDocument::fromJson(
+ QByteArray(m_readBuffer.buffer.data() + m_readBuffer.startIdx + commandPacketSize,
+ header->size));
+
+ if (!doc.isNull()) {
+ qDebug() << Q_FUNC_INFO << doc.toJson();
+ // Send command to the aspectEngine
+ QJsonObject commandObj = doc.object();
+ const QJsonValue commandNameValue = commandObj.value(QLatin1String("command"));
+ executeCommand(commandNameValue.toString(), socket);
+ }
+
+ m_readBuffer.startIdx += commandPacketSize + header->size;
+ }
+ }
+ if (m_readBuffer.startIdx != m_readBuffer.endIdx && m_readBuffer.startIdx != 0) {
+ // Copy remaining length of buffer at begininning
+ memcpy(m_readBuffer.buffer.data(),
+ m_readBuffer.buffer.constData() + m_readBuffer.startIdx,
+ m_readBuffer.endIdx - m_readBuffer.startIdx);
+ }
+ m_readBuffer.endIdx -= m_readBuffer.startIdx;
+ m_readBuffer.startIdx = 0;
+
+}
+
+void AspectCommandDebugger::sendReply(QTcpSocket *socket, const QByteArray &payload)
+{
+ CommandHeader replyHeader;
+
+ replyHeader.magic = MagicNumber;
+ replyHeader.size = payload.size();
+ // Write header
+ socket->write(reinterpret_cast<const char *>(&replyHeader), sizeof(CommandHeader));
+ // Write payload
+ socket->write(payload.constData(), payload.size());
+}
+
+void AspectCommandDebugger::executeCommand(const QString &command,
+ QTcpSocket *socket)
+{
+ // Only a single aspect is going to reply
+ const QVariant response = m_aspectEngine->executeCommand(command);
+ if (response.userType() == qMetaTypeId<AsynchronousCommandReply *>()) { // AsynchronousCommand
+ // Store the command | socket in a table
+ AsynchronousCommandReply *reply = response.value<AsynchronousCommandReply *>();
+ // Command has already been completed
+ if (reply->isFinished()) {
+ sendReply(socket, reply->data());
+ } else { // Command is not completed yet
+ QObject::connect(reply, &AsynchronousCommandReply::finished,
+ this, &AspectCommandDebugger::asynchronousReplyFinished);
+ m_asyncCommandToSocketEntries.insert(reply, socket);
+ }
+ } else { // Synchronous command
+ // and send response to client
+ QJsonObject reply;
+ reply.insert(QStringLiteral("command"), QJsonValue(command));
+ // TO DO: convert QVariant to QJsonDocument/QByteArray
+ sendReply(socket, QJsonDocument(reply).toJson());
+
+ }
+}
+
+} // Debug
+
+} // Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/core/aspects/aspectcommanddebugger_p.h b/src/core/aspects/aspectcommanddebugger_p.h
new file mode 100644
index 000000000..917019b6d
--- /dev/null
+++ b/src/core/aspects/aspectcommanddebugger_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt3D module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifdef QT3D_JOBS_RUN_STATS
+
+#ifndef QT3DCORE_DEBUG_ASPECTCOMMANDDEBUGGER_H
+#define QT3DCORE_DEBUG_ASPECTCOMMANDDEBUGGER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QTcpServer>
+
+QT_BEGIN_NAMESPACE
+
+namespace Qt3DCore {
+
+class QAspectEngine;
+
+namespace Debug {
+
+class AsynchronousCommandReply;
+
+class AspectCommandDebugger : public QTcpServer
+{
+ Q_OBJECT
+public:
+ explicit AspectCommandDebugger(QObject *parent = nullptr);
+
+ void initialize();
+ void setAspectEngine(QAspectEngine *engine);
+
+private Q_SLOTS:
+ void asynchronousReplyFinished(AsynchronousCommandReply *reply);
+
+private:
+ void sendReply(QTcpSocket *socket, const QByteArray &data);
+ void onCommandReceived(QTcpSocket *socket);
+ void executeCommand(const QString &command, QTcpSocket *socket);
+
+ QVector<QTcpSocket *> m_connections;
+ QAspectEngine *m_aspectEngine;
+
+ struct ReadBuffer {
+ ReadBuffer();
+
+ QByteArray buffer;
+ int startIdx;
+ int endIdx;
+ };
+ ReadBuffer m_readBuffer;
+ QHash<AsynchronousCommandReply *, QTcpSocket *> m_asyncCommandToSocketEntries;
+};
+
+} // Debug
+
+} // Qt3DCore
+
+QT_END_NAMESPACE
+
+#endif // QT3DCORE_DEBUG_ASPECTCOMMANDDEBUGGER_H
+
+#endif // QT3D_JOBS_RUN_STATS
diff --git a/src/core/aspects/aspects.pri b/src/core/aspects/aspects.pri
index 40bbfe739..773c736a3 100644
--- a/src/core/aspects/aspects.pri
+++ b/src/core/aspects/aspects.pri
@@ -5,7 +5,8 @@ SOURCES += \
$$PWD/qaspectengine.cpp \
$$PWD/qaspectfactory.cpp \
$$PWD/qaspectmanager.cpp \
- $$PWD/qaspectthread.cpp
+ $$PWD/qaspectthread.cpp \
+ $$PWD/aspectcommanddebugger.cpp
HEADERS += \
$$PWD/qabstractaspect.h \
@@ -14,6 +15,7 @@ HEADERS += \
$$PWD/qaspectengine_p.h \
$$PWD/qaspectfactory_p.h \
$$PWD/qaspectmanager_p.h \
- $$PWD/qaspectthread_p.h
+ $$PWD/qaspectthread_p.h \
+ $$PWD/aspectcommanddebugger_p.h
INCLUDEPATH += $$PWD
diff --git a/src/core/aspects/qabstractaspect.cpp b/src/core/aspects/qabstractaspect.cpp
index 25f1d3fd2..13195a798 100644
--- a/src/core/aspects/qabstractaspect.cpp
+++ b/src/core/aspects/qabstractaspect.cpp
@@ -323,6 +323,29 @@ void QAbstractAspect::onEngineShutdown()
{
}
+namespace Debug {
+
+AsynchronousCommandReply::AsynchronousCommandReply(const QString &commandName, QObject *parent)
+ : QObject(parent)
+ , m_commandName(commandName)
+ , m_finished(false)
+{
+}
+
+void AsynchronousCommandReply::setFinished(bool replyFinished)
+{
+ m_finished = replyFinished;
+ if (m_finished)
+ emit finished(this);
+}
+
+void AsynchronousCommandReply::setData(const QByteArray &data)
+{
+ m_data = data;
+}
+
+} // Debug
+
} // of namespace Qt3DCore
diff --git a/src/core/aspects/qabstractaspect_p.h b/src/core/aspects/qabstractaspect_p.h
index 8f17bee54..d83b25e60 100644
--- a/src/core/aspects/qabstractaspect_p.h
+++ b/src/core/aspects/qabstractaspect_p.h
@@ -72,6 +72,32 @@ class QAbstractAspectJobManager;
class QChangeArbiter;
class QServiceLocator;
+namespace Debug {
+
+class AsynchronousCommandReply : public QObject
+{
+ Q_OBJECT
+public:
+ explicit AsynchronousCommandReply(const QString &commandName, QObject *parent = nullptr);
+
+ inline QByteArray data() const { return m_data; }
+ inline QString commandName() const { return m_commandName; }
+ inline bool isFinished() const { return m_finished; }
+
+ void setFinished(bool finished);
+ void setData(const QByteArray &data);
+
+Q_SIGNALS:
+ void finished(AsynchronousCommandReply *reply);
+
+private:
+ QByteArray m_data;
+ QString m_commandName;
+ bool m_finished;
+};
+
+} // Debug
+
class QT3DCORE_PRIVATE_EXPORT QAbstractAspectPrivate
: public QObjectPrivate
, public QBackendNodeFactory
diff --git a/src/core/core.pro b/src/core/core.pro
index 74aa56e95..02cd8cc80 100644
--- a/src/core/core.pro
+++ b/src/core/core.pro
@@ -1,7 +1,7 @@
TARGET = Qt3DCore
MODULE = 3dcore
-QT = core-private gui-private
+QT = core-private gui-private network
# Qt3D is free of Q_FOREACH - make sure it stays that way:
DEFINES += QT_NO_FOREACH