summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ivicore/ivicore.pro4
-rw-r--r--src/ivicore/qivisimulationengine.cpp278
-rw-r--r--src/ivicore/qivisimulationengine.h71
-rw-r--r--src/ivicore/qivisimulationproxy.cpp246
-rw-r--r--src/ivicore/qivisimulationproxy.h196
5 files changed, 795 insertions, 0 deletions
diff --git a/src/ivicore/ivicore.pro b/src/ivicore/ivicore.pro
index ab74365..f0c640c 100644
--- a/src/ivicore/ivicore.pro
+++ b/src/ivicore/ivicore.pro
@@ -56,6 +56,8 @@ HEADERS += \
qividefaultpropertyoverrider_p.h \
qivipendingreply.h \
qivipendingreply_p.h \
+ qivisimulationengine.h \
+ qivisimulationproxy.h \
qtivicoremodule.h
SOURCES += \
@@ -79,6 +81,8 @@ SOURCES += \
qividefaultpropertyoverrider.cpp \
qiviqmlconversion_helper.cpp \
qivipendingreply.cpp \
+ qivisimulationengine.cpp \
+ qivisimulationproxy.cpp \
qtivicoremodule.cpp
include(queryparser/queryparser.pri)
diff --git a/src/ivicore/qivisimulationengine.cpp b/src/ivicore/qivisimulationengine.cpp
new file mode 100644
index 0000000..dd3ff90
--- /dev/null
+++ b/src/ivicore/qivisimulationengine.cpp
@@ -0,0 +1,278 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtIvi module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite 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$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#include "qivisimulationengine.h"
+
+#include <QFile>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QIviSimulationEngine
+ \inmodule QtIviCore
+ \brief QIviSimulationEngine provides a way to script a simulation backend from QML.
+
+ This class is an extended QQmlApplicationEngine which can be used to load QML files. It is made
+ especially for \l {Dynamic Backend System}{simulation backends} to script the behavior of a
+ simulation backend from QML.
+
+ In contrast to a normal QQmlEngine the QIviSimulationEngine provides an extra template function
+ called registerSimulationInstance().
+ Using this function it is possible to register an class instance as a QML type. Inside a QML
+ file this QML type can be used to define the behavior for function calls, update properties or
+ emit signals.
+
+ \section1 Registering an instance
+
+ Any instance of a class derived from QObject can be registered to the QIviSimulationEngine by
+ calling the registerSimulationInstance function. Similar to qmlRegisterTypes, the provided uri,
+ version and name is used for importing the type from within QML.
+
+ \code
+ class MyClass : public QObject
+ {
+ Q_OBJECT
+ Q_PROPERTY(int currentTemperature READ currentTemperature WRITE setCurrentTemperature NOTIFY currentTemperatureChanged)
+
+ ...
+ }
+ \endcode
+
+ An instance of this simple class can be registered like this:
+
+ \code
+ QIviSimulationEngine engine;
+ MyClass myClass;
+ engine.registerSimulationInstance<MyClass>(&myClass, "Test", 1, 0, "MyClass");
+ engine.loadSimulation("simulation.qml")
+ \endcode
+
+ The registered instance has the same constraints as other C++ classes exposed to QML and needs
+ to use Q_PROPERTY, Q_INVOKABLE or slots to make the functionality available to QML.
+
+ \section1 Using the type from QML
+
+ Once an instance is registered to the engine, the type can be used like any other QML element
+ in a declarative form:
+
+ \qml
+ import QtQuick 2.0
+ import Test 1.0
+
+ Item {
+ MyClass {
+ id: myClass
+
+ Component.onCompleted: currentTemperature = 10;
+ }
+
+ Timer {
+ running: true
+ repeat: true
+ interval: 1000
+ onTriggered: myClass.currentTemperature++;
+ }
+ }
+ \endqml
+
+ This QML file will initialize the \c currentTemperature of \c myClass with a value of \e 10 and
+ increase it every second.
+
+ In the same way values can be updated from the C++ side and the QML side can react to the
+ change. E.g. the following QML prints the \c currentTemperature whenever it changed:
+
+ \qml
+ import QtQuick 2.0
+ import Test 1.0
+
+ MyClass {
+ onCurrentTemperatureChanged: print(currentTemperature)
+ }
+ \endqml
+
+ The slot will be called once the myClass variable is updated:
+
+ \code
+ QIviSimulationEngine engine;
+ MyClass myClass;
+ engine.registerSimulationInstance<MyClass>(&myClass, "Test", 1, 0, "MyClass");
+ engine.loadSimulation("simulation.qml")
+ ...
+ myClass.setCurrentTemperature(100);
+ \endcode
+
+ \section1 Forwarding calls from the instance to the engine
+
+ Providing the behavior for invokable functions in QML can be done as well, but for this the
+ exposed class needs to be extended.
+
+ E.g. by adding the following line to the \c setCurrentTemperature setter:
+
+ \code
+ void MyClass::setCurrentTemperature(int currentTemperature)
+ {
+ QIVI_SIMULATION_TRY_CALL(MyClass, "setCurrentTemperature", void, currentTemperature);
+
+ if (m_currentTemperature == currentTemperature)
+ return;
+ m_currentTemperature = currentTemperature;
+ emit currentTemperatureChanged(m_currentTemperature);
+ }
+ \endcode
+
+ Calling the setCurrentTemperature() function will now try to forward the call to the QML
+ instance if a function matching the signature is defined in QML and when successful, use its
+ returned value and skip the execution of the original C++ function.
+
+ By using the following QML snippet the execution of the C++ setter is skipped and only an error
+ is emitted on the console:
+
+ \qml
+ import QtQuick 2.0
+ import Test 1.0
+
+ MyClass {
+ function setCurrentTemperature(temperature) {
+ print("Updating the temperature is not possible")
+ }
+ }
+ \endqml
+
+ \section1 Reusing existing behavior in the instance
+
+ Replacing the C++ functionality with a QML behavior is not always what is desired. Because of
+ this, it is also possible to call the original C++ behavior from QML. For this the original C++
+ function needs to be a Q_INVOKABLE or a slot and the functionality works like function
+ overriding in C++. In the C++ world the functionality of the overridden function can be
+ accessed by calling \c <BaseClass>::<function>. In the exposed QML type this is possible by
+ calling the function in the \e Base object.
+
+ \qml
+ import QtQuick 2.0
+ import Test 1.0
+
+ MyClass {
+ function setCurrentTemperature(temperature) {
+ print("Updating the temperature: " + temperature )
+ Base.setCurrentTemperature(temperature)
+ }
+ }
+ \endqml
+
+ This QML code overrides the setCurrentTemperature behavior in QML and prints an debug message
+ for the new value. The original C++ behavior is called by using \c
+ Base.setCurrentTemperature(temperature).
+
+ \section1 Multiple QML instances
+
+ The registered instance is exposed as a normal QML type. This makes it possible to have
+ multiple declarations in QML and by that have multiple QML instances linked to the same C++
+ instance. Updating and reacting to property changes and signal emissions is possible in all
+ instances, but should be used with care as this can result in property update loops and other
+ issues.
+
+ Forwarding C++ function calls to QML is limited. Every call is forwarded to only one QML
+ instance as the return value is used from this call. If multiple QML instances define the same
+ method, the C++ call is always forwarded to the first registered QML instance.
+*/
+
+QIviSimulationEngine::QIviSimulationEngine(QObject *parent)
+ : QQmlApplicationEngine (parent)
+{
+}
+
+/*!
+ Loads the QML \a file as the simulation behavior.
+
+ In addition to QQmlApplicationEngine::load(), this function provides functionality to change
+ the used simulation file by using an environment variable.
+*/
+void QIviSimulationEngine::loadSimulation(const QString &file)
+{
+ if (QFile::exists(file))
+ load(file);
+}
+
+/*!
+ \fn template <typename T> void QIviSimulationEngine::registerSimulationInstance(T* instance, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+
+ Registers the provided \a instance in the QML system with the name \a qmlName, in the library
+ imported from \a uri having the version number composed from \a versionMajor and \a
+ versionMinor.
+
+ \note The registered instance is only available to this instance of the QIviSimulationEngine.
+ Using it from another QIviSimulationEngine or a QQmlEngine will not work and produce an error.
+
+ \sa qmlRegisterType
+*/
+
+/*!
+ \macro QIVI_SIMULATION_TRY_CALL_FUNC(instance_type, function, ret_func, ...)
+ \relates QIviSimulationEngine
+
+ Tries to call \a function in the QML instances registered for the instance of type \a
+ instance_type. The variadic arguments are passed as arguments to the function in QML.
+
+ If the call was successful the code passed in \a ret_func will be executed. This can be useful
+ for situations when the return value needs to be converted first. The original return value is
+ available as \c return_value.
+
+ \code
+ QIVI_SIMULATION_TRY_CALL_FUNC(MyClass, "contactList", return return_value.toStringList());
+ \endcode
+
+ \sa QIVI_SIMULATION_TRY_CALL {Forwarding calls from the instance to the engine}
+*/
+
+/*!
+ \macro QIVI_SIMULATION_TRY_CALL(instance_type, function, ret_type, ...)
+ \relates QIviSimulationEngine
+
+ Tries to call \a function in the QML instances registered for the instance of type \a
+ instance_type. The variadic arguments are passed as arguments to the function in QML.
+
+ If the call was successful the return value of type \a ret_type will be returned and all code
+ after this macro will \b not be executed.
+
+ \sa QIVI_SIMULATION_TRY_CALL_FUNC {Forwarding calls from the instance to the engine}
+*/
+
+QT_END_NAMESPACE
diff --git a/src/ivicore/qivisimulationengine.h b/src/ivicore/qivisimulationengine.h
new file mode 100644
index 0000000..988fd9a
--- /dev/null
+++ b/src/ivicore/qivisimulationengine.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtIvi module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite 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$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#ifndef QIVISIMULATIONENGINE_H
+#define QIVISIMULATIONENGINE_H
+
+#include <QtIviCore/QtIviCoreModule>
+
+#include <QtQml/QQmlApplicationEngine>
+#include <QtIviCore/qivisimulationproxy.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QTIVICORE_EXPORT QIviSimulationEngine : public QQmlApplicationEngine
+{
+ Q_OBJECT
+public:
+ QIviSimulationEngine(QObject *parent = nullptr);
+
+ template <typename T> void registerSimulationInstance(T* instance, const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+ {
+ //pass engine here to check that it's only used in this engine
+ qtivi_private::QIviSimulationProxy<T>::registerInstance(this, instance);
+ //@uri BridgetTest
+ qmlRegisterType< qtivi_private::QIviSimulationProxy<T> >(uri, versionMajor, versionMinor, qmlName);
+ }
+
+ void loadSimulation(const QString &file);
+};
+
+QT_END_NAMESPACE
+
+#endif // QIVISIMULATIONENGINE_H
diff --git a/src/ivicore/qivisimulationproxy.cpp b/src/ivicore/qivisimulationproxy.cpp
new file mode 100644
index 0000000..b3ce374
--- /dev/null
+++ b/src/ivicore/qivisimulationproxy.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtIvi module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite 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$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#include "qivisimulationproxy.h"
+#include "qivisimulationengine.h"
+
+#include <QDebug>
+#include <QLoggingCategory>
+#include <QQmlInfo>
+
+#include <private/qmetaobjectbuilder_p.h>
+
+QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(qLcIviSimulationEngine, "qt.ivi.simulationengine");
+
+namespace qtivi_private {
+
+QIviSimulationProxyBase::QIviSimulationProxyBase(QMetaObject *staticMetaObject, QObject *instance, const QHash<int, int> &methodMap, QObject *parent)
+ : QObject(parent)
+ , m_noSimulationEngine(false)
+ , m_instance(instance)
+ , m_staticMetaObject(staticMetaObject)
+ , m_methodMap(methodMap)
+{
+}
+
+const QMetaObject *QIviSimulationProxyBase::metaObject() const
+{
+ // Copied from moc_ class code
+ // A dynamicMetaObject is created when the type is used from QML and new functions/properties
+ // are added. This makes sure that we can access these from C++ as well
+ return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : m_staticMetaObject;
+}
+
+void *QIviSimulationProxyBase::qt_metacast(const char *classname)
+{
+ if (!classname)
+ return nullptr;
+ return m_instance->qt_metacast(classname);
+}
+
+int QIviSimulationProxyBase::qt_metacall(QMetaObject::Call call, int methodId, void **a)
+{
+ if (m_noSimulationEngine)
+ return -1;
+
+ if (call == QMetaObject::InvokeMetaMethod) {
+ // When a forwarded signal from the registered instance gets in. Directly call the signal here as well
+ if (sender() == m_instance) {
+ // The static MetaObject uses local ids, so we need to subtract the offset
+ QMetaObject::activate(this, m_staticMetaObject, methodId - m_staticMetaObject->methodOffset(), a);
+ return 0;
+ }
+ return m_instance->qt_metacall(call, m_methodMap.key(methodId), a);
+ }
+ return m_instance->qt_metacall(call, methodId, a);
+}
+
+void QIviSimulationProxyBase::classBegin()
+{
+}
+
+void QIviSimulationProxyBase::componentComplete()
+{
+ setProperty("Base", QVariant::fromValue(m_instance));
+}
+
+QMetaObject QIviSimulationProxyBase::buildObject(const QMetaObject *metaObject, QHash<int, int> &methodMap, QIviSimulationProxyBase::StaticMetacallFunction metaCallFunction)
+{
+ QMetaObjectBuilder builder;
+ const QString name = QString(QStringLiteral("QIviSimulationProxy_%1")).arg(QLatin1String(metaObject->className()));
+ builder.setClassName(qPrintable(name));
+ builder.setSuperClass(&QObject::staticMetaObject);
+ builder.setStaticMetacallFunction(metaCallFunction);
+
+ // Build the MetaObject ourself
+ // This is needed as QML uses the static_metacall for reading the properties and every QMetaObject
+ // has its own set. But as we need to intercept this to forward it to the registered instance, we
+ // build our MetaObject completely and have one static_metacall function for all properties
+ const QMetaObject *mo = metaObject;
+
+ //Search for the QObject base class to know which offset we need to start building from
+ const QMetaObject *superClass = mo->superClass();
+ while (qstrcmp(superClass->className(), "QObject") != 0) {
+ superClass = superClass->superClass();
+ }
+ const int methodOffset = superClass->methodCount();
+ const int propertyOffset = superClass->propertyCount();
+
+ //Fill the mapping for all QObject methods.
+ for (int i=0; i<methodOffset; ++i)
+ methodMap.insert(i, i);
+
+ //Add all signals
+ qCDebug(qLcIviSimulationEngine) << "Signal Mapping: Original -> Proxy";
+ for (int index = methodOffset; index < mo->methodCount(); ++index) {
+ QMetaMethod mm = mo->method(index);
+ if (mm.methodType() == QMetaMethod::Signal) {
+ auto mb = builder.addMethod(mm);
+ qCDebug(qLcIviSimulationEngine) << index << "->" << methodOffset + mb.index();
+ methodMap.insert(index, methodOffset + mb.index());
+ }
+ }
+
+ //Add all other methods
+ qCDebug(qLcIviSimulationEngine) << "Method Mapping: Original -> Proxy";
+ for (int index = methodOffset; index < mo->methodCount(); ++index) {
+ QMetaMethod mm = mo->method(index);
+ if (mm.methodType() != QMetaMethod::Signal) {
+ auto mb = builder.addMethod(mm);
+ qCDebug(qLcIviSimulationEngine) << index << "->" << methodOffset + mb.index();
+ methodMap.insert(index, methodOffset + mb.index());
+ }
+ }
+
+ //Add all properties
+ for (int index = propertyOffset; index < mo->propertyCount(); ++index) {
+ QMetaProperty prop = mo->property(index);
+ builder.addProperty(prop);
+ }
+ //Add a Base property which works like a attached property
+ builder.addProperty("Base", "QObject *");
+
+ //Debugging output
+ if (qLcIviSimulationEngine().isDebugEnabled()) {
+ qCDebug(qLcIviSimulationEngine) << "Original Object:";
+ for (int i=0; i < mo->methodCount(); i++) {
+ QMetaMethod method = mo->method(i);
+ qCDebug(qLcIviSimulationEngine) << "method: " << method.methodIndex() << method.methodSignature();
+ }
+ for (int i=0; i < mo->propertyCount(); i++) {
+ QMetaProperty prop = mo->property(i);
+ qCDebug(qLcIviSimulationEngine) << "property:" << prop.propertyIndex() << prop.name();
+ QMetaMethod method = prop.notifySignal();
+ qCDebug(qLcIviSimulationEngine) << "signal: " << method.methodIndex() << method.methodSignature();
+ }
+
+ qCDebug(qLcIviSimulationEngine) << "Proxy Object:";
+ mo = builder.toMetaObject();
+ for (int i=0; i < mo->methodCount(); i++) {
+ QMetaMethod method = mo->method(i);
+ qCDebug(qLcIviSimulationEngine) << "method: " << method.methodIndex() << method.methodSignature();
+ }
+ for (int i=0; i < mo->propertyCount(); i++) {
+ QMetaProperty prop = mo->property(i);
+ qCDebug(qLcIviSimulationEngine) << "property:" << prop.propertyIndex() << prop.name();
+ QMetaMethod method = prop.notifySignal();
+ qCDebug(qLcIviSimulationEngine) << "signal: " << method.methodIndex() << method.methodSignature();
+ }
+ }
+
+ return *builder.toMetaObject();
+}
+
+bool QIviSimulationProxyBase::callQmlMethod(const char *function, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
+{
+ if (m_noSimulationEngine)
+ return false;
+
+ //Prevent recursion
+ static bool recursionGuard = false;
+ if (recursionGuard)
+ return false;
+
+ recursionGuard = true;
+
+ bool functionExecuted = false;
+ const QMetaObject *mo = metaObject();
+
+ // Only invoke the functions declared in QML.
+ // Once a function/property is added to a type a new MetaObject gets created which contains
+ // _QML_ in the name.
+ if (QString::fromLatin1(mo->className()).contains(QLatin1String("_QML_"))) {
+ for (int i=mo->methodOffset(); i<mo->methodCount(); i++) {
+ //qDebug() << "CHECKING FOR: " << function << mo->method(i).name();
+ if (mo->method(i).name() != function)
+ continue;
+ //qDebug() << "EXECUTING";
+ functionExecuted = QMetaObject::invokeMethod(this, function, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
+ break;
+ }
+ }
+ recursionGuard = false;
+ return functionExecuted;
+}
+
+void QIviSimulationProxyBase::setup(QIviSimulationEngine *engine)
+{
+ if (engine != qmlEngine(this)) {
+ qmlWarning(this) << "QIviSimulationProxy can only be used in the same Engine it is registered in";
+ m_noSimulationEngine = true;
+ return;
+ }
+
+ // Connect all signals from the instance to the signals of this metaobject.
+ // This is needed to relay the signals from the instance to this instance and to QML
+ const QMetaObject *mo = m_instance->metaObject();
+ for (int i=0; i<mo->methodCount(); i++) {
+ QMetaMethod mm = mo->method(i);
+ if (mm.methodType() != QMetaMethod::Signal)
+ continue;
+ connect(m_instance, mm, this, m_staticMetaObject->method(m_methodMap.value(i)));
+ }
+}
+
+} //namespace
+
+QT_END_NAMESPACE
diff --git a/src/ivicore/qivisimulationproxy.h b/src/ivicore/qivisimulationproxy.h
new file mode 100644
index 0000000..911c2e3
--- /dev/null
+++ b/src/ivicore/qivisimulationproxy.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtIvi module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite 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$
+**
+** SPDX-License-Identifier: LGPL-3.0
+**
+****************************************************************************/
+
+#ifndef QIVISIMULATIONPROXY_H
+#define QIVISIMULATIONPROXY_H
+
+#include <QtIviCore/QtIviCoreModule>
+
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtCore/QMetaObject>
+#include <QtQml/QQmlParserStatus>
+
+QT_BEGIN_NAMESPACE
+
+class QIviSimulationEngine;
+
+// The classes here can't be moved to a private header as they are used in macros in the user code
+// They are still considered private as they shouldn't be used directly by the user.
+namespace qtivi_private {
+
+ // This is needed as QVariant doesn't support returning void
+ // It is used to cast the variant to the needed return type and use it in the return statement.
+ template <typename T> struct QIviReturnValueHelper {
+ static T value(const QVariant &var)
+ {
+ return var.value<T>();
+ }
+ };
+
+ template <> struct QIviReturnValueHelper <void> {
+ static void value(const QVariant &var)
+ {
+ Q_UNUSED(var);
+ return;
+ }
+ };
+
+ class Q_QTIVICORE_EXPORT QIviSimulationProxyBase : public QObject, public QQmlParserStatus
+ {
+ Q_INTERFACES(QQmlParserStatus)
+
+ public:
+ QIviSimulationProxyBase(QMetaObject *staticMetaObject, QObject *instance, const QHash<int, int> &methodMap, QObject *parent=nullptr);
+
+ virtual const QMetaObject *metaObject() const override;
+ virtual void *qt_metacast(const char *classname) override;
+ virtual int qt_metacall(QMetaObject::Call call, int methodId, void **a) override;
+
+ void classBegin() override;
+ void componentComplete() override;
+
+ typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
+ static QMetaObject buildObject(const QMetaObject *metaObject, QHash<int, int> &methodMap, QIviSimulationProxyBase::StaticMetacallFunction metaCallFunction);
+
+ bool callQmlMethod(const char* function,
+ QGenericReturnArgument ret,
+ QGenericArgument val0 = QGenericArgument(nullptr),
+ QGenericArgument val1 = QGenericArgument(),
+ QGenericArgument val2 = QGenericArgument(),
+ QGenericArgument val3 = QGenericArgument(),
+ QGenericArgument val4 = QGenericArgument(),
+ QGenericArgument val5 = QGenericArgument(),
+ QGenericArgument val6 = QGenericArgument(),
+ QGenericArgument val7 = QGenericArgument(),
+ QGenericArgument val8 = QGenericArgument(),
+ QGenericArgument val9 = QGenericArgument());
+
+ template<typename... Ts>
+ bool callQmlMethod(const char* function, QVariant &returnValue, Ts... args)
+ {
+ return QIviSimulationProxyBase::callQmlMethod(function, Q_RETURN_ARG(QVariant, returnValue), Q_ARG(QVariant, QVariant::fromValue(args))...);
+ }
+
+ protected:
+ void setup(QIviSimulationEngine *engine);
+
+ private:
+ bool m_noSimulationEngine;
+ QObject *m_instance;
+ QMetaObject *m_staticMetaObject;
+ QHash<int, int> m_methodMap;
+ };
+
+ template <typename T> class Q_QTIVICORE_EXPORT QIviSimulationProxy: public QIviSimulationProxyBase
+ {
+ public:
+ QIviSimulationProxy(QObject *parent=nullptr)
+ : QIviSimulationProxyBase(&staticMetaObject, m_instance, m_methodMap, parent)
+ {
+ Q_ASSERT_X(m_instance, "QIviSimulationProxy()", "QIviSimulationProxy::registerInstance needs to be called first");
+ }
+
+ ~QIviSimulationProxy()
+ {
+ proxies.removeAll(this);
+ }
+
+ void classBegin() override
+ {
+ QIviSimulationProxyBase::setup(m_engine);
+ proxies.append(this);
+ }
+
+ // Function is used from QML when reading a property. The static QMetaObject has this function set
+ // as the handler for all static meta calls
+ static void qt_static_metacall(QObject *obj, QMetaObject::Call call, int methodId, void **a)
+ {
+ Q_UNUSED(obj);
+ Q_ASSERT_X(m_instance, "qt_static_metacall()", "QIviSimulationProxy::registerInstance needs to be called first");
+ // As the class acts as a proxy, forward all calls here to the registered instance
+ if (call == QMetaObject::ReadProperty) {
+ void *_v = a[0];
+ *reinterpret_cast< T**>(_v) = m_instance;
+ obj->qt_metacall(call, methodId + staticMetaObject.propertyOffset(), a);
+ return;
+ }
+
+ obj->qt_metacall(call, methodId, a);
+ }
+
+ static void registerInstance(QIviSimulationEngine *engine, T *instance)
+ {
+ m_engine = engine;
+ m_instance = instance;
+ }
+
+ static QMetaObject staticMetaObject;
+ static QList<QIviSimulationProxy<T> *> proxies;
+
+ private:
+ static QIviSimulationEngine *m_engine;
+ static T *m_instance;
+ static QHash<int, int> m_methodMap;
+ };
+
+ template <typename T> QMetaObject QIviSimulationProxy<T>::staticMetaObject = QIviSimulationProxy<T>::buildObject(&T::staticMetaObject, QIviSimulationProxy::m_methodMap, &QIviSimulationProxy<T>::qt_static_metacall);
+ template <typename T> T *QIviSimulationProxy<T>::m_instance = nullptr;
+ template <typename T> QIviSimulationEngine *QIviSimulationProxy<T>::m_engine = nullptr;
+ template <typename T> QList<QIviSimulationProxy<T> *> QIviSimulationProxy<T>::proxies = QList<QIviSimulationProxy<T> *>();
+ template <typename T> QHash<int, int> QIviSimulationProxy<T>::m_methodMap = QHash<int, int>();
+}
+
+#define QIVI_SIMULATION_TRY_CALL_FUNC(instance_type, function, ret_func, ...) \
+for (auto _qivi_instance : qtivi_private::QIviSimulationProxy<instance_type>::proxies) { \
+ QVariant return_value; \
+ if (_qivi_instance->callQmlMethod(function, return_value, ##__VA_ARGS__)) { \
+ ret_func; \
+ } \
+} \
+
+
+#define QIVI_SIMULATION_TRY_CALL(instance_type, function, ret_type, ...) \
+QIVI_SIMULATION_TRY_CALL_FUNC(instance_type, function, return qtivi_private::QIviReturnValueHelper<ret_type>::value(return_value);, ##__VA_ARGS__) \
+
+QT_END_NAMESPACE
+
+#endif // QIVISIMULATIONPROXY_H