Skip to content

Application

cziter15 edited this page Feb 14, 2026 · 4 revisions

Application

An Application is the central orchestrator in ksIotFrameworkLib that integrates and manages components to achieve specific IoT functionality.


📖 Overview

The Application class (ksApplication) serves as the entry point and coordinator for your IoT device. It manages the lifecycle of all components, handles their initialization, and coordinates their interactions.

Key Responsibilities

  • Component Management — Add, find, and remove components
  • Lifecycle Orchestration — Coordinate init, postInit, and loop phases
  • Dependency Resolution — Enable components to find each other
  • State Management — Track initialization and execution state

🎯 Application Lifecycle

┌─────────────────────────────────────────────────────────┐
│  1. Application Created                                 │
│     App rotator instantiates your application           │
└─────────────────────┬───────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────┐
│  2. init() Called                                      │
│     Override to add components                          │
│     Return false to abort and switch apps              │
└─────────────────────┬───────────────────────────────────┘
                      │ true
                      ▼
┌─────────────────────────────────────────────────────────┐
│  3. Component Initialization                           │
│     Framework calls init() on each component           │
└─────────────────────┬───────────────────────────────────┘
                      │ all succeed
                      ▼
┌─────────────────────────────────────────────────────────┐
│  4. postInit() Called                                 │
│     Override to find component references               │
│     Components enter Active state                      │
└─────────────────────┬───────────────────────────────────┘
                      │
                      ▼
┌─────────────────────────────────────────────────────────┐
│  5. Loop Execution                                     │
│     loop() called repeatedly                           │
│     Each component's loop() executed                    │
└─────────────────────┬───────────────────────────────────┘
                      │
                      ▼ false
┌─────────────────────────────────────────────────────────┐
│  6. Application Terminated                             │
│     Components destroyed                                │
│     Rotator switches to next app                      │
└─────────────────────────────────────────────────────────┘

📝 Creating an Application

Basic Template

#pragma once
#include <ksf/ksApplication.h>

class MyApp : public ksf::ksApplication
{
protected:
    bool init() override {
        // Phase 1: Add components
        addComponent<SomeComponent>();
        return true;  // Return false to abort
    }
    
    bool postInit() override {
        // Phase 2: Find component references
        auto comp = findComponent<SomeComponent>();
        return comp != nullptr;  // Return false to abort
    }
    
    bool loop() override {
        // Phase 3: Main application loop
        // Return false to stop application
        return true;
    }
};

🔧 Core Methods

addComponent<TComponentType>(ctorArgs...)

Creates and adds a component to the application.

Parameters:

  • TComponentType — Component class type (template parameter)
  • ctorArgs... — Constructor arguments for the component

Returns: std::shared_ptr<TComponentType> — Smart pointer to created component

Example:

// Add LED component with pin number
auto led = addComponent<ksf::comps::ksLed>(LED_BUILTIN);

// Add WiFi connector with device name
addComponent<ksf::comps::ksWifiConnector>("MyDevice");

// Add MQTT connector with no args
addComponent<ksf::comps::ksMqttConnector>();

When to call:

  • Always in init() method
  • Can be called later, but it's exceptional

findComponent<TComponentType>()

Finds the first component of a specific type.

Parameters:

  • TComponentType — Component class type to search for

Returns: std::shared_ptr<TComponentType> or nullptr

Example:

bool postInit() override {
    auto mqtt = findComponent<ksf::comps::ksMqttConnector>();
    if (mqtt) {
        // Use mqtt component
        mqtt->subscribe("topic");
    }
    return mqtt != nullptr;
}

When to call:

  • In postInit() method
  • After all components have been added

findComponents<TComponentType>(outVector)

Finds all components of a specific type.

Parameters:

  • TComponentType — Component class type to search for
  • outVector — Vector to store found components

Example:

std::vector<std::shared_ptr<ksLed>> leds;
findComponents<ksf::comps::ksLed>(leds);

for (auto& led : leds) {
    led->on();
}

📋 Method Overrides

init()

Called once when application is created. Use this to:

  • Add all required components
  • Set up initial hardware state
  • Validate prerequisites

Return Value:

  • true — Continue to component initialization
  • false — Abort and trigger app rotation

Example:

bool init() override {
    // Check if configured
    if (!hasConfiguration()) {
        return false;  // Will switch to config app
    }
    
    // Add components
    addComponent<ksf::comps::ksWifiConnector>("Device1");
    addComponent<ksf::comps::ksMqttConnector>();
    addComponent<ksf::comps::ksLed>(LED_BUILTIN);
    
    return true;
}

postInit()

Called after all components have been initialized. Use this to:

  • Get references to other components
  • Set up component interactions
  • Register event handlers

Return Value:

  • true — Continue to application loop
  • false — Abort and trigger app rotation

Example:

bool postInit() override {
    // Get component references
    mqtt = findComponent<ksf::comps::ksMqttConnector>();
    wifi = findComponent<ksf::comps::ksWifiConnector>();
    led = findComponent<ksf::comps::ksLed>();
    
    // Validate all found
    if (!mqtt || !wifi || !led) {
        return false;
    }
    
    // Set up component interaction
    mqtt->onMessage([this](auto topic, auto payload) {
        led->toggle();
    });
    
    return true;
}

loop()

Called repeatedly by the framework. Use this to:

  • Implement application-level logic
  • Coordinate component interactions
  • Check application-level conditions

Return Value:

  • true — Continue running
  • false — Stop application and trigger rotation

Example:

bool loop() override {
    // Check error condition
    if (fatalErrorDetected()) {
        return false;  // Stop this app
    }
    
    // Application logic
    static unsigned long lastCheck = 0;
    if (millis() - lastCheck > 60000) {
        sendHeartbeat();
        lastCheck = millis();
    }
    
    return true;
}

💡 Best Practices

1. Add Components in init()

bool init() override {
    // ✅ Good - All components in init()
    addComponent<ksWifiConnector>();
    addComponent<ksMqttConnector>();
    addComponent<ksLed>();
    return true;
}

2. Find References in postInit()

bool postInit() override {
    // ✅ Good - Get references when ready
    mqtt = findComponent<ksMqttConnector>();
    led = findComponent<ksLed>();
    return mqtt && led;
}

3. Handle Component Dependencies

bool postInit() override {
    // Find WiFi first (needed for MQTT)
    auto wifi = findComponent<ksWifiConnector>();
    if (!wifi) return false;
    
    // Now find MQTT (depends on WiFi)
    mqtt = findComponent<ksMqttConnector>();
    if (!mqtt) return false;
    
    return true;
}

4. Use Smart Pointers for References

class MyApp : public ksApplication
{
private:
    std::shared_ptr<ksf::comps::ksMqttConnector> mqtt;
    std::shared_ptr<ksf::comps::ksLed> led;
    
    bool postInit() override {
        mqtt = findComponent<ksf::comps::ksMqttConnector>();
        led = findComponent<ksf::comps::ksLed>();
        return mqtt && led;
    }
};

5. Implement Graceful Failover

bool init() override {
    // Check configuration exists
    if (!configFileExists()) {
        // Let config app handle setup
        return false;
    }
    
    // Try to load config
    if (!loadConfiguration()) {
        // Config invalid, trigger config app
        return false;
    }
    
    // All good, add components
    addComponent<ksWifiConnector>();
    return true;
}

🎨 Application Patterns

Pattern 1: Main Application

class MainApp : public ksApplication
{
protected:
    bool init() override {
        // Add all runtime components
        addComponent<ksWifiConnector>("MyDevice");
        addComponent<ksMqttConnector>();
        addComponent<DeviceLogic>();
        addComponent<ksLed>(LED_BUILTIN);
        return true;
    }
    
    bool postInit() override {
        auto wifi = findComponent<ksWifiConnector>();
        auto mqtt = findComponent<ksMqttConnector>();
        return wifi && mqtt;
    }
};

Pattern 2: Configuration Application

class ConfigApp : public ksApplication
{
protected:
    bool init() override {
        // Minimal components for setup
        addComponent<ksWifiConfigurator>();
        addComponent<ksDevicePortal>();
        addComponent<ksLed>(LED_BUILTIN);
        return true;
    }
    
    bool loop() override {
        // Auto-restart when configured
        if (configurationComplete()) {
            return false;  // Switch back to main app
        }
        return true;
    }
};

Pattern 3: Stateful Application

class StatefulApp : public ksApplication
{
private:
    enum State { CONNECTING, OPERATIONAL, ERROR };
    State state = CONNECTING;
    
protected:
    bool postInit() override {
        mqtt = findComponent<ksMqttConnector>();
        led = findComponent<ksLed>();
        return mqtt && led;
    }
    
    bool loop() override {
        switch (state) {
            case CONNECTING:
                if (mqtt->isConnected()) {
                    state = OPERATIONAL;
                    led->pattern(0b10101010);  // Fast blink = connected
                }
                break;
            case OPERATIONAL:
                if (!mqtt->isConnected()) {
                    state = ERROR;
                    led->pattern(0b100100100);  // Slow blink = error
                }
                break;
        }
        return true;
    }
};

⚠️ Common Mistakes

Don't: Call addComponent() in loop()

bool loop() override {
    // ❌ Bad - Adds component every loop!
    if (needComponent) {
        addComponent<SomeComponent>();
    }
    return true;
}

Don't: Store raw pointers

private:
    ksMqttConnector* mqtt;  // ❌ Bad - Dangling pointer risk

bool postInit() override {
    mqtt = findComponent<ksMqttConnector>().get();  // Don't
    return true;
}

Don't: Init components manually

bool init() override {
    auto led = addComponent<ksLed>();
    led->init();  // ❌ Bad - Framework handles this
    return true;
}

📖 Related Topics

📘 Getting Started

🏗️ Core Concepts

📦 Components Reference

⚙️ Configuration & Management

🔬 Advanced Topics

💡 Examples

🔗 External Resources

Clone this wiki locally