Skip to main content
EVerest modules are the fundamental building blocks of the charging system. Each module implements specific functionality and communicates with other modules through well-defined interfaces.

Module Structure

Every EVerest module follows a standard directory structure:
MyModule/
├── manifest.yaml           # Module metadata and configuration
├── CMakeLists.txt         # Build configuration
├── MyModule.hpp           # Module header file
├── MyModule.cpp           # Module implementation
└── main/                  # Interface implementations
    ├── InterfaceImpl.hpp
    └── InterfaceImpl.cpp

Example: Store Module

Let’s examine the simple Store module from modules/Misc/Store/:
Store/
├── manifest.yaml
├── CMakeLists.txt
├── Store.hpp
├── Store.cpp
└── main/
    ├── kvsImpl.hpp
    └── kvsImpl.cpp

Creating a Module with ev-cli

The recommended way to create a new module is using the ev-cli tool:
ev-cli module create MyModule
This generates the complete module skeleton including:
  • CMakeLists.txt: Build instructions for CMake
  • ld-ev.hpp/ld-ev.cpp: Framework glue code
  • MyModule.hpp/MyModule.cpp: Main module files
  • Interface implementations: One subdirectory per provided interface
Make sure you have created the manifest.yaml file first, as ev-cli generates code based on the manifest.

Module Implementation Basics

Module Header (MyModule.hpp)

The module header defines the module class structure:
#ifndef MYMODULE_HPP
#define MYMODULE_HPP

#include "ld-ev.hpp"
#include <generated/interfaces/my_interface/Implementation.hpp>

namespace module {

struct Conf {
    // Configuration parameters from manifest
};

class MyModule : public Everest::ModuleBase {
public:
    MyModule() = delete;
    MyModule(const ModuleInfo& info, 
             std::unique_ptr<my_interfaceImplBase> p_main,
             Conf& config) :
        ModuleBase(info), p_main(std::move(p_main)), config(config) {};

    const std::unique_ptr<my_interfaceImplBase> p_main;
    const Conf& config;

protected:
    void init();
    void ready();
};

} // namespace module

#endif // MYMODULE_HPP

Module Implementation (MyModule.cpp)

The implementation file defines the lifecycle methods:
#include "MyModule.hpp"

namespace module {

void MyModule::init() {
    // Called during module initialization
    // Setup resources, initialize member variables
    invoke_init(*p_main);
}

void MyModule::ready() {
    // Called when all modules are initialized
    // Start processing, subscribe to events
    invoke_ready(*p_main);
}

} // namespace module

Interface Implementation

Each interface your module provides requires an implementation:
// main/kvsImpl.hpp
#ifndef MAIN_KVS_IMPL_HPP
#define MAIN_KVS_IMPL_HPP

#include <generated/interfaces/kvs/Implementation.hpp>
#include <map>

namespace module {
namespace main {

struct Conf {};

class kvsImpl : public kvsImplBase {
public:
    kvsImpl() : kvsImplBase(nullptr, "main") {};

    virtual void init() override;
    virtual void ready() override;
    
    // Command handlers
    virtual void handle_store(std::string& key, 
                             std::variant<...>& value) override;
    virtual std::variant<...> handle_load(std::string& key) override;
    
private:
    std::map<std::string, std::variant<...>> kvs;
};

} // namespace main
} // namespace module

#endif
// main/kvsImpl.cpp
#include "kvsImpl.hpp"

namespace module {
namespace main {

void kvsImpl::init() {
    // Initialize interface
}

void kvsImpl::ready() {
    // Interface is ready
}

void kvsImpl::handle_store(std::string& key, 
                          std::variant<...>& value) {
    kvs[key] = value;
}

std::variant<...> kvsImpl::handle_load(std::string& key) {
    return kvs[key];
}

} // namespace main
} // namespace module

CMakeLists.txt Integration

The module’s CMakeLists.txt is auto-generated but can be customized:
# Module setup
ev_setup_cpp_module()

# Add source files
target_sources(${MODULE_NAME}
    PRIVATE
        "main/kvsImpl.cpp"
)

# Link additional libraries if needed
# target_link_libraries(${MODULE_NAME} PRIVATE my_library)

# Add custom targets
# install(...)
Keep code within the marked regions (between ev@... comments) when updating modules with ev-cli, as these sections are preserved during updates.

Logging in Modules

Use the EVLOG macros for logging:
#include <everest/logging.hpp>

EVLOG_info << "Module initialized";
EVLOG_debug << "Processing value: " << value;
EVLOG_warning << "Unusual condition detected";
EVLOG_error << "Failed to process: " << error;
Log levels:
  • EVLOG_debug: Detailed debugging information
  • EVLOG_info: General informational messages
  • EVLOG_warning: Warning messages for unusual conditions
  • EVLOG_error: Error messages for failures

Updating Modules

When you modify the manifest or interface definitions:
ev-cli module update MyModule
This updates:
  • Header files (preserving marked sections)
  • CMakeLists.txt (preserving marked sections)
  • Interface implementation headers
Use --diff to preview changes without modifying files:
ev-cli module update MyModule --diff

Next Steps

Module Manifest

Learn about the manifest.yaml format and configuration options

Testing

Write tests for your custom modules

Debugging

Debug your modules effectively

Interfaces

Explore available EVerest interfaces