feat: move logos-cpp-sdk to its own repo

This commit is contained in:
Iuri Matias
2025-09-30 14:56:47 -04:00
parent c05739331a
commit 65aa4a1b24
21 changed files with 3221 additions and 0 deletions
+42
View File
@@ -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
View File
@@ -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!"
+105
View File
@@ -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();
}
+49
View File
@@ -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;
}
+60
View File
@@ -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
+179
View File
@@ -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 "";
}
+211
View File
@@ -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
+321
View File
@@ -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();
}
+140
View File
@@ -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
+117
View File
@@ -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));
}
+79
View File
@@ -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
+429
View File
@@ -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"
+78
View File
@@ -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
+33
View File
@@ -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;
}
+66
View File
@@ -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();
}
+116
View File
@@ -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