diff --git a/cpp-generator/main.cpp b/cpp-generator/main.cpp index 2fe90f6..3e3b105 100644 --- a/cpp-generator/main.cpp +++ b/cpp-generator/main.cpp @@ -163,6 +163,22 @@ static QString makeHeader(const QString& moduleName, const QString& className, c if (i + 1 < params.size()) s << ", "; } s << ");\n"; + // Async overload: same params + callback + optional Timeout + QString asyncCallbackType = (ret == "void") ? QString("std::function") : QString("std::function"; + s << " void " << name << "Async("; + for (int i = 0; i < params.size(); ++i) { + QJsonObject p = params.at(i).toObject(); + QString pt = mapParamType(p.value("type").toString()); + QString pn = p.value("name").toString(); + if (pt == "QString" || pt == "QStringList" || pt == "QJsonArray") { + s << "const " << pt << "& " << pn; + } else { + s << pt << " " << pn; + } + if (i + 1 < params.size()) s << ", "; + } + if (params.size() > 0) s << ", "; + s << asyncCallbackType << " callback, Timeout timeout = Timeout());\n"; } s << "\nprivate:\n"; s << " QObject* ensureReplica();\n"; @@ -332,6 +348,54 @@ static QString makeSource(const QString& moduleName, const QString& className, c s << " return _result;\n"; } s << "}\n\n"; + // Async implementation + s << "void " << className << "::" << name << "Async("; + for (int i = 0; i < params.size(); ++i) { + QJsonObject p = params.at(i).toObject(); + QString pt = mapParamType(p.value("type").toString()); + QString pn = p.value("name").toString(); + if (pt == "QString" || pt == "QStringList" || pt == "QJsonArray") { + s << "const " << pt << "& " << pn; + } else { + s << pt << " " << pn; + } + if (i + 1 < params.size()) s << ", "; + } + if (params.size() > 0) s << ", "; + s << "std::function callback, Timeout timeout) {\n"; + s << " if (!callback) return;\n"; + s << " m_client->invokeRemoteMethodAsync(\"" << moduleName << "\", \"" << name << "\", "; + if (params.size() == 0) { + s << "QVariantList()"; + } else if (params.size() == 1) { + s << "QVariantList() << " << params.at(0).toObject().value("name").toString(); + } else { + s << "QVariantList{"; + for (int i = 0; i < params.size(); ++i) { + s << params.at(i).toObject().value("name").toString(); + if (i + 1 < params.size()) s << ", "; + } + s << "}"; + } + s << ", [callback](QVariant v) {\n"; + if (ret == "void") { + s << " callback();\n"; + } else { + QString defaultVal; + if (ret == "bool") defaultVal = "false"; + else if (ret == "int" || ret == "double" || ret == "float") defaultVal = "0"; + else if (ret == "QString") defaultVal = "QString()"; + else if (ret == "QStringList") defaultVal = "QStringList()"; + else if (ret == "QJsonArray") defaultVal = "QJsonArray()"; + else defaultVal = ret + "{}"; + if (ret == "QVariant") { + s << " callback(v);\n"; + } else { + s << " callback(v.isValid() ? qvariant_cast<" << ret << ">(v) : " << defaultVal << ");\n"; + } + } + s << " }, timeout);\n"; + s << "}\n\n"; } return c; } diff --git a/cpp/logos_api_client.cpp b/cpp/logos_api_client.cpp index ac1d366..d9df72d 100644 --- a/cpp/logos_api_client.cpp +++ b/cpp/logos_api_client.cpp @@ -99,6 +99,59 @@ QVariant LogosAPIClient::invokeRemoteMethod(const QString& objectName, const QSt return invokeRemoteMethod(objectName, methodName, QVariantList() << arg1 << arg2 << arg3 << arg4 << arg5, timeout); } +void LogosAPIClient::invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariantList& args, + std::function callback, + Timeout timeout) +{ + if (!callback) { + return; + } + QString token = getToken(objectName); + if (token.isEmpty() && objectName != "capability_module") { + LogosAPIConsumer* apiConsumer = new LogosAPIConsumer("capability_module", m_origin_module, m_token_manager, this); + QString capabilityToken = getToken("capability_module"); + QVariant result = apiConsumer->invokeRemoteMethod(capabilityToken, "capability_module", "requestModule", QVariantList() << m_origin_module << objectName, timeout); + token = result.toString(); + } + m_consumer->invokeRemoteMethodAsync(token, objectName, methodName, args, callback, timeout); +} + +void LogosAPIClient::invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg, std::function callback, Timeout timeout) +{ + invokeRemoteMethodAsync(objectName, methodName, QVariantList() << arg, callback, timeout); +} + +void LogosAPIClient::invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, std::function callback, Timeout timeout) +{ + invokeRemoteMethodAsync(objectName, methodName, QVariantList() << arg1 << arg2, callback, timeout); +} + +void LogosAPIClient::invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, + std::function callback, Timeout timeout) +{ + invokeRemoteMethodAsync(objectName, methodName, QVariantList() << arg1 << arg2 << arg3, callback, timeout); +} + +void LogosAPIClient::invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, + const QVariant& arg4, + std::function callback, Timeout timeout) +{ + invokeRemoteMethodAsync(objectName, methodName, QVariantList() << arg1 << arg2 << arg3 << arg4, callback, timeout); +} + +void LogosAPIClient::invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, + const QVariant& arg4, const QVariant& arg5, + std::function callback, Timeout timeout) +{ + invokeRemoteMethodAsync(objectName, methodName, QVariantList() << arg1 << arg2 << arg3 << arg4 << arg5, callback, timeout); +} + void LogosAPIClient::onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName, std::function callback) { m_consumer->onEvent(originObject, destinationObject, eventName, callback); @@ -167,4 +220,4 @@ QString LogosAPIClient::getToken(const QString& module_name) // TODO: this is breaking here for core_manager // return AUTH_TOKEN; return ""; -} \ No newline at end of file +} diff --git a/cpp/logos_api_client.h b/cpp/logos_api_client.h index a744bce..13219e3 100644 --- a/cpp/logos_api_client.h +++ b/cpp/logos_api_client.h @@ -143,6 +143,54 @@ public: const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, const QVariant& arg4, const QVariant& arg5, Timeout timeout = Timeout()); + /** + * @brief Invoke a remote method asynchronously; result or error is delivered via callback + * @param objectName The name of the remote object + * @param methodName The name of the method to call + * @param args Arguments to pass to the method + * @param callback Called when the call completes (typically on the main thread) + * @param timeout Timeout for replica acquisition and for the remote call (default 20000ms) + */ + void invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariantList& args, + std::function callback, + Timeout timeout = Timeout()); + + /** + * @brief Invoke a remote method asynchronously with a single argument + */ + void invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg, std::function callback, Timeout timeout = Timeout()); + + /** + * @brief Invoke a remote method asynchronously with two arguments + */ + void invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, std::function callback, Timeout timeout = Timeout()); + + /** + * @brief Invoke a remote method asynchronously with three arguments + */ + void invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, + std::function callback, Timeout timeout = Timeout()); + + /** + * @brief Invoke a remote method asynchronously with four arguments + */ + void invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, + const QVariant& arg4, + std::function callback, Timeout timeout = Timeout()); + + /** + * @brief Invoke a remote method asynchronously with five arguments + */ + void invokeRemoteMethodAsync(const QString& objectName, const QString& methodName, + const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, + const QVariant& arg4, const QVariant& arg5, + std::function callback, Timeout timeout = Timeout()); + /** * @brief Register an event listener for the specified event name * @param originObject The object that will emit the event diff --git a/cpp/logos_api_consumer.cpp b/cpp/logos_api_consumer.cpp index 86e6c07..ad94bed 100644 --- a/cpp/logos_api_consumer.cpp +++ b/cpp/logos_api_consumer.cpp @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -207,6 +210,81 @@ QVariant LogosAPIConsumer::invokeRemoteMethod(const QString& authToken, const QS return pendingCall.returnValue(); } +void LogosAPIConsumer::invokeRemoteMethodAsync(const QString& authToken, const QString& objectName, const QString& methodName, + const QVariantList& args, + AsyncResultCallback callback, + Timeout timeout) +{ + if (!callback) { + qWarning() << "LogosAPIConsumer: invokeRemoteMethodAsync called with null callback"; + return; + } + + QObject* plugin = requestObject(objectName, timeout); + if (!plugin) { + qWarning() << "LogosAPIConsumer: Failed to acquire plugin/replica for object:" << objectName; + QTimer::singleShot(0, this, [callback]() { callback(QVariant()); }); + return; + } + + ModuleProxy* moduleProxy = qobject_cast(plugin); + if (moduleProxy) { + QVariant result = moduleProxy->callRemoteMethod(authToken, methodName, args); + if (!LogosModeConfig::isLocal()) { + delete plugin; + } + QTimer::singleShot(0, this, [callback, result]() { callback(result); }); + return; + } + + if (LogosModeConfig::isLocal()) { + qWarning() << "LogosAPIConsumer: Local mode requires ModuleProxy-wrapped objects"; + QTimer::singleShot(0, this, [callback]() { callback(QVariant()); }); + return; + } + + QRemoteObjectPendingCall pendingCall; + bool success = QMetaObject::invokeMethod( + plugin, + "callRemoteMethod", + Qt::DirectConnection, + Q_RETURN_ARG(QRemoteObjectPendingCall, pendingCall), + Q_ARG(QString, authToken), + Q_ARG(QString, methodName), + Q_ARG(QVariantList, args) + ); + + if (!success) { + qWarning() << "LogosAPIConsumer: Failed to invoke callRemoteMethod on replica for object:" << objectName; + delete plugin; + QTimer::singleShot(0, this, [callback]() { callback(QVariant()); }); + return; + } + + QRemoteObjectPendingCallWatcher* watcher = new QRemoteObjectPendingCallWatcher(pendingCall, this); + QPointer watcherRef(watcher); + QObject* pluginToDelete = plugin; + // Use QueuedConnection so the callback always runs on the consumer's (e.g. main) thread, + // even if the watcher emits finished() from an IO or worker thread. Otherwise the callback + // may never run or may run in a context where UI/consumer state is inaccessible. + connect(watcher, &QRemoteObjectPendingCallWatcher::finished, this, [watcherRef, callback, pluginToDelete]() { + QVariant result; + if (!watcherRef.isNull() && watcherRef->error() == QRemoteObjectPendingCall::NoError) { + result = watcherRef->returnValue(); + } + if (callback) callback(result); + delete pluginToDelete; + if (!watcherRef.isNull()) watcherRef->deleteLater(); + }, Qt::QueuedConnection); + QTimer::singleShot(timeout.ms, this, [watcherRef, callback, pluginToDelete]() { + if (!watcherRef.isNull() && !watcherRef->isFinished()) { + if (callback) callback(QVariant()); + delete pluginToDelete; + watcherRef->deleteLater(); + } + }); +} + void LogosAPIConsumer::onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName, std::function callback) { qDebug() << "LogosAPIConsumer: Registering event listener for event:" << eventName; diff --git a/cpp/logos_api_consumer.h b/cpp/logos_api_consumer.h index 77b5e8e..76fe656 100644 --- a/cpp/logos_api_consumer.h +++ b/cpp/logos_api_consumer.h @@ -80,6 +80,23 @@ public: QVariant invokeRemoteMethod(const QString& authToken, const QString& objectName, const QString& methodName, const QVariantList& args = QVariantList(), Timeout timeout = Timeout()); + /** Callback type for async remote method results. Receives the result QVariant (invalid on error/timeout). */ + using AsyncResultCallback = std::function; + + /** + * @brief Invoke a remote method asynchronously; result or error is delivered via callback + * @param authToken Authentication token for the operation + * @param objectName The name of the remote object + * @param methodName The name of the method to call + * @param args Arguments to pass to the method + * @param callback Called when the call completes (on the same thread as this consumer, typically main) + * @param timeout Timeout for replica acquisition and for the remote call (default 20000ms) + */ + void invokeRemoteMethodAsync(const QString& authToken, const QString& objectName, const QString& methodName, + const QVariantList& args, + AsyncResultCallback callback, + Timeout timeout = Timeout()); + /** * @brief Register an event listener for the specified event name * @param originObject The object that will emit the event