How to create a Logos Module - logos-co/logos-core-poc GitHub Wiki

How to Create a New Module for Logos Core

Note: WIP and subject to change


1. Create the Module Directory

Navigate to the modules/ directory and create a new folder for your module:

cd modules
mkdir my_new_module
cd my_new_module

Note: The module can be in a separate repo


2. Create the Interface Header

Create a file named my_new_module_interface.h. This defines the API your module exposes.

#pragma once

#include <QtCore/QObject>
#include "../../core/interface.h"

class MyNewModuleInterface : public PluginInterface
{
public:
    virtual ~MyNewModuleInterface() {}
    Q_INVOKABLE virtual bool foo(const QString &bar) = 0;

signals:
    // Required for event communication
    void eventResponse(const QString& eventName, const QVariantList& data);
};

#define MyNewModuleInterface_iid "org.logos.MyNewModuleInterface"
Q_DECLARE_INTERFACE(MyNewModuleInterface, MyNewModuleInterface_iid)
  • Change foo and bar to your desired method and parameter names.
  • Update the IID string to match your module.
  • Include the eventResponse signal for event-based communication.

Note: An #include "interface.h" is needed for the module to have an interface the core understands. One can just include this directly in the project. Here it's relative since it's in the same repo and we want to take advantage of any changes to the core without having to copy interface files.


3. Create the Plugin Header

Create my_new_module_plugin.h:

#pragma once

#include <QtCore/QObject>
#include "my_new_module_interface.h"
#include "../../SDK/cpp/logos_api.h"

class MyNewModulePlugin : public QObject, public MyNewModuleInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID MyNewModuleInterface_iid FILE "metadata.json")
    Q_INTERFACES(MyNewModuleInterface PluginInterface)

public:
    MyNewModulePlugin();
    ~MyNewModulePlugin();

    Q_INVOKABLE bool foo(const QString &bar) override;
    QString name() const override { return "my_new_module"; }
    QString version() const override { return "1.0.0"; }

signals:
    // Required for event communication
    void eventResponse(const QString& eventName, const QVariantList& data);

private:
    LogosAPI* logosAPI;
};

Note: The plugin now includes the LogosAPI header and has a private LogosAPI pointer for interacting with the Logos Core system.


4. Create the Plugin Implementation

Create my_new_module_plugin.cpp:

#include "my_new_module_plugin.h"
#include <QDebug>
#include <QCoreApplication>
#include <QVariantList>
#include <QDateTime>

MyNewModulePlugin::MyNewModulePlugin() : logosAPI(nullptr)
{
    qDebug() << "MyNewModulePlugin: Initializing...";
    
    // Initialize the Logos API
    logosAPI = new LogosAPI("core_registry", this);
    
    qDebug() << "MyNewModulePlugin: Initialized successfully";
}

MyNewModulePlugin::~MyNewModulePlugin() 
{
    // Clean up resources
    if (logosAPI) {
        delete logosAPI;
        logosAPI = nullptr;
    }
}

bool MyNewModulePlugin::foo(const QString &bar)
{
    qDebug() << "MyNewModulePlugin::foo called with:" << bar;
    
    // Create event data with the bar parameter
    QVariantList eventData;
    eventData << bar; // Add the bar parameter to the event data
    eventData << QDateTime::currentDateTime().toString(Qt::ISODate); // Add timestamp
    
    // Trigger the event using LogosAPI
    if (logosAPI) {
        qDebug() << "MyNewModulePlugin: Triggering event 'fooTriggered' with data:" << eventData;
        logosAPI->onEventResponse(this, "fooTriggered", eventData);
        qDebug() << "MyNewModulePlugin: Event 'fooTriggered' triggered with data:" << eventData;
    } else {
        qWarning() << "MyNewModulePlugin: LogosAPI not available, cannot trigger event";
    }
    
    return true;
}

Note: The implementation now uses LogosAPI to interact with the Logos Core system and emit events.


5. Create the Metadata File

Create metadata.json:

{
  "name": "my_new_module",
  "version": "1.0.0",
  "description": "Describe your module here",
  "author": "Your Name or Team",
  "type": "core",
  "category": "custom",
  "main": "my_new_module_plugin",
  "dependencies": [],
  "capabilities": [
    "custom_capability"
  ]
}

6. Create the CMake Build File

Create CMakeLists.txt:

set(CMAKE_AUTOMOC ON)

# Find Qt RemoteObjects (needed for LogosAPI)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core RemoteObjects)

# Plugin sources
set(PLUGIN_SOURCES
    my_new_module_plugin.cpp
    my_new_module_plugin.h
    my_new_module_interface.h
    ${CMAKE_SOURCE_DIR}/../core/interface.h
    ${CMAKE_CURRENT_SOURCE_DIR}/../../SDK/cpp/logos_api.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../../SDK/cpp/logos_api.h
)

# Create the plugin library
add_library(my_new_module_plugin SHARED ${PLUGIN_SOURCES})

# Set output name without lib prefix
set_target_properties(my_new_module_plugin PROPERTIES
    PREFIX "")

# Link Qt libraries
target_link_libraries(my_new_module_plugin PRIVATE 
    Qt${QT_VERSION_MAJOR}::Core 
    Qt${QT_VERSION_MAJOR}::RemoteObjects
)

# Include directories
target_include_directories(my_new_module_plugin PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}
    ${CMAKE_SOURCE_DIR}/../core
    ${CMAKE_CURRENT_SOURCE_DIR}/../../SDK/cpp
)

# Set common properties for both platforms
set_target_properties(my_new_module_plugin PROPERTIES
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/modules"
    BUILD_WITH_INSTALL_RPATH TRUE
    SKIP_BUILD_RPATH FALSE)

if(APPLE)
    set_target_properties(my_new_module_plugin PROPERTIES
        INSTALL_RPATH "@loader_path"
        INSTALL_NAME_DIR "@rpath"
        BUILD_WITH_INSTALL_NAME_DIR TRUE)
    add_custom_command(TARGET my_new_module_plugin POST_BUILD
        COMMAND install_name_tool -id "@rpath/my_new_module_plugin.dylib" $<TARGET_FILE:my_new_module_plugin>
        COMMENT "Updating library paths for macOS"
    )
else()
    set_target_properties(my_new_module_plugin PROPERTIES
        INSTALL_RPATH "$ORIGIN"
        INSTALL_RPATH_USE_LINK_PATH FALSE)
endif()

Note: The CMake file now includes Qt RemoteObjects as a dependency and includes the LogosAPI source files.


7. Register the Module in the Build System

Edit modules/CMakeLists.txt and add:

add_subdirectory(my_new_module)

8. Build the Project

From the root of your project:

./scripts/clean.sh && ./scripts/run_app.sh all

9. Test Your Module

  • Start the Logos Core app.
  • Your module should be detected and loaded.
  • Check the logs for "MyNewModulePlugin: Initializing..." and "MyNewModulePlugin: Initialized successfully" messages.
  • When your module's API is called, you should see "MyNewModulePlugin::foo called with:" and event triggering messages.

10. Using the LogosAPI

The LogosAPI provides several useful methods for interacting with the Logos Core system:

  • requestObject(objectName): Get a remote object by name
  • callRemoteMethod(objectName, methodName, args): Call a method on a remote object
  • onEvent(originObject, destinationObject, eventName, callback): Register an event listener
  • onEventResponse(replica, eventName, data): Trigger an event

Example of calling a method on another module:

// Get a result from another module
QVariant result = logosAPI->callRemoteMethod("other_module", "someMethod", QVariantList() << "param1" << "param2");

// Listen for events from another module
logosAPI->onEvent(otherModuleObject, this, "someEvent", [this](const QString& eventName, const QVariantList& data) {
    qDebug() << "Received event:" << eventName << "with data:" << data;
    // Handle the event
});

11. Customize

  • Add more methods to your interface and implementation as needed.
  • Update metadata and capabilities to reflect your module's features.
  • Use LogosAPI to interact with other modules in the Logos Core system.

Reference

See modules/template_module for a working example.

⚠️ **GitHub.com Fallback** ⚠️