mirror of
https://github.com/logos-co/logos-cpp-sdk.git
synced 2026-08-31 01:31:10 +00:00
feat: move logos-cpp-sdk to its own repo
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
project(LogosSDK)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
|
||||
# Find Qt packages
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core RemoteObjects)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core RemoteObjects)
|
||||
|
||||
# SDK sources
|
||||
set(SDK_SOURCES
|
||||
logos_api.cpp
|
||||
logos_api.h
|
||||
logos_api_client.cpp
|
||||
logos_api_client.h
|
||||
logos_api_consumer.cpp
|
||||
logos_api_consumer.h
|
||||
logos_api_provider.cpp
|
||||
logos_api_provider.h
|
||||
module_proxy.cpp
|
||||
module_proxy.h
|
||||
token_manager.cpp
|
||||
token_manager.h
|
||||
)
|
||||
|
||||
# Create the SDK library as STATIC instead of SHARED
|
||||
add_library(logos_sdk STATIC ${SDK_SOURCES})
|
||||
|
||||
# Link Qt libraries
|
||||
target_link_libraries(logos_sdk PUBLIC Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::RemoteObjects)
|
||||
|
||||
# Include directories
|
||||
target_include_directories(logos_sdk PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
# Set output directories for static library
|
||||
set_target_properties(logos_sdk PROPERTIES
|
||||
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
|
||||
)
|
||||
Executable
+200
@@ -0,0 +1,200 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple compilation test for LogosAPI
|
||||
# This script compiles the LogosAPI files to check for syntax and compilation errors
|
||||
|
||||
echo "Testing LogosAPI compilation..."
|
||||
|
||||
# Find Qt installation
|
||||
if [ -n "$QT_DIR" ]; then
|
||||
QT_PATH="$QT_DIR"
|
||||
echo "Using QT_DIR: $QT_PATH"
|
||||
elif command -v qmake >/dev/null 2>&1; then
|
||||
QT_PATH=$(qmake -query QT_INSTALL_PREFIX)
|
||||
echo "Found Qt via qmake at: $QT_PATH"
|
||||
else
|
||||
echo "Error: QT_DIR not set and qmake not found. Please set QT_DIR environment variable or ensure Qt is installed and in PATH."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find MOC binary
|
||||
if [ -f "$QT_PATH/bin/moc" ]; then
|
||||
MOC_BIN="$QT_PATH/bin/moc"
|
||||
elif [ -f "$QT_PATH/libexec/moc" ]; then
|
||||
MOC_BIN="$QT_PATH/libexec/moc"
|
||||
elif command -v moc >/dev/null 2>&1; then
|
||||
MOC_BIN="moc"
|
||||
else
|
||||
echo "Error: MOC (Meta-Object Compiler) not found. Please ensure Qt development tools are installed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Using MOC: $MOC_BIN"
|
||||
|
||||
# Set Qt include paths - handle both Qt5 and Qt6 on different platforms
|
||||
if [ -d "$QT_PATH/lib" ]; then
|
||||
# Qt6 style with lib directory (common on macOS)
|
||||
# Add framework headers and the lib directory itself for framework-style includes
|
||||
QT_INCLUDES="-F$QT_PATH/lib"
|
||||
QT_INCLUDES="$QT_INCLUDES -I$QT_PATH/lib/QtCore.framework/Headers"
|
||||
QT_INCLUDES="$QT_INCLUDES -I$QT_PATH/lib/QtRemoteObjects.framework/Headers"
|
||||
# Also add the general include paths as fallback
|
||||
QT_INCLUDES="$QT_INCLUDES -I$QT_PATH/include -I$QT_PATH/include/QtCore -I$QT_PATH/include/QtRemoteObjects"
|
||||
else
|
||||
# Standard include directory structure
|
||||
QT_INCLUDES="-I$QT_PATH/include -I$QT_PATH/include/QtCore -I$QT_PATH/include/QtRemoteObjects"
|
||||
fi
|
||||
|
||||
echo "Using Qt includes: $QT_INCLUDES"
|
||||
|
||||
# Compiler flags
|
||||
CXXFLAGS="-std=c++17 -fPIC"
|
||||
|
||||
# Generate MOC files for headers with Q_OBJECT
|
||||
echo "Generating MOC files..."
|
||||
|
||||
# List of headers that need MOC processing (contain Q_OBJECT)
|
||||
MOC_HEADERS=(
|
||||
"logos_api.h"
|
||||
"logos_api_client.h"
|
||||
"logos_api_provider.h"
|
||||
"logos_api_consumer.h"
|
||||
"module_proxy.h"
|
||||
"token_manager.h"
|
||||
)
|
||||
|
||||
for header in "${MOC_HEADERS[@]}"; do
|
||||
if [ -f "$header" ]; then
|
||||
echo "Generating MOC for $header..."
|
||||
$MOC_BIN $header -o "moc_${header%.h}.cpp"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "❌ MOC generation failed for $header"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Try to compile the headers (syntax check)
|
||||
echo "Checking header syntax..."
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c -x c++-header logos_api.h -o /tmp/logos_api.h.gch
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ LogosAPI header syntax OK"
|
||||
rm -f /tmp/logos_api.h.gch
|
||||
else
|
||||
echo "❌ LogosAPI header has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c -x c++-header logos_api_client.h -o /tmp/logos_api_client.h.gch
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Client header syntax OK"
|
||||
rm -f /tmp/logos_api_client.h.gch
|
||||
else
|
||||
echo "❌ Client header has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c -x c++-header logos_api_provider.h -o /tmp/logos_api_provider.h.gch
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Provider header syntax OK"
|
||||
rm -f /tmp/logos_api_provider.h.gch
|
||||
else
|
||||
echo "❌ Provider header has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c -x c++-header logos_api_consumer.h -o /tmp/logos_api_consumer.h.gch
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Consumer header syntax OK"
|
||||
rm -f /tmp/logos_api_consumer.h.gch
|
||||
else
|
||||
echo "❌ Consumer header has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c -x c++-header module_proxy.h -o /tmp/module_proxy.h.gch
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Module proxy header syntax OK"
|
||||
rm -f /tmp/module_proxy.h.gch
|
||||
else
|
||||
echo "❌ Module proxy header has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c -x c++-header token_manager.h -o /tmp/token_manager.h.gch
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Token manager header syntax OK"
|
||||
rm -f /tmp/token_manager.h.gch
|
||||
else
|
||||
echo "❌ Token manager header has syntax errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Try to compile the implementations (without linking)
|
||||
echo "Checking implementation syntax..."
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c logos_api.cpp -o /tmp/logos_api.o
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ LogosAPI implementation compiles OK"
|
||||
rm -f /tmp/logos_api.o
|
||||
else
|
||||
echo "❌ LogosAPI implementation has compilation errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c logos_api_client.cpp -o /tmp/logos_api_client.o
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Client implementation compiles OK"
|
||||
rm -f /tmp/logos_api_client.o
|
||||
else
|
||||
echo "❌ Client implementation has compilation errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c logos_api_provider.cpp -o /tmp/logos_api_provider.o
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Provider implementation compiles OK"
|
||||
rm -f /tmp/logos_api_provider.o
|
||||
else
|
||||
echo "❌ Provider implementation has compilation errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c logos_api_consumer.cpp -o /tmp/logos_api_consumer.o
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Consumer implementation compiles OK"
|
||||
rm -f /tmp/logos_api_consumer.o
|
||||
else
|
||||
echo "❌ Consumer implementation has compilation errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c module_proxy.cpp -o /tmp/module_proxy.o
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Module proxy implementation compiles OK"
|
||||
rm -f /tmp/module_proxy.o
|
||||
else
|
||||
echo "❌ Module proxy implementation has compilation errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
g++ $CXXFLAGS $QT_INCLUDES -c token_manager.cpp -o /tmp/token_manager.o
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ Token manager implementation compiles OK"
|
||||
rm -f /tmp/token_manager.o
|
||||
else
|
||||
echo "❌ Token manager implementation has compilation errors"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean up generated MOC files
|
||||
echo "Cleaning up generated MOC files..."
|
||||
for header in "${MOC_HEADERS[@]}"; do
|
||||
moc_file="moc_${header%.h}.cpp"
|
||||
if [ -f "$moc_file" ]; then
|
||||
rm -f "$moc_file"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "🎉 LogosAPI compilation test passed for all components!"
|
||||
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @file example_usage.cpp
|
||||
* @brief Example showing how to use the LogosAPI SDK
|
||||
*
|
||||
* This example demonstrates how to use the LogosAPI to connect
|
||||
* to the Logos Core registry and request remote objects.
|
||||
*/
|
||||
|
||||
#include "logos_api_client.h"
|
||||
#include "token_manager.h"
|
||||
#include <QMetaObject>
|
||||
#include <QRemoteObjectReplica>
|
||||
#include <QDebug>
|
||||
|
||||
void exampleUsage()
|
||||
{
|
||||
// Create a client instance (uses default registry URL)
|
||||
LogosAPIClient client("core_manager", "example");
|
||||
|
||||
// Check if connected
|
||||
if (!client.isConnected()) {
|
||||
qWarning() << "Failed to connect to Logos Core registry";
|
||||
return;
|
||||
}
|
||||
|
||||
// Request the Core Manager object
|
||||
QRemoteObjectReplica* coreManager = client.requestObject("Core Manager");
|
||||
if (!coreManager) {
|
||||
qWarning() << "Failed to acquire Core Manager replica";
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the replica to call methods
|
||||
QString pluginName;
|
||||
bool success = QMetaObject::invokeMethod(
|
||||
coreManager,
|
||||
"processPlugin",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QString, pluginName),
|
||||
Q_ARG(QString, "/path/to/plugin.dylib")
|
||||
);
|
||||
|
||||
if (success && !pluginName.isEmpty()) {
|
||||
qDebug() << "Successfully processed plugin:" << pluginName;
|
||||
} else {
|
||||
qWarning() << "Failed to process plugin";
|
||||
}
|
||||
|
||||
// Clean up the replica when done
|
||||
delete coreManager;
|
||||
}
|
||||
|
||||
// Alternative usage with custom registry URL and timeout
|
||||
void exampleCustomUsage()
|
||||
{
|
||||
// Create client with custom registry URL
|
||||
LogosAPI client("custom_registry");
|
||||
|
||||
if (!client.isConnected()) {
|
||||
// Try to reconnect
|
||||
if (!client.reconnect()) {
|
||||
qWarning() << "Failed to connect to custom registry";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Request object with custom timeout (10 seconds)
|
||||
QRemoteObjectReplica* someObject = client.requestObject("Some Object", 10000);
|
||||
if (someObject) {
|
||||
// Use the object...
|
||||
|
||||
// Clean up
|
||||
delete someObject;
|
||||
}
|
||||
|
||||
// Example TokenManager usage
|
||||
TokenManager& tokenManager = TokenManager::instance();
|
||||
|
||||
// Save some tokens
|
||||
tokenManager.saveToken("auth_token", "abc123xyz");
|
||||
tokenManager.saveToken("refresh_token", "def456uvw");
|
||||
tokenManager.saveToken("session_token", "ghi789rst");
|
||||
|
||||
// Retrieve tokens
|
||||
QString authToken = tokenManager.getToken("auth_token");
|
||||
qDebug() << "Auth token:" << authToken;
|
||||
|
||||
// Check if token exists
|
||||
if (tokenManager.hasToken("refresh_token")) {
|
||||
qDebug() << "Refresh token exists";
|
||||
}
|
||||
|
||||
// Get all token keys
|
||||
QList<QString> keys = tokenManager.getTokenKeys();
|
||||
qDebug() << "Token keys:" << keys;
|
||||
qDebug() << "Total tokens:" << tokenManager.tokenCount();
|
||||
|
||||
// Remove a token
|
||||
if (tokenManager.removeToken("session_token")) {
|
||||
qDebug() << "Session token removed";
|
||||
}
|
||||
|
||||
// Clear all tokens when done
|
||||
// tokenManager.clearAllTokens();
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "logos_api.h"
|
||||
#include "logos_api_client.h"
|
||||
#include "logos_api_provider.h"
|
||||
#include "token_manager.h"
|
||||
|
||||
LogosAPI::LogosAPI(const QString& module_name, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_module_name(module_name)
|
||||
, m_provider(nullptr)
|
||||
, m_token_manager(nullptr)
|
||||
{
|
||||
// Initialize provider
|
||||
m_provider = new LogosAPIProvider(m_module_name, this);
|
||||
|
||||
// Get token manager instance
|
||||
m_token_manager = &TokenManager::instance();
|
||||
}
|
||||
|
||||
LogosAPI::~LogosAPI()
|
||||
{
|
||||
// Provider and client will be automatically deleted as child objects
|
||||
// Token manager is a singleton, so we don't delete it
|
||||
}
|
||||
|
||||
LogosAPIProvider* LogosAPI::getProvider() const
|
||||
{
|
||||
return m_provider;
|
||||
}
|
||||
|
||||
LogosAPIClient* LogosAPI::getClient(const QString& target_module) const
|
||||
{
|
||||
// Check if we already have a client for this target module
|
||||
if (m_clients.contains(target_module)) {
|
||||
return m_clients.value(target_module);
|
||||
}
|
||||
|
||||
// Create a new client for this target module
|
||||
LogosAPIClient* client = new LogosAPIClient(target_module, m_module_name, m_token_manager, const_cast<LogosAPI*>(this));
|
||||
|
||||
// Cache it for future use
|
||||
m_clients.insert(target_module, client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
TokenManager* LogosAPI::getTokenManager() const
|
||||
{
|
||||
return m_token_manager;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef LOGOS_API_H
|
||||
#define LOGOS_API_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
|
||||
class LogosAPIClient;
|
||||
class LogosAPIProvider;
|
||||
class TokenManager;
|
||||
|
||||
/**
|
||||
* @brief LogosAPI provides a unified interface to the Logos SDK
|
||||
*
|
||||
* This class initializes and keeps instances of the client provider and token manager.
|
||||
*/
|
||||
class LogosAPI : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new LogosAPI instance
|
||||
* @param module_name The name of this module
|
||||
* @param parent Parent QObject
|
||||
*/
|
||||
explicit LogosAPI(const QString& module_name, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~LogosAPI();
|
||||
|
||||
/**
|
||||
* @brief Get the client provider instance
|
||||
* @return LogosAPIProvider* Pointer to the provider
|
||||
*/
|
||||
LogosAPIProvider* getProvider() const;
|
||||
|
||||
/**
|
||||
* @brief Get the client instance for communicating with a module
|
||||
* @param target_module The module to communicate with
|
||||
* @return LogosAPIClient* Pointer to the client
|
||||
*/
|
||||
LogosAPIClient* getClient(const QString& target_module) const;
|
||||
|
||||
/**
|
||||
* @brief Get the token manager instance
|
||||
* @return TokenManager* Pointer to the token manager
|
||||
*/
|
||||
TokenManager* getTokenManager() const;
|
||||
|
||||
private:
|
||||
QString m_module_name;
|
||||
LogosAPIProvider* m_provider;
|
||||
mutable QHash<QString, LogosAPIClient*> m_clients; // Cache of clients per target module
|
||||
TokenManager* m_token_manager;
|
||||
};
|
||||
|
||||
#endif // LOGOS_API_H
|
||||
@@ -0,0 +1,179 @@
|
||||
#include "logos_api_client.h"
|
||||
#include "logos_api_consumer.h"
|
||||
#include "token_manager.h"
|
||||
|
||||
LogosAPIClient::LogosAPIClient(const QString& module_to_talk_to, const QString& origin_module, TokenManager* token_manager, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_consumer(new LogosAPIConsumer(module_to_talk_to, origin_module, token_manager, this))
|
||||
, m_token_manager(token_manager)
|
||||
, m_origin_module(origin_module)
|
||||
{
|
||||
}
|
||||
|
||||
LogosAPIClient::~LogosAPIClient()
|
||||
{
|
||||
// m_consumer will be deleted automatically as it's a child object
|
||||
}
|
||||
|
||||
QObject* LogosAPIClient::requestObject(const QString& objectName, int timeoutMs)
|
||||
{
|
||||
return m_consumer->requestObject(objectName, timeoutMs);
|
||||
}
|
||||
|
||||
bool LogosAPIClient::isConnected() const
|
||||
{
|
||||
return m_consumer->isConnected();
|
||||
}
|
||||
|
||||
QString LogosAPIClient::registryUrl() const
|
||||
{
|
||||
return m_consumer->registryUrl();
|
||||
}
|
||||
|
||||
bool LogosAPIClient::reconnect()
|
||||
{
|
||||
return m_consumer->reconnect();
|
||||
}
|
||||
|
||||
QVariant LogosAPIClient::invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariantList& args, int timeoutMs)
|
||||
{
|
||||
qDebug() << "LogosAPIClient: invoking remote method" << objectName << methodName << args;
|
||||
|
||||
// Get the token for the module
|
||||
QString token = getToken(objectName);
|
||||
|
||||
if (token.isEmpty() && objectName != "capability_module") {
|
||||
qDebug() << "LogosAPIClient: calling requestModule for" << objectName;
|
||||
LogosAPIConsumer* packageManagerConsumer = new LogosAPIConsumer("capability_module", m_origin_module, m_token_manager, this);
|
||||
QString capabilityToken = getToken("capability_module");
|
||||
QVariant result = packageManagerConsumer->invokeRemoteMethod(capabilityToken, "capability_module", "requestModule", QVariantList() << m_origin_module << objectName, timeoutMs);
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "LogosAPIClient: requestModule result for" << objectName << ":" << result.toString();
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
qDebug() << "================================================";
|
||||
|
||||
token = result.toString();
|
||||
}
|
||||
|
||||
return m_consumer->invokeRemoteMethod(token, objectName, methodName, args, timeoutMs);
|
||||
}
|
||||
|
||||
QVariant LogosAPIClient::invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg, int timeoutMs)
|
||||
{
|
||||
return invokeRemoteMethod(objectName, methodName, QVariantList() << arg, timeoutMs);
|
||||
}
|
||||
|
||||
QVariant LogosAPIClient::invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, int timeoutMs)
|
||||
{
|
||||
return invokeRemoteMethod(objectName, methodName, QVariantList() << arg1 << arg2, timeoutMs);
|
||||
}
|
||||
|
||||
QVariant LogosAPIClient::invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, int timeoutMs)
|
||||
{
|
||||
return invokeRemoteMethod(objectName, methodName, QVariantList() << arg1 << arg2 << arg3, timeoutMs);
|
||||
}
|
||||
|
||||
QVariant LogosAPIClient::invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, const QVariant& arg3,
|
||||
const QVariant& arg4, int timeoutMs)
|
||||
{
|
||||
return invokeRemoteMethod(objectName, methodName, QVariantList() << arg1 << arg2 << arg3 << arg4, timeoutMs);
|
||||
}
|
||||
|
||||
QVariant LogosAPIClient::invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, const QVariant& arg3,
|
||||
const QVariant& arg4, const QVariant& arg5, int timeoutMs)
|
||||
{
|
||||
return invokeRemoteMethod(objectName, methodName, QVariantList() << arg1 << arg2 << arg3 << arg4 << arg5, timeoutMs);
|
||||
}
|
||||
|
||||
void LogosAPIClient::onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName, std::function<void(const QString&, const QVariantList&)> callback)
|
||||
{
|
||||
m_consumer->onEvent(originObject, destinationObject, eventName, callback);
|
||||
}
|
||||
|
||||
void LogosAPIClient::onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName)
|
||||
{
|
||||
m_consumer->onEvent(originObject, destinationObject, eventName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void LogosAPIClient::invokeCallback(const QString& eventName, const QVariantList& data)
|
||||
{
|
||||
m_consumer->invokeCallback(eventName, data);
|
||||
}
|
||||
|
||||
void LogosAPIClient::onEventResponse(QObject* replica, const QString& eventName, const QVariantList& data)
|
||||
{
|
||||
// qDebug() << "LogosAPIClient: Received event:" << eventName << "with data:" << data;
|
||||
qDebug() << "LogosAPIClient: Received event:" << eventName;
|
||||
|
||||
if (eventName.isEmpty()) {
|
||||
qWarning() << "LogosAPIClient: Event name cannot be empty";
|
||||
return;
|
||||
}
|
||||
|
||||
// qDebug() << "LogosAPIClient: Emitting event:" << eventName << "with data:" << data;
|
||||
qDebug() << "LogosAPIClient: Emitting event:" << eventName;
|
||||
|
||||
// emit the eventResponse signal of replica
|
||||
QMetaObject::invokeMethod(replica, "eventResponse", Qt::QueuedConnection, Q_ARG(QString, eventName), Q_ARG(QVariantList, data));
|
||||
}
|
||||
|
||||
bool LogosAPIClient::informModuleToken(const QString& authToken, const QString& moduleName, const QString& token)
|
||||
{
|
||||
return m_consumer->informModuleToken(authToken, moduleName, token);
|
||||
}
|
||||
|
||||
bool LogosAPIClient::informModuleToken_module(const QString& authToken, const QString& originModule, const QString& moduleName, const QString& token)
|
||||
{
|
||||
return m_consumer->informModuleToken_module(authToken, originModule, moduleName, token);
|
||||
}
|
||||
|
||||
TokenManager* LogosAPIClient::getTokenManager() const
|
||||
{
|
||||
return m_token_manager;
|
||||
}
|
||||
|
||||
QString LogosAPIClient::getToken(const QString& module_name)
|
||||
{
|
||||
qDebug() << "getoken: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-";
|
||||
qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-";
|
||||
// if (m_token_manager) {
|
||||
qDebug() << "LogosAPIClient: printing keys";
|
||||
QList<QString> keys = m_token_manager->getTokenKeys();
|
||||
for (const QString& key : keys) {
|
||||
qDebug() << "LogosAPIClient: Token key:" << key << "value:" << m_token_manager->getToken(key);
|
||||
}
|
||||
|
||||
QString token = m_token_manager->getToken(module_name);
|
||||
if (!token.isEmpty()) {
|
||||
qDebug() << "LogosAPIClient: Found token for module:" << module_name;
|
||||
return token;
|
||||
} else {
|
||||
qDebug() << "LogosAPIClient: No token found for module:" << module_name;
|
||||
}
|
||||
// } else {
|
||||
// qDebug() << "LogosAPIClient: No token manager found - using default AUTH_TOKEN";
|
||||
// }
|
||||
|
||||
qDebug() << "LogosAPIClient: No stored token for module:" << module_name;
|
||||
qDebug() << "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-";
|
||||
|
||||
// TODO: this is breaking here for core_manager
|
||||
// return AUTH_TOKEN;
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
#ifndef LOGOS_API_CLIENT_H
|
||||
#define LOGOS_API_CLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QMap>
|
||||
#include <functional>
|
||||
|
||||
class LogosAPIConsumer;
|
||||
class TokenManager;
|
||||
|
||||
/**
|
||||
* @brief LogosAPIClient provides a high-level interface for remote method calls
|
||||
*
|
||||
* This class serves as a facade over LogosAPIConsumer, providing a clean interface
|
||||
* for applications that need to call remote methods and handle events. It includes
|
||||
* additional logic like token management and request routing.
|
||||
*/
|
||||
class LogosAPIClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new LogosAPIClient
|
||||
* @param module_to_talk_to The name of the module to connect to
|
||||
* @param origin_module The name of the originating module
|
||||
* @param token_manager Pointer to the token manager instance
|
||||
* @param parent Parent QObject
|
||||
*/
|
||||
explicit LogosAPIClient(const QString& module_to_talk_to, const QString& origin_module, TokenManager* token_manager, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~LogosAPIClient();
|
||||
|
||||
/**
|
||||
* @brief Request a remote object replica by name
|
||||
* @param objectName The name of the remote object to acquire
|
||||
* @param timeoutMs Timeout in milliseconds to wait for the replica to be ready
|
||||
* @return QObject* pointer to the replica, or nullptr if failed
|
||||
*/
|
||||
QObject* requestObject(const QString& objectName, int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Check if the client is connected to the registry
|
||||
* @return true if connected, false otherwise
|
||||
*/
|
||||
bool isConnected() const;
|
||||
|
||||
/**
|
||||
* @brief Get the registry URL this client is connected to
|
||||
* @return QString containing the registry URL
|
||||
*/
|
||||
QString registryUrl() const;
|
||||
|
||||
/**
|
||||
* @brief Reconnect to the registry
|
||||
* @return true if reconnection successful, false otherwise
|
||||
*/
|
||||
bool reconnect();
|
||||
|
||||
/**
|
||||
* @brief Invoke a remote method on a remote object
|
||||
* @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 timeoutMs Timeout in milliseconds to wait for the result
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
QVariant invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariantList& args = QVariantList(), int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Invoke a remote method on a remote object with a single argument
|
||||
* @param objectName The name of the remote object
|
||||
* @param methodName The name of the method to call
|
||||
* @param arg Argument to pass to the method
|
||||
* @param timeoutMs Timeout in milliseconds to wait for the result
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
QVariant invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg, int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Invoke a remote method on a remote object with two arguments
|
||||
* @param objectName The name of the remote object
|
||||
* @param methodName The name of the method to call
|
||||
* @param arg1 First argument to pass to the method
|
||||
* @param arg2 Second argument to pass to the method
|
||||
* @param timeoutMs Timeout in milliseconds to wait for the result
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
QVariant invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Invoke a remote method on a remote object with three arguments
|
||||
* @param objectName The name of the remote object
|
||||
* @param methodName The name of the method to call
|
||||
* @param arg1 First argument to pass to the method
|
||||
* @param arg2 Second argument to pass to the method
|
||||
* @param arg3 Third argument to pass to the method
|
||||
* @param timeoutMs Timeout in milliseconds to wait for the result
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
QVariant invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, const QVariant& arg3, int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Invoke a remote method on a remote object with four arguments
|
||||
* @param objectName The name of the remote object
|
||||
* @param methodName The name of the method to call
|
||||
* @param arg1 First argument to pass to the method
|
||||
* @param arg2 Second argument to pass to the method
|
||||
* @param arg3 Third argument to pass to the method
|
||||
* @param arg4 Fourth argument to pass to the method
|
||||
* @param timeoutMs Timeout in milliseconds to wait for the result
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
QVariant invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, const QVariant& arg3,
|
||||
const QVariant& arg4, int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Invoke a remote method on a remote object with five arguments
|
||||
* @param objectName The name of the remote object
|
||||
* @param methodName The name of the method to call
|
||||
* @param arg1 First argument to pass to the method
|
||||
* @param arg2 Second argument to pass to the method
|
||||
* @param arg3 Third argument to pass to the method
|
||||
* @param arg4 Fourth argument to pass to the method
|
||||
* @param arg5 Fifth argument to pass to the method
|
||||
* @param timeoutMs Timeout in milliseconds to wait for the result
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
QVariant invokeRemoteMethod(const QString& objectName, const QString& methodName,
|
||||
const QVariant& arg1, const QVariant& arg2, const QVariant& arg3,
|
||||
const QVariant& arg4, const QVariant& arg5, int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Register an event listener for the specified event name
|
||||
* @param originObject The object that will emit the event
|
||||
* @param destinationObject The object that will receive the event
|
||||
* @param eventName The name of the event to listen for
|
||||
* @param callback Function to call when the event is triggered
|
||||
*/
|
||||
void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName,
|
||||
std::function<void(const QString&, const QVariantList&)> callback);
|
||||
|
||||
/**
|
||||
* @brief Register an event listener without callback (connects to destinationObject's slot)
|
||||
* @param originObject The object that will emit the event
|
||||
* @param destinationObject The object that will receive the event
|
||||
* @param eventName The name of the event to listen for
|
||||
*/
|
||||
void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Emit an event response (for plugins that also act as event sources)
|
||||
* @param replica The replica object that should receive the event
|
||||
* @param eventName The name of the event
|
||||
* @param data The event data
|
||||
*/
|
||||
void onEventResponse(QObject* replica, const QString& eventName, const QVariantList& data);
|
||||
|
||||
/**
|
||||
* @brief Inform a module about a token
|
||||
* @param authToken Authentication token for the operation
|
||||
* @param moduleName The name of the module
|
||||
* @param token The token to inform the module about
|
||||
* @return bool true if successful, false otherwise
|
||||
*/
|
||||
bool informModuleToken(const QString& authToken, const QString& moduleName, const QString& token);
|
||||
|
||||
bool informModuleToken_module(const QString& authToken, const QString& originModule, const QString& moduleName, const QString& token);
|
||||
|
||||
/**
|
||||
* @brief Get the token manager instance
|
||||
* @return TokenManager* Pointer to the token manager
|
||||
*/
|
||||
TokenManager* getTokenManager() const;
|
||||
|
||||
/**
|
||||
* @brief Get authentication token for a module
|
||||
* @param module_name The module name to get token for
|
||||
* @return QString containing the token
|
||||
*/
|
||||
QString getToken(const QString& module_name);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Helper slot to invoke stored callbacks
|
||||
* @param eventName The name of the event that was triggered
|
||||
* @param data The event data to pass to the callback
|
||||
*/
|
||||
void invokeCallback(const QString& eventName, const QVariantList& data);
|
||||
|
||||
private:
|
||||
LogosAPIConsumer* m_consumer;
|
||||
QMap<QString, QString> m_tokens;
|
||||
TokenManager* m_token_manager;
|
||||
QString m_origin_module;
|
||||
};
|
||||
|
||||
#endif // LOGOS_API_CLIENT_H
|
||||
@@ -0,0 +1,321 @@
|
||||
#include "logos_api_consumer.h"
|
||||
#include "module_proxy.h"
|
||||
#include "logos_api_client.h"
|
||||
#include "token_manager.h"
|
||||
#include <QRemoteObjectNode>
|
||||
#include <QRemoteObjectReplica>
|
||||
#include <QRemoteObjectPendingCall>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QMetaObject>
|
||||
#include <QTime>
|
||||
#include <string>
|
||||
|
||||
LogosAPIConsumer::LogosAPIConsumer(const QString& module_to_talk_to, const QString& origin_module, TokenManager* token_manager, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_node(nullptr)
|
||||
, m_registryUrl(QString("local:logos_%1").arg(module_to_talk_to))
|
||||
, m_connected(false)
|
||||
, m_token_manager(token_manager)
|
||||
{
|
||||
m_node = new QRemoteObjectNode(this);
|
||||
connectToRegistry();
|
||||
}
|
||||
|
||||
LogosAPIConsumer::~LogosAPIConsumer()
|
||||
{
|
||||
// Clean up event callbacks and connections
|
||||
for (auto it = m_connections.begin(); it != m_connections.end(); ++it) {
|
||||
QObject::disconnect(it.value());
|
||||
}
|
||||
m_eventCallbacks.clear();
|
||||
m_connections.clear();
|
||||
|
||||
// QRemoteObjectNode will be deleted automatically as it's a child object
|
||||
}
|
||||
|
||||
QObject* LogosAPIConsumer::requestObject(const QString& objectName, int timeoutMs)
|
||||
{
|
||||
qDebug() << "LogosAPIConsumer: Requesting object:" << objectName << "at" << QTime::currentTime().toString("hh:mm:ss.zzz");
|
||||
if (!m_connected) {
|
||||
qWarning() << "LogosAPIConsumer: Not connected to registry. Cannot request object:" << objectName;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (objectName.isEmpty()) {
|
||||
qWarning() << "LogosAPIConsumer: Object name cannot be empty";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
qDebug() << "LogosAPIConsumer: Requesting object:" << objectName;
|
||||
|
||||
// Acquire the dynamic replica
|
||||
QRemoteObjectReplica* replica = m_node->acquireDynamic(objectName);
|
||||
if (!replica) {
|
||||
qWarning() << "LogosAPIConsumer: Failed to acquire replica for object:" << objectName;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Wait for the replica to be initialized
|
||||
if (!replica->waitForSource(timeoutMs)) {
|
||||
qWarning() << "LogosAPIConsumer: Timeout waiting for object replica to be ready:" << objectName;
|
||||
delete replica;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
qDebug() << "LogosAPIConsumer: Successfully acquired replica for object:" << objectName;
|
||||
qDebug() << "LogosAPIConsumer: Replica acquired at" << QTime::currentTime().toString("hh:mm:ss.zzz");
|
||||
return replica;
|
||||
}
|
||||
|
||||
bool LogosAPIConsumer::isConnected() const
|
||||
{
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
QString LogosAPIConsumer::registryUrl() const
|
||||
{
|
||||
return m_registryUrl;
|
||||
}
|
||||
|
||||
bool LogosAPIConsumer::reconnect()
|
||||
{
|
||||
qDebug() << "LogosAPIConsumer: Attempting to reconnect to registry:" << m_registryUrl;
|
||||
|
||||
// Disconnect first if already connected
|
||||
if (m_connected) {
|
||||
// Note: QRemoteObjectNode doesn't have a direct disconnect method
|
||||
// We'll create a new node instead
|
||||
m_node->deleteLater();
|
||||
m_node = new QRemoteObjectNode(this);
|
||||
m_connected = false;
|
||||
}
|
||||
|
||||
return connectToRegistry();
|
||||
}
|
||||
|
||||
bool LogosAPIConsumer::connectToRegistry()
|
||||
{
|
||||
if (!m_node) {
|
||||
qWarning() << "LogosAPIConsumer: Remote object node is null";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_registryUrl.isEmpty()) {
|
||||
qWarning() << "LogosAPIConsumer: Registry URL is empty";
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "LogosAPIConsumer: Connecting to registry:" << m_registryUrl;
|
||||
qDebug() << "LogosAPIConsumer: Connecting to registry at" << QTime::currentTime().toString("hh:mm:ss.zzz");
|
||||
|
||||
// Connect to the registry node
|
||||
QUrl url(m_registryUrl);
|
||||
bool success = m_node->connectToNode(url);
|
||||
|
||||
if (success) {
|
||||
m_connected = true;
|
||||
qDebug() << "LogosAPIConsumer: Successfully connected to registry:" << m_registryUrl;
|
||||
} else {
|
||||
m_connected = false;
|
||||
qWarning() << "LogosAPIConsumer: Failed to connect to registry:" << m_registryUrl;
|
||||
}
|
||||
qDebug() << "LogosAPIConsumer: Connected to registry at" << QTime::currentTime().toString("hh:mm:ss.zzz");
|
||||
|
||||
return m_connected;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QVariant LogosAPIConsumer::invokeRemoteMethod(const QString& authToken, const QString& objectName, const QString& methodName,
|
||||
const QVariantList& args, int timeoutMs)
|
||||
{
|
||||
qDebug() << "LogosAPIConsumer: Calling invokeRemoteMethod with params:" << authToken << objectName << methodName << args << timeoutMs;
|
||||
|
||||
// This method handles both ModuleProxy-wrapped modules (template_module, package_manager)
|
||||
// and direct remote object calls for other modules
|
||||
QObject* replica = requestObject(objectName, timeoutMs);
|
||||
if (!replica) {
|
||||
qWarning() << "LogosAPIConsumer: Failed to acquire replica for object:" << objectName;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// Try to cast to ModuleProxy first (in case the replica is a wrapped module)
|
||||
ModuleProxy* moduleProxy = qobject_cast<ModuleProxy*>(replica);
|
||||
if (moduleProxy) {
|
||||
QVariant result = moduleProxy->callRemoteMethod(authToken, methodName, args);
|
||||
delete replica;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Fallback: use QMetaObject::invokeMethod directly
|
||||
// Note: Remote objects' callRemoteMethod returns QRemoteObjectPendingCall, not QVariant
|
||||
QRemoteObjectPendingCall pendingCall;
|
||||
bool success = QMetaObject::invokeMethod(
|
||||
replica,
|
||||
"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 replica;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// Wait for the result
|
||||
pendingCall.waitForFinished(timeoutMs);
|
||||
delete replica;
|
||||
|
||||
if (!pendingCall.isFinished() || pendingCall.error() != QRemoteObjectPendingCall::NoError) {
|
||||
qWarning() << "LogosAPIConsumer: Remote callRemoteMethod failed or timed out:" << pendingCall.error();
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return pendingCall.returnValue();
|
||||
}
|
||||
|
||||
void LogosAPIConsumer::onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName, std::function<void(const QString&, const QVariantList&)> callback)
|
||||
{
|
||||
qDebug() << "LogosAPIConsumer: Registering event listener for event:" << eventName;
|
||||
|
||||
// Store the callback for this event name
|
||||
m_eventCallbacks[eventName].append(callback);
|
||||
|
||||
// Check if we already have a connection for this origin object
|
||||
if (!m_connections.contains(originObject)) {
|
||||
// Create new connection only if it doesn't exist
|
||||
auto connection = QObject::connect(originObject, SIGNAL(eventResponse(QString, QVariantList)),
|
||||
this, SLOT(invokeCallback(QString, QVariantList)));
|
||||
|
||||
if (connection) {
|
||||
m_connections[originObject] = connection;
|
||||
qDebug() << "LogosAPIConsumer: Created new connection for origin object";
|
||||
} else {
|
||||
qWarning() << "LogosAPIConsumer: Failed to create connection for event:" << eventName;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "LogosAPIConsumer: Reusing existing connection for origin object";
|
||||
}
|
||||
|
||||
qDebug() << "LogosAPIConsumer: Registered callback for event:" << eventName;
|
||||
}
|
||||
|
||||
void LogosAPIConsumer::invokeCallback(const QString& eventName, const QVariantList& data)
|
||||
{
|
||||
// qDebug() << "LogosAPIConsumer: invokeCallback called for event:" << eventName;
|
||||
|
||||
// Call all registered callbacks
|
||||
// Note: This will call all callbacks for any event. In a more sophisticated implementation,
|
||||
// you might want to store event names with callbacks to filter them.
|
||||
for (const auto& callback : m_eventCallbacks[eventName]) {
|
||||
try {
|
||||
callback(eventName, data);
|
||||
} catch (...) {
|
||||
qWarning() << "LogosAPIConsumer: Exception in callback for event:" << eventName;
|
||||
}
|
||||
}
|
||||
|
||||
// qDebug() << "LogosAPIConsumer: Called" << m_eventCallbacks[eventName].size() << "callbacks for event:" << eventName;
|
||||
}
|
||||
|
||||
void LogosAPIConsumer::onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName)
|
||||
{
|
||||
qDebug() << "LogosAPIConsumer: Registering event listener for event:" << eventName << "(connecting to destination slot)";
|
||||
|
||||
// connect to the eventResponse signal of the destinationObject's slot
|
||||
QObject::connect(originObject, SIGNAL(eventResponse(QString, QVariantList)),
|
||||
destinationObject, SLOT(onEventResponse(QString, QVariantList)), Qt::AutoConnection);
|
||||
}
|
||||
|
||||
bool LogosAPIConsumer::informModuleToken(const QString& authToken, const QString& moduleName, const QString& token)
|
||||
{
|
||||
qDebug() << "LogosAPIConsumer: Informing module token for module:" << moduleName << "with token:" << token;
|
||||
|
||||
// Request the ModuleProxy object
|
||||
QObject* replica = requestObject("capability_module", 20000);
|
||||
if (!replica) {
|
||||
qWarning() << "LogosAPIConsumer: Failed to acquire replica for object:" << "capability_module";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use QRemoteObjectPendingCall similar to invokeRemoteMethod
|
||||
QRemoteObjectPendingCall pendingCall;
|
||||
bool success = QMetaObject::invokeMethod(
|
||||
replica,
|
||||
"informModuleToken",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QRemoteObjectPendingCall, pendingCall),
|
||||
Q_ARG(QString, authToken),
|
||||
Q_ARG(QString, moduleName),
|
||||
Q_ARG(QString, token)
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
qWarning() << "LogosAPIConsumer: Failed to invoke informModuleToken on replica";
|
||||
delete replica;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for the result
|
||||
pendingCall.waitForFinished(20000);
|
||||
delete replica;
|
||||
|
||||
if (!pendingCall.isFinished() || pendingCall.error() != QRemoteObjectPendingCall::NoError) {
|
||||
qWarning() << "LogosAPIConsumer: Remote informModuleToken failed or timed out:" << pendingCall.error();
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant result = pendingCall.returnValue();
|
||||
qDebug() << "LogosAPIConsumer: informModuleToken completed with result:" << result;
|
||||
|
||||
return result.toBool();
|
||||
}
|
||||
|
||||
bool LogosAPIConsumer::informModuleToken_module(const QString& authToken, const QString& originModule, const QString& moduleName, const QString& token)
|
||||
{
|
||||
qDebug() << "LogosAPIConsumer: Informing module token for module:" << moduleName << "with token:" << token;
|
||||
|
||||
// Request the ModuleProxy object
|
||||
QObject* replica = requestObject(originModule, 20000);
|
||||
if (!replica) {
|
||||
qWarning() << "LogosAPIConsumer: Failed to acquire replica for object:" << "capability_module";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use QRemoteObjectPendingCall similar to invokeRemoteMethod
|
||||
QRemoteObjectPendingCall pendingCall;
|
||||
bool success = QMetaObject::invokeMethod(
|
||||
replica,
|
||||
"informModuleToken",
|
||||
Qt::DirectConnection,
|
||||
Q_RETURN_ARG(QRemoteObjectPendingCall, pendingCall),
|
||||
Q_ARG(QString, authToken),
|
||||
Q_ARG(QString, moduleName),
|
||||
Q_ARG(QString, token)
|
||||
);
|
||||
|
||||
if (!success) {
|
||||
qWarning() << "LogosAPIConsumer: Failed to invoke informModuleToken on replica";
|
||||
delete replica;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for the result
|
||||
pendingCall.waitForFinished(20000);
|
||||
delete replica;
|
||||
|
||||
if (!pendingCall.isFinished() || pendingCall.error() != QRemoteObjectPendingCall::NoError) {
|
||||
qWarning() << "LogosAPIConsumer: Remote informModuleToken failed or timed out:" << pendingCall.error();
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant result = pendingCall.returnValue();
|
||||
qDebug() << "LogosAPIConsumer: informModuleToken completed with result:" << result;
|
||||
|
||||
return result.toBool();
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#ifndef LOGOS_API_CONSUMER_H
|
||||
#define LOGOS_API_CONSUMER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QHash>
|
||||
#include <QMap>
|
||||
#include <functional>
|
||||
|
||||
class QRemoteObjectNode;
|
||||
class TokenManager;
|
||||
|
||||
/**
|
||||
* @brief LogosAPIConsumer handles connecting to remote objects and invoking their methods
|
||||
*
|
||||
* This class is responsible for the consumer/client side functionality:
|
||||
* - Connecting to remote object registries
|
||||
* - Requesting remote object replicas
|
||||
* - Invoking methods on remote objects
|
||||
* - Handling events from remote objects
|
||||
*/
|
||||
class LogosAPIConsumer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new LogosAPIConsumer
|
||||
* @param module_to_talk_to The name of the module to connect to
|
||||
* @param origin_module The name of the originating module
|
||||
* @param token_manager Pointer to the token manager instance
|
||||
* @param parent Parent QObject
|
||||
*/
|
||||
explicit LogosAPIConsumer(const QString& module_to_talk_to, const QString& origin_module, TokenManager* token_manager, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destructor - cleans up connections and resources
|
||||
*/
|
||||
~LogosAPIConsumer();
|
||||
|
||||
/**
|
||||
* @brief Request a remote object replica by name
|
||||
* @param objectName The name of the remote object to acquire
|
||||
* @param timeoutMs Timeout in milliseconds to wait for the replica to be ready
|
||||
* @return QObject* pointer to the replica, or nullptr if failed
|
||||
*/
|
||||
QObject* requestObject(const QString& objectName, int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Check if the consumer is connected to the registry
|
||||
* @return true if connected, false otherwise
|
||||
*/
|
||||
bool isConnected() const;
|
||||
|
||||
/**
|
||||
* @brief Get the registry URL this consumer is connected to
|
||||
* @return QString containing the registry URL
|
||||
*/
|
||||
QString registryUrl() const;
|
||||
|
||||
/**
|
||||
* @brief Reconnect to the registry
|
||||
* @return true if reconnection successful, false otherwise
|
||||
*/
|
||||
bool reconnect();
|
||||
|
||||
/**
|
||||
* @brief Invoke a remote method on a remote object
|
||||
* @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 timeoutMs Timeout in milliseconds to wait for the result
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
QVariant invokeRemoteMethod(const QString& authToken, const QString& objectName, const QString& methodName,
|
||||
const QVariantList& args = QVariantList(), int timeoutMs = 20000);
|
||||
|
||||
/**
|
||||
* @brief Register an event listener for the specified event name
|
||||
* @param originObject The object that will emit the event
|
||||
* @param destinationObject The object that will receive the event
|
||||
* @param eventName The name of the event to listen for
|
||||
* @param callback Function to call when the event is triggered
|
||||
*/
|
||||
void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName,
|
||||
std::function<void(const QString&, const QVariantList&)> callback);
|
||||
|
||||
/**
|
||||
* @brief Register an event listener without callback (connects to destinationObject's slot)
|
||||
* @param originObject The object that will emit the event
|
||||
* @param destinationObject The object that will receive the event
|
||||
* @param eventName The name of the event to listen for
|
||||
*/
|
||||
void onEvent(QObject* originObject, QObject* destinationObject, const QString& eventName);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Helper slot to invoke stored callbacks
|
||||
* @param eventName The name of the event that was triggered
|
||||
* @param data The event data to pass to the callback
|
||||
*/
|
||||
void invokeCallback(const QString& eventName, const QVariantList& data);
|
||||
|
||||
/**
|
||||
* @brief Inform a module about a token
|
||||
* @param authToken Authentication token for the operation
|
||||
* @param moduleName The name of the module
|
||||
* @param token The token to inform the module about
|
||||
* @return bool true if successful, false otherwise
|
||||
*/
|
||||
bool informModuleToken(const QString& authToken, const QString& moduleName, const QString& token);
|
||||
|
||||
bool informModuleToken_module(const QString& authToken, const QString& originModule, const QString& moduleName, const QString& token);
|
||||
|
||||
private:
|
||||
QRemoteObjectNode* m_node;
|
||||
QString m_registryUrl;
|
||||
bool m_connected;
|
||||
QMap<QString, QString> m_tokens;
|
||||
TokenManager* m_token_manager;
|
||||
|
||||
// Store callbacks by event name
|
||||
QHash<QString, QList<std::function<void(const QString&, const QVariantList&)>>> m_eventCallbacks;
|
||||
|
||||
// Track existing connections by origin object to avoid duplicates
|
||||
QHash<QObject*, QMetaObject::Connection> m_connections;
|
||||
|
||||
/**
|
||||
* @brief Internal method to establish connection to the registry
|
||||
* @return true if connection successful, false otherwise
|
||||
*/
|
||||
bool connectToRegistry();
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // LOGOS_API_CONSUMER_H
|
||||
@@ -0,0 +1,117 @@
|
||||
#include "logos_api_provider.h"
|
||||
#include "module_proxy.h"
|
||||
#include "logos_api.h"
|
||||
#include <QRemoteObjectRegistryHost>
|
||||
#include <QDebug>
|
||||
#include <QUrl>
|
||||
#include <QMetaObject>
|
||||
|
||||
LogosAPIProvider::LogosAPIProvider(const QString& module_name, QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_registryHost(nullptr)
|
||||
, m_registryUrl(QString("local:logos_%1").arg(module_name))
|
||||
, m_moduleProxy(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
LogosAPIProvider::~LogosAPIProvider()
|
||||
{
|
||||
// QRemoteObjectRegistryHost will be deleted automatically as it's a child object
|
||||
// ModuleProxy will be deleted automatically as it's a child object
|
||||
}
|
||||
|
||||
bool LogosAPIProvider::registerObject(const QString& name, QObject* object)
|
||||
{
|
||||
if (!object) {
|
||||
qWarning() << "LogosAPIProvider: Cannot register null object";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name.isEmpty()) {
|
||||
qWarning() << "LogosAPIProvider: Cannot register object with empty name";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if a ModuleProxy was already created - only allow one registration
|
||||
if (m_moduleProxy) {
|
||||
qCritical() << "LogosAPIProvider: Object already registered. Only one registration per provider is allowed";
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "LogosAPIProvider: Creating ModuleProxy for" << name << "wrapping the provided object";
|
||||
|
||||
// Before wrapping with ModuleProxy, call initLogos if the method exists
|
||||
// Check if the object has an initLogos method and call it with the parent (LogosAPI instance)
|
||||
int methodIndex = object->metaObject()->indexOfMethod("initLogos(LogosAPI*)");
|
||||
if (methodIndex != -1) {
|
||||
qDebug() << "LogosAPIProvider: Calling initLogos on object before wrapping";
|
||||
bool methodSuccess = QMetaObject::invokeMethod(object, "initLogos",
|
||||
Qt::DirectConnection,
|
||||
Q_ARG(LogosAPI*, qobject_cast<LogosAPI*>(parent())));
|
||||
if (methodSuccess) {
|
||||
qDebug() << "LogosAPIProvider: Successfully called initLogos on object";
|
||||
} else {
|
||||
qWarning() << "LogosAPIProvider: Failed to call initLogos on object";
|
||||
}
|
||||
} else {
|
||||
qDebug() << "LogosAPIProvider: Object does not have initLogos method, skipping";
|
||||
}
|
||||
|
||||
m_moduleProxy = new ModuleProxy(object, this);
|
||||
object = m_moduleProxy;
|
||||
|
||||
if (!m_registryHost) {
|
||||
m_registryHost = new QRemoteObjectRegistryHost(QUrl(m_registryUrl));
|
||||
if (!m_registryHost) {
|
||||
qCritical() << "LogosAPIProvider: Failed to create registry host";
|
||||
return false;
|
||||
}
|
||||
qDebug() << "LogosAPIProvider: Created registry host with URL:" << m_registryUrl;
|
||||
}
|
||||
|
||||
bool success = m_registryHost->enableRemoting(object, name);
|
||||
if (success) {
|
||||
qDebug() << "LogosAPIProvider: Successfully registered object with name:" << name;
|
||||
} else {
|
||||
qCritical() << "LogosAPIProvider: Failed to register object with name:" << name;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
QString LogosAPIProvider::registryUrl() const
|
||||
{
|
||||
return m_registryUrl;
|
||||
}
|
||||
|
||||
bool LogosAPIProvider::saveToken(const QString& from_module_name, const QString& token)
|
||||
{
|
||||
if (!m_moduleProxy) {
|
||||
qWarning() << "LogosAPIProvider: Cannot save token - no module proxy available";
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "LogosAPIProvider: Delegating saveToken call to module proxy for module:" << from_module_name;
|
||||
return m_moduleProxy->saveToken(from_module_name, token);
|
||||
}
|
||||
|
||||
void LogosAPIProvider::onEventResponse(QObject* replica, const QString& eventName, const QVariantList& data)
|
||||
{
|
||||
// qDebug() << "LogosAPIProvider: Received event:" << eventName << "with data:" << data;
|
||||
qDebug() << "LogosAPIProvider: Received event:" << eventName;
|
||||
|
||||
if (eventName.isEmpty()) {
|
||||
qWarning() << "LogosAPIProvider: Event name cannot be empty";
|
||||
return;
|
||||
}
|
||||
|
||||
// qDebug() << "LogosAPIProvider: Emitting event:" << eventName << "with data:" << data;
|
||||
qDebug() << "LogosAPIProvider: Emitting event:" << eventName;
|
||||
|
||||
// emit the eventResponse signal of replica
|
||||
QMetaObject::invokeMethod(replica, "eventResponse", Qt::QueuedConnection, Q_ARG(QString, eventName), Q_ARG(QVariantList, data));
|
||||
// QMetaObject::invokeMethod(replica, "eventResponse_another", Qt::QueuedConnection, Q_ARG(QString, eventName), Q_ARG(QVariantList, data));
|
||||
// TODO: try queued connection instead
|
||||
// QMetaObject::invokeMethod(replica, "eventResponse_another", Qt::DirectConnection, Q_ARG(QString, eventName), Q_ARG(QVariantList, data));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
#ifndef LOGOS_API_PROVIDER_H
|
||||
#define LOGOS_API_PROVIDER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QMap>
|
||||
|
||||
class QRemoteObjectRegistryHost;
|
||||
class ModuleProxy;
|
||||
|
||||
/**
|
||||
* @brief LogosAPIProvider handles registering objects for remote access
|
||||
*
|
||||
* This class is responsible for the provider/server side functionality:
|
||||
* - Creating registry hosts
|
||||
* - Registering objects for remote access
|
||||
* - Handling event responses
|
||||
*/
|
||||
class LogosAPIProvider : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new LogosAPIProvider
|
||||
* @param module_name The name of this module
|
||||
* @param parent Parent QObject
|
||||
*/
|
||||
explicit LogosAPIProvider(const QString& module_name, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destructor - cleans up registry host
|
||||
*/
|
||||
~LogosAPIProvider();
|
||||
|
||||
/**
|
||||
* @brief Register an object to be available for remote access
|
||||
* @param name The name to register the object under
|
||||
* @param object The object to register
|
||||
* @param authToken Authentication token for the object
|
||||
* @return true if registration successful, false otherwise
|
||||
*/
|
||||
bool registerObject(const QString& name, QObject* object);
|
||||
|
||||
/**
|
||||
* @brief Get the registry URL for this provider
|
||||
* @return QString containing the registry URL
|
||||
*/
|
||||
QString registryUrl() const;
|
||||
|
||||
/**
|
||||
* @brief Save a token from a module via the proxy
|
||||
* @param from_module_name The name of the module providing the token
|
||||
* @param token The token to save
|
||||
* @return bool true if token was saved successfully, false otherwise
|
||||
*/
|
||||
bool saveToken(const QString& from_module_name, const QString& token);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief Handle event responses from objects
|
||||
* @param replica The replica object that should receive the event
|
||||
* @param eventName The name of the event
|
||||
* @param data The event data
|
||||
*/
|
||||
void onEventResponse(QObject* replica, const QString& eventName, const QVariantList& data);
|
||||
|
||||
private:
|
||||
QRemoteObjectRegistryHost* m_registryHost;
|
||||
QString m_registryUrl;
|
||||
QMap<QString, QString> m_tokens;
|
||||
ModuleProxy* m_moduleProxy;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif // LOGOS_API_PROVIDER_H
|
||||
@@ -0,0 +1,429 @@
|
||||
#include "module_proxy.h"
|
||||
#include <QDebug>
|
||||
#include <QMetaObject>
|
||||
#include <QMetaMethod>
|
||||
#include <QMetaType>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QStringList>
|
||||
#include "../core/interface.h"
|
||||
#include "logos_api.h"
|
||||
#include "token_manager.h"
|
||||
|
||||
// Helper macro to simplify method invocation with return types
|
||||
#define INVOKE_METHOD_WITH_RETURN(returnType, castType) \
|
||||
do { \
|
||||
castType* result = static_cast<castType*>(returnValue); \
|
||||
switch (args.size()) { \
|
||||
case 0: \
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result)); \
|
||||
case 1: \
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg); \
|
||||
case 2: \
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg); \
|
||||
case 3: \
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg); \
|
||||
case 4: \
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg); \
|
||||
case 5: \
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, Q_RETURN_ARG(returnType, *result), scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg, scopedArgs[4].arg); \
|
||||
default: \
|
||||
qWarning() << "ModuleProxy: Currently supports 0-5 arguments. Got:" << args.size(); \
|
||||
return false; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
namespace {
|
||||
class ScopedQArg {
|
||||
public:
|
||||
ScopedQArg(QMetaMethodArgument a, std::function<void(const void*)> d)
|
||||
: arg(a), deleter(std::move(d)) {}
|
||||
|
||||
~ScopedQArg() {
|
||||
if (deleter) {
|
||||
deleter(arg.data);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedQArg(ScopedQArg&& other)
|
||||
: arg(std::move(other.arg)), deleter(std::move(other.deleter)) {
|
||||
other.deleter = nullptr;
|
||||
}
|
||||
ScopedQArg& operator=(ScopedQArg&&) = delete;
|
||||
ScopedQArg(const ScopedQArg&) = delete;
|
||||
ScopedQArg& operator=(const ScopedQArg&) = delete;
|
||||
|
||||
QMetaMethodArgument arg;
|
||||
|
||||
private:
|
||||
std::function<void(const void*)> deleter;
|
||||
};
|
||||
|
||||
auto toScopedQArgs(const QVariantList& args)
|
||||
{
|
||||
auto scopedArgs = std::vector<ScopedQArg>{};
|
||||
for (const auto& arg : args) {
|
||||
switch (arg.typeId()) {
|
||||
case QMetaType::Int: {
|
||||
auto value = new int{arg.toInt()};
|
||||
scopedArgs.emplace_back(
|
||||
Q_ARG(int, *value),
|
||||
[](const void* data) {
|
||||
delete static_cast<const int*>(data);
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case QMetaType::QString:
|
||||
default: {
|
||||
auto value = new QString{arg.toString()};
|
||||
scopedArgs.emplace_back(
|
||||
Q_ARG(QString, *value),
|
||||
[](const void* data) {
|
||||
delete static_cast<const QString*>(data);
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return scopedArgs;
|
||||
}
|
||||
|
||||
// Helper method to invoke methods with different return types and argument counts
|
||||
bool invokeMethodByArgCount(QObject *module, const QString& methodName, const QVariantList& args, void* returnValue, const char* returnTypeName)
|
||||
{
|
||||
// Store the UTF-8 data to ensure it stays in scope
|
||||
QByteArray methodNameBytes = methodName.toUtf8();
|
||||
const char* methodNameCStr = methodNameBytes.constData();
|
||||
|
||||
auto scopedArgs = toScopedQArgs(args);
|
||||
|
||||
if (returnValue == nullptr) {
|
||||
// Void method - no return value
|
||||
switch (args.size()) {
|
||||
case 0:
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection);
|
||||
case 1:
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg);
|
||||
case 2:
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg);
|
||||
case 3:
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg);
|
||||
case 4:
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg);
|
||||
case 5:
|
||||
return QMetaObject::invokeMethod(module, methodNameCStr, Qt::DirectConnection, scopedArgs[0].arg, scopedArgs[1].arg, scopedArgs[2].arg, scopedArgs[3].arg, scopedArgs[4].arg);
|
||||
default:
|
||||
qWarning() << "ModuleProxy: Currently supports 0-5 arguments. Got:" << args.size();
|
||||
return false;
|
||||
}
|
||||
} else if (strcmp(returnTypeName, "bool") == 0) {
|
||||
qDebug() << "ModuleProxy: invokeMethodByArgCount - bool case with" << args.size() << "arguments";
|
||||
INVOKE_METHOD_WITH_RETURN(bool, bool);
|
||||
} else if (strcmp(returnTypeName, "int") == 0) {
|
||||
INVOKE_METHOD_WITH_RETURN(int, int);
|
||||
} else if (strcmp(returnTypeName, "QString") == 0) {
|
||||
INVOKE_METHOD_WITH_RETURN(QString, QString);
|
||||
} else if (strcmp(returnTypeName, "QVariant") == 0) {
|
||||
INVOKE_METHOD_WITH_RETURN(QVariant, QVariant);
|
||||
} else if (strcmp(returnTypeName, "QJsonArray") == 0) {
|
||||
INVOKE_METHOD_WITH_RETURN(QJsonArray, QJsonArray);
|
||||
} else if (strcmp(returnTypeName, "QStringList") == 0) {
|
||||
INVOKE_METHOD_WITH_RETURN(QStringList, QStringList);
|
||||
} else {
|
||||
qWarning() << "ModuleProxy: Unsupported return type in invokeMethodByArgCount:" << returnTypeName;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModuleProxy::ModuleProxy(QObject* module, QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_module(module)
|
||||
{
|
||||
// Connect to the wrapped object's eventResponse signal to forward events
|
||||
if (m_module) {
|
||||
QObject::connect(m_module, SIGNAL(eventResponse(QString, QVariantList)),
|
||||
this, SIGNAL(eventResponse(QString, QVariantList)));
|
||||
qDebug() << "ModuleProxy: Connected to wrapped object's eventResponse signal";
|
||||
}
|
||||
}
|
||||
|
||||
ModuleProxy::~ModuleProxy()
|
||||
{
|
||||
qDebug() << "ModuleProxy: Destroyed for module:" << m_module;
|
||||
}
|
||||
|
||||
bool ModuleProxy::saveToken(const QString& from_module_name, const QString& token)
|
||||
{
|
||||
if (from_module_name.isEmpty()) {
|
||||
qWarning() << "ModuleProxy: Cannot save token with empty module name";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (token.isEmpty()) {
|
||||
qWarning() << "ModuleProxy: Cannot save empty token for module:" << from_module_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
qDebug() << "ModuleProxy: Saving token for module:" << from_module_name;
|
||||
m_tokens[from_module_name] = token;
|
||||
|
||||
qDebug() << "ModuleProxy: Token saved successfully. Total tokens stored:" << m_tokens.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant ModuleProxy::callRemoteMethod(const QString& authToken, const QString& methodName, const QVariantList& args)
|
||||
{
|
||||
if (!m_module) {
|
||||
qWarning() << "ModuleProxy: Cannot call method on null module:" << methodName;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (methodName.isEmpty()) {
|
||||
qWarning() << "ModuleProxy: Method name cannot be empty";
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
qDebug() << "ModuleProxy: Auth token received:" << authToken;
|
||||
qDebug() << "ModuleProxy: Calling method" << methodName << "on module" << m_module << "with args:" << args;
|
||||
|
||||
|
||||
PluginInterface* pluginInterface = qobject_cast<PluginInterface*>(m_module);
|
||||
if (!pluginInterface) {
|
||||
qWarning() << "ModuleProxy: Module is not a PluginInterface";
|
||||
return false;
|
||||
}
|
||||
|
||||
// now print the name
|
||||
qDebug() << "ModuleProxy: PluginInterface name:" << pluginInterface->name();
|
||||
|
||||
// get Logos API
|
||||
LogosAPI* logosAPI = pluginInterface->logosAPI;
|
||||
if (!logosAPI) {
|
||||
qWarning() << "ModuleProxy: LogosAPI not available";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get TokenManager
|
||||
TokenManager* tokenManager = logosAPI->getTokenManager();
|
||||
if (!tokenManager) {
|
||||
qWarning() << "ModuleProxy: TokenManager not available";
|
||||
return false;
|
||||
}
|
||||
|
||||
// print keys vand values for debug purposes
|
||||
QList<QString> keys = tokenManager->getTokenKeys();
|
||||
for (const QString& key : keys) {
|
||||
qDebug() << "ModuleProxy: Token key:" << key << "value:" << tokenManager->getToken(key);
|
||||
}
|
||||
|
||||
// check if authToken is valid
|
||||
if (authToken.isEmpty()) {
|
||||
qWarning() << "ModuleProxy: Auth token is empty";
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// check if authToken is stored in tokenManager
|
||||
if (!tokenManager->getToken(authToken).isEmpty()) {
|
||||
qDebug() << "ERROR: ===================== getToken(authToken) is INVALID =====================";
|
||||
qWarning() << "ModuleProxy: Auth token not found in stored tokens";
|
||||
qDebug() << "ERROR: ===================== getToken(authToken) is INVALID =====================";
|
||||
|
||||
return QVariant();
|
||||
} else {
|
||||
qDebug() << "VALID: ===================== getToken(authToken) is VALID =====================";
|
||||
}
|
||||
|
||||
// Each createArgument() call now generates its own unique GUID
|
||||
|
||||
// Find the method to get its return type
|
||||
const QMetaObject* metaObject = m_module->metaObject();
|
||||
int methodIndex = -1;
|
||||
|
||||
qDebug() << "ModuleProxy: Looking for method" << methodName << "with" << args.size() << "arguments";
|
||||
qDebug() << "ModuleProxy: Available methods in" << metaObject->className() << ":";
|
||||
|
||||
// Debug: List all available methods
|
||||
for (int i = 0; i < metaObject->methodCount(); ++i) {
|
||||
QMetaMethod method = metaObject->method(i);
|
||||
qDebug() << " Method" << i << ":" << method.name() << "with" << method.parameterCount() << "parameters, return type:" << method.returnMetaType().name();
|
||||
}
|
||||
|
||||
// Find the method with matching name and argument count
|
||||
for (int i = 0; i < metaObject->methodCount(); ++i) {
|
||||
QMetaMethod method = metaObject->method(i);
|
||||
if (method.name() == methodName && method.parameterCount() == args.size()) {
|
||||
methodIndex = i;
|
||||
qDebug() << "ModuleProxy: Found matching method at index" << i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (methodIndex == -1) {
|
||||
qWarning() << "ModuleProxy: Method not found:" << methodName << "with" << args.size() << "arguments";
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QMetaMethod method = metaObject->method(methodIndex);
|
||||
QMetaType returnType = method.returnMetaType();
|
||||
|
||||
qDebug() << "ModuleProxy: Method signature:" << method.methodSignature();
|
||||
qDebug() << "ModuleProxy: Parameter types:";
|
||||
for (int i = 0; i < method.parameterCount(); ++i) {
|
||||
qDebug() << " Param" << i << ":" << method.parameterMetaType(i).name();
|
||||
}
|
||||
|
||||
// Handle different return types
|
||||
bool success = false;
|
||||
QVariant result;
|
||||
|
||||
if (returnType == QMetaType::fromType<void>()) {
|
||||
// Void method - no return value expected
|
||||
success = invokeMethodByArgCount(m_module, methodName, args, nullptr, nullptr);
|
||||
if (success) {
|
||||
result = QVariant(true); // Return true to indicate success
|
||||
}
|
||||
} else if (returnType == QMetaType::fromType<bool>()) {
|
||||
// Bool return type
|
||||
qDebug() << "ModuleProxy: Invoking bool method" << methodName;
|
||||
bool boolResult = false;
|
||||
success = invokeMethodByArgCount(m_module, methodName, args, &boolResult, "bool");
|
||||
qDebug() << "ModuleProxy: Bool method invocation result:" << success << "value:" << boolResult;
|
||||
if (success) {
|
||||
result = QVariant(boolResult);
|
||||
}
|
||||
} else if (returnType == QMetaType::fromType<int>()) {
|
||||
// Int return type
|
||||
int intResult = 0;
|
||||
success = invokeMethodByArgCount(m_module, methodName, args, &intResult, "int");
|
||||
if (success) {
|
||||
result = QVariant(intResult);
|
||||
}
|
||||
} else if (returnType == QMetaType::fromType<QString>()) {
|
||||
// QString return type
|
||||
QString stringResult;
|
||||
success = invokeMethodByArgCount(m_module, methodName, args, &stringResult, "QString");
|
||||
if (success) {
|
||||
result = QVariant(stringResult);
|
||||
}
|
||||
} else if (returnType == QMetaType::fromType<QVariant>()) {
|
||||
// QVariant return type
|
||||
QVariant variantResult;
|
||||
success = invokeMethodByArgCount(m_module, methodName, args, &variantResult, "QVariant");
|
||||
if (success) {
|
||||
result = variantResult;
|
||||
}
|
||||
} else if (returnType == QMetaType::fromType<QJsonArray>()) {
|
||||
// QJsonArray return type
|
||||
qDebug() << "ModuleProxy: Invoking QJsonArray method" << methodName;
|
||||
QJsonArray jsonArrayResult;
|
||||
success = invokeMethodByArgCount(m_module, methodName, args, &jsonArrayResult, "QJsonArray");
|
||||
qDebug() << "ModuleProxy: QJsonArray method invocation result:" << success << "array size:" << jsonArrayResult.size();
|
||||
if (success) {
|
||||
result = QVariant(jsonArrayResult);
|
||||
}
|
||||
} else if (returnType == QMetaType::fromType<QStringList>()) {
|
||||
// QStringList return type
|
||||
qDebug() << "ModuleProxy: Invoking QStringList method" << methodName;
|
||||
QStringList stringListResult;
|
||||
success = invokeMethodByArgCount(m_module, methodName, args, &stringListResult, "QStringList");
|
||||
qDebug() << "ModuleProxy: QStringList method invocation result:" << success << "list size:" << stringListResult.size();
|
||||
if (success) {
|
||||
result = QVariant(stringListResult);
|
||||
}
|
||||
} else {
|
||||
qWarning() << "ModuleProxy: Unsupported return type:" << returnType.name() << "for method:" << methodName;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
qWarning() << "ModuleProxy: Failed to invoke method" << methodName << "on module" << m_module;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
// Note: Argument cleanup is now handled automatically by each createArgument() call's unique GUID
|
||||
qDebug() << "ModuleProxy: Successfully called method" << methodName << "on module" << m_module;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ModuleProxy::informModuleToken(const QString& authToken, const QString& moduleName, const QString& token)
|
||||
{
|
||||
Q_UNUSED(authToken) // Authentication token validation can be added later
|
||||
|
||||
// cast m_module to PluginInterface
|
||||
PluginInterface* pluginInterface = qobject_cast<PluginInterface*>(m_module);
|
||||
if (!pluginInterface) {
|
||||
qWarning() << "ModuleProxy: Module is not a PluginInterface";
|
||||
return false;
|
||||
}
|
||||
|
||||
// now print the name
|
||||
qDebug() << "ModuleProxy: PluginInterface name:" << pluginInterface->name();
|
||||
|
||||
// get Logos API
|
||||
LogosAPI* logosAPI = pluginInterface->logosAPI;
|
||||
if (!logosAPI) {
|
||||
qWarning() << "ModuleProxy: LogosAPI not available";
|
||||
return false;
|
||||
}
|
||||
|
||||
// get TokenManager
|
||||
TokenManager* tokenManager = logosAPI->getTokenManager();
|
||||
if (!tokenManager) {
|
||||
qWarning() << "ModuleProxy: TokenManager not available";
|
||||
return false;
|
||||
}
|
||||
|
||||
// save token
|
||||
qDebug() << "ModuleProxy: Saving token for module:" << moduleName << "with token:" << token;
|
||||
tokenManager->saveToken(moduleName, token);
|
||||
qDebug() << "ModuleProxy: Token saved successfully";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonArray ModuleProxy::getPluginMethods()
|
||||
{
|
||||
QJsonArray methodsArray;
|
||||
|
||||
const QMetaObject* metaObject = m_module->metaObject();
|
||||
|
||||
for (int i = 0; i < metaObject->methodCount(); ++i) {
|
||||
QMetaMethod method = metaObject->method(i);
|
||||
|
||||
if (method.enclosingMetaObject() != metaObject) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonObject methodObj;
|
||||
methodObj["signature"] = QString::fromUtf8(method.methodSignature());
|
||||
methodObj["name"] = QString::fromUtf8(method.name());
|
||||
methodObj["returnType"] = QString::fromUtf8(method.typeName());
|
||||
methodObj["isInvokable"] = method.isValid() && (method.methodType() == QMetaMethod::Method || method.methodType() == QMetaMethod::Slot);
|
||||
|
||||
if (method.parameterCount() > 0) {
|
||||
QJsonArray params;
|
||||
for (int p = 0; p < method.parameterCount(); ++p) {
|
||||
QJsonObject paramObj;
|
||||
paramObj["type"] = QString::fromUtf8(method.parameterTypeName(p));
|
||||
QByteArrayList paramNames = method.parameterNames();
|
||||
if (p < paramNames.size() && !paramNames.at(p).isEmpty()) {
|
||||
paramObj["name"] = QString::fromUtf8(paramNames.at(p));
|
||||
} else {
|
||||
paramObj["name"] = QString("param%1").arg(p);
|
||||
}
|
||||
params.append(paramObj);
|
||||
}
|
||||
methodObj["parameters"] = params;
|
||||
}
|
||||
|
||||
methodsArray.append(methodObj);
|
||||
}
|
||||
|
||||
return methodsArray;
|
||||
}
|
||||
|
||||
// Include MOC for template instantiation
|
||||
#include "moc_module_proxy.cpp"
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef MODULE_PROXY_H
|
||||
#define MODULE_PROXY_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
#include <QVariantList>
|
||||
#include <QUuid>
|
||||
#include <QHash>
|
||||
#include <QMetaObject>
|
||||
#include <QString>
|
||||
#include <QJsonArray>
|
||||
|
||||
/**
|
||||
* @brief ModuleProxy provides a proxy interface for module interactions
|
||||
*
|
||||
* This class serves as a proxy layer for communicating with modules
|
||||
* in the Logos Core system.
|
||||
*/
|
||||
class ModuleProxy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Construct a new ModuleProxy with authentication token
|
||||
* @param module The module object to proxy
|
||||
* @param authToken Authentication token for the module
|
||||
* @param parent Parent QObject
|
||||
*/
|
||||
explicit ModuleProxy(QObject* module, QObject* parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~ModuleProxy();
|
||||
|
||||
/**
|
||||
* @brief Call a method on the proxied module
|
||||
* @param authToken Authentication token for the method call
|
||||
* @param methodName The name of the method to call
|
||||
* @param args Arguments to pass to the method
|
||||
* @return QVariant containing the result, or invalid QVariant if failed
|
||||
*/
|
||||
Q_INVOKABLE QVariant callRemoteMethod(const QString& authToken, const QString& methodName, const QVariantList& args = QVariantList());
|
||||
|
||||
/**
|
||||
* @brief Inform module of a token
|
||||
* @param authToken Authentication token for the operation
|
||||
* @param moduleName The name of the module
|
||||
* @param token The token to inform the module about
|
||||
* @return bool true if successful, false otherwise
|
||||
*/
|
||||
Q_INVOKABLE bool informModuleToken(const QString& authToken, const QString& moduleName, const QString& token);
|
||||
|
||||
/**
|
||||
* @brief Save a token from a module
|
||||
* @param from_module_name The name of the module providing the token
|
||||
* @param token The token to save
|
||||
* @return bool true if token was saved successfully, false otherwise
|
||||
*/
|
||||
bool saveToken(const QString& from_module_name, const QString& token);
|
||||
|
||||
/**
|
||||
* @brief Get a list of methods for the encapsulated module
|
||||
* @return QJsonArray of method metadata (name, signature, returnType, parameters)
|
||||
*/
|
||||
Q_INVOKABLE QJsonArray getPluginMethods();
|
||||
|
||||
signals:
|
||||
void eventResponse(const QString& eventName, const QVariantList& data);
|
||||
|
||||
private:
|
||||
QObject* m_module;
|
||||
QHash<QString, QString> m_tokens;
|
||||
};
|
||||
|
||||
#endif // MODULE_PROXY_H
|
||||
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @file simple_example.cpp
|
||||
* @brief Simple example showing how to use the LogosAPI class
|
||||
*/
|
||||
|
||||
#include "logos_api.h"
|
||||
#include "logos_api_client.h"
|
||||
#include "logos_api_provider.h"
|
||||
#include "token_manager.h"
|
||||
#include <QDebug>
|
||||
|
||||
void simpleExample()
|
||||
{
|
||||
// Create a LogosAPI instance for our module
|
||||
LogosAPI api("core");
|
||||
|
||||
// Get the provider and register an object
|
||||
LogosAPIProvider* provider = api.getProvider();
|
||||
QObject* myService = new QObject();
|
||||
provider->registerObject("my_service", myService);
|
||||
|
||||
// Get the client to communicate with other modules
|
||||
LogosAPIClient* client = api.getClient("core_manager");
|
||||
// Use client to call remote methods...
|
||||
|
||||
// Get the token manager and save tokens
|
||||
TokenManager* tokenManager = api.getTokenManager();
|
||||
tokenManager->saveToken("auth_token", "abc123");
|
||||
|
||||
qDebug() << "LogosAPI initialized successfully!";
|
||||
|
||||
delete myService;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#include "token_manager.h"
|
||||
#include <QMutexLocker>
|
||||
|
||||
TokenManager& TokenManager::instance()
|
||||
{
|
||||
static TokenManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
TokenManager::TokenManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
TokenManager::~TokenManager()
|
||||
{
|
||||
}
|
||||
|
||||
void TokenManager::saveToken(const QString& key, const QString& token)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_tokens[key] = token;
|
||||
emit tokenSaved(key);
|
||||
}
|
||||
|
||||
QString TokenManager::getToken(const QString& key) const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
return m_tokens.value(key, QString());
|
||||
}
|
||||
|
||||
bool TokenManager::hasToken(const QString& key) const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
return m_tokens.contains(key);
|
||||
}
|
||||
|
||||
bool TokenManager::removeToken(const QString& key)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
if (m_tokens.contains(key)) {
|
||||
m_tokens.remove(key);
|
||||
emit tokenRemoved(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TokenManager::clearAllTokens()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
m_tokens.clear();
|
||||
emit allTokensCleared();
|
||||
}
|
||||
|
||||
QList<QString> TokenManager::getTokenKeys() const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
return m_tokens.keys();
|
||||
}
|
||||
|
||||
int TokenManager::tokenCount() const
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
return m_tokens.size();
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#ifndef TOKEN_MANAGER_H
|
||||
#define TOKEN_MANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
|
||||
/**
|
||||
* @brief TokenManager provides a singleton interface for managing authentication tokens
|
||||
*
|
||||
* This class manages a collection of tokens identified by keys, providing thread-safe
|
||||
* access to store, retrieve, and manage tokens throughout the application lifecycle.
|
||||
*/
|
||||
class TokenManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Get the singleton instance of TokenManager
|
||||
* @return TokenManager& Reference to the singleton instance
|
||||
*/
|
||||
static TokenManager& instance();
|
||||
|
||||
/**
|
||||
* @brief Save a token with the given key
|
||||
* @param key The identifier for the token
|
||||
* @param token The token value to store
|
||||
*/
|
||||
void saveToken(const QString& key, const QString& token);
|
||||
|
||||
/**
|
||||
* @brief Retrieve a token by key
|
||||
* @param key The identifier for the token
|
||||
* @return QString The token value, or empty string if not found
|
||||
*/
|
||||
QString getToken(const QString& key) const;
|
||||
|
||||
/**
|
||||
* @brief Check if a token exists for the given key
|
||||
* @param key The identifier to check
|
||||
* @return bool True if token exists, false otherwise
|
||||
*/
|
||||
bool hasToken(const QString& key) const;
|
||||
|
||||
/**
|
||||
* @brief Remove a token by key
|
||||
* @param key The identifier for the token to remove
|
||||
* @return bool True if token was removed, false if it didn't exist
|
||||
*/
|
||||
bool removeToken(const QString& key);
|
||||
|
||||
/**
|
||||
* @brief Clear all tokens
|
||||
*/
|
||||
void clearAllTokens();
|
||||
|
||||
/**
|
||||
* @brief Get all token keys
|
||||
* @return QList<QString> List of all token keys
|
||||
*/
|
||||
QList<QString> getTokenKeys() const;
|
||||
|
||||
/**
|
||||
* @brief Get the number of stored tokens
|
||||
* @return int Number of tokens stored
|
||||
*/
|
||||
int tokenCount() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief Emitted when a token is saved
|
||||
* @param key The key of the saved token
|
||||
*/
|
||||
void tokenSaved(const QString& key);
|
||||
|
||||
/**
|
||||
* @brief Emitted when a token is removed
|
||||
* @param key The key of the removed token
|
||||
*/
|
||||
void tokenRemoved(const QString& key);
|
||||
|
||||
/**
|
||||
* @brief Emitted when all tokens are cleared
|
||||
*/
|
||||
void allTokensCleared();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Private constructor for singleton pattern
|
||||
* @param parent Parent QObject
|
||||
*/
|
||||
explicit TokenManager(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Private destructor
|
||||
*/
|
||||
~TokenManager();
|
||||
|
||||
// Delete copy constructor and assignment operator to enforce singleton
|
||||
TokenManager(const TokenManager&) = delete;
|
||||
TokenManager& operator=(const TokenManager&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Hash map storing tokens by key
|
||||
*/
|
||||
QHash<QString, QString> m_tokens;
|
||||
|
||||
/**
|
||||
* @brief Mutex for thread-safe access to tokens
|
||||
*/
|
||||
mutable QMutex m_mutex;
|
||||
};
|
||||
|
||||
#endif // TOKEN_MANAGER_H
|
||||
Reference in New Issue
Block a user