DAO Component System
Overview
The DAO Component system provides a framework for building modular, state-driven applications. It implements a consistent component lifecycle model with well-defined states and transitions. Components communicate via ZeroMQ messaging and can share memory for high-performance data exchange.
Component Architecture
The Component architecture consists of several layers:
Component Class: The user-facing API that implements the ComponentIfce interface
ComponentBase: Core implementation providing state machine and thread management
ComponentIfce: Interface definition for component lifecycle methods
ComponentStateMachine: State machine implementation for component lifecycle
ComponentUpdateThread: Thread for handling shared memory updates
ComponentZmqThread: Thread for handling ZeroMQ messaging
Class Hierarchy
ComponentIfce (Interface)
|
+--> StateMachine
|
+--> ComponentBase
|
+--> Component
Component States
Components follow a state machine with the following states:
Off: Initial state, component is not initialized
Standby: Component is initialized but not enabled
Idle: Component is enabled but not running
Running: Component is actively processing
Error: Component has encountered an error
State transitions are triggered by events:
Init: Transition from Off to Standby
Stop: Transition from Standby to Off
Enable: Transition from Standby to Idle
Disable: Transition from Idle to Standby
Run: Transition from Idle to Running
Idle: Transition from Running to Idle
OnFailure: Transition to Error state
Recover: Transition from Error to Idle
Component Class
The Component class is the main entry point for users to create component instances. It inherits from ComponentBase and implements the ComponentIfce interface.
Construction
Component(std::string name, Dao::Log::Logger& logger, std::string ip, int port, int core=-1)
Parameters:
name: Unique identifier for the component
logger: Logger instance for component logging
ip: IP address for ZMQ communication
port: Port number for ZMQ communication
core: CPU core affinity (-1 means no specific core)
Lifecycle Methods
Components expose the following lifecycle methods:
Init(): Initialize the component
Stop(): Stop and clean up the component
Enable(): Enable the component’s functionality
Disable(): Disable the component’s functionality
Run(): Start active processing
Idle(): Pause active processing
OnFailure(): Handle error conditions
Recover(): Recover from error state
GetStateText(): Get the current state as a string
ComponentBase Class
The ComponentBase class provides the core implementation for components, handling thread management and state transitions.
Key Methods
PostConstructor(): Finalizes component setup after construction
PostEnable(): Additional actions during Enable
PostDisable(): Additional actions during Disable
Thread Management
ComponentBase manages two threads:
ZmqThread: Handles command-response messaging
UpdateThread: Handles shared memory updates
ComponentIfce Interface
This interface defines the required methods for component lifecycle management:
class ComponentIfce
{
public:
virtual ~ComponentIfce(){};
virtual void Init() = 0;
virtual void Stop() = 0;
virtual void Enable() = 0;
virtual void Disable() = 0;
virtual void Run() = 0;
virtual void Idle() = 0;
virtual void OnFailure()= 0;
virtual void Recover() = 0;
virtual std::string GetStateText() = 0;
};
State Machine
The Component State Machine is based on the StateMachine class and handles transitions between component states.
Key Features:
Event-driven state transitions
Entry and exit hooks for each state
Protected virtual methods for transition customization
Thread-safe state changes
ZeroMQ Communication
The ComponentZmqThread handles command and control messaging using ZeroMQ’s request-response pattern.
Supported Commands:
EXEC: Execute component lifecycle methods
SETUP: Configure component parameters
UPDATE: Update component data
PING: Check component health
STATE: Get current state information
SET_LOG_LEVEL: Change logging level
Example Command Processing:
void process_EXEC(std::string Payload)
{
if(Payload == "Init")
m_ifce->Init();
else if (Payload == "Stop")
m_ifce->Stop();
else if (Payload == "Enable")
m_ifce->Enable();
// ...
}
Creating a Custom Component
To create a custom component, inherit from the Component class:
#include <daoComponent.hpp>
class MyComponent : public Dao::Component
{
public:
MyComponent(std::string name, Dao::Log::Logger& logger,
std::string ip, int port, int core=-1)
: Component(name, logger, ip, port, core)
{
// Custom initialization
}
protected:
// Override state machine hooks
void entry_Idle() override
{
// Custom idle state entry
Component::entry_Idle();
}
void transition_Idle_Running() override
{
// Custom transition logic
Component::transition_Idle_Running();
}
private:
// Component-specific members
};
Usage Example
#include <daoComponent.hpp>
#include <daoLog.hpp>
int main()
{
// Create logger
Dao::Log::Logger logger("MyApp");
logger.SetLevel(Dao::Log::LEVEL::INFO);
// Create component
Dao::Component myComponent("TestComponent", logger, "127.0.0.1", 5555);
// Initialize component
myComponent.Init();
// Enable component
myComponent.Enable();
// Start processing
myComponent.Run();
// Later...
// Stop processing
myComponent.Idle();
// Disable component
myComponent.Disable();
// Shut down
myComponent.Stop();
return 0;
}
Best Practices
Lifecycle Management: Always follow the proper sequence of state transitions
Error Handling: Implement robust error handling in the OnFailure() method
Resource Cleanup: Clean up resources in the Stop() method
Thread Safety: Use thread-safe data structures for sharing data between threads
Core Affinity: Set appropriate core affinity for real-time applications
Advanced Features
Custom State Transitions
Override the transition methods to implement custom behavior:
void transition_Idle_Running() override
{
// Pre-transition tasks
prepareForRunning();
// Call base implementation
Component::transition_Idle_Running();
// Post-transition tasks
startDataProcessing();
}
ZeroMQ Command Handling
To handle custom commands, extend the ZmqThread functionality:
// Create custom ZmqThread subclass
class MyZmqThread : public ComponentZmqThread
{
// Override process_message to handle custom commands
};
// Then use your custom thread in the component
Python Developers
Dao offers a Python component framework with the same state-driven architecture and lifecycle management as the C++ implementation. Python components can be controlled remotely via ZeroMQ using the standard interface commands, enabling high-performance, networked applications with seamless integration between Python and C++ components.
Below is an example of how to get started:
from daoComponent import daoComponent
from daoLog import daoLog
class ExampleProcessingComponent(daoComponent):
def __init__(self):
super().__init__(name="ExampleComponent", port=8100)
# Custom class initialization ...
def load_static_config(self):
# Load, parse and apply configuration data ...
def load_dynamic_config(self):
# Allocate memory and other resources to prepare for running ...
def processingThread(self):
while not self.procThreadStop.is_set():
# Process real-time data ...
if __name__ == "__main__":
daoLog(componentName)
// Create daoComponent object
myComponent = ExampleProcessingComponent()
// Initialize component
myComponent.Init();
// Enable component
myComponent.Enable();
// Start processing
myComponent.Run();
// Later...
// Stop processing
myComponent.Idle();
// Disable component
myComponent.Disable();
// Shut down
myComponent.Stop();