Program Listing for File integration_interface.hpp

Return to documentation for file (include/interfaces/integration_interface.hpp)

#pragma once

#include <core/concepts.hpp>
#include <signal/signal_processor.hpp>
#include <functional>
#include <memory>
#include <vector>
#include <chrono>
#include <optional>
#include <any>

namespace diffeq::interfaces {

template<system_state StateType, can_be_time TimeType>
class IntegrationInterface {
public:
    using state_type = StateType;
    using time_type = TimeType;
    using signal_processor_type = signal::SignalProcessor<StateType>;

    enum class InfluenceMode {
        DISCRETE_EVENT,     // Signal causes instantaneous state jump
        CONTINUOUS_SHIFT,   // Signal modifies ODE trajectory continuously
        PARAMETER_UPDATE,   // Signal updates integration parameters
        OUTPUT_TRIGGER      // Signal triggers output/logging
    };

    struct SignalInfluence {
        InfluenceMode mode;
        std::string signal_type;
        std::function<void(const std::any&, state_type&, time_type)> handler;
        double priority = 1.0;
        bool is_active = true;
    };

    struct OutputStream {
        std::string stream_id;
        std::function<void(const state_type&, time_type)> output_func;
        std::chrono::microseconds interval{1000}; // Output frequency
        std::chrono::steady_clock::time_point last_output;
        bool is_active = true;
    };

private:
    std::shared_ptr<signal_processor_type> signal_processor_;
    std::vector<SignalInfluence> signal_influences_;
    std::vector<OutputStream> output_streams_;

    // Current integration state
    std::optional<state_type> current_state_;
    time_type current_time_ = {};

    // Trajectory modification functions
    std::vector<std::function<void(time_type, state_type&, state_type&)>> trajectory_modifiers_;

public:
    explicit IntegrationInterface(std::shared_ptr<signal_processor_type> processor = nullptr)
        : signal_processor_(processor ? processor : signal::make_signal_processor<StateType>()) {
        setup_signal_handling();
    }

    template<typename SignalDataType>
    void register_signal_influence(
        std::string_view signal_type,
        InfluenceMode mode,
        std::function<void(const SignalDataType&, state_type&, time_type)> handler,
        double priority = 1.0) {

        SignalInfluence influence{
            .mode = mode,
            .signal_type = std::string(signal_type),
            .handler = [h = std::move(handler)](const std::any& signal_data, state_type& state, time_type t) {
                try {
                    const auto& typed_data = std::any_cast<const SignalDataType&>(signal_data);
                    h(typed_data, state, t);
                } catch (const std::bad_any_cast&) {
                    // Type mismatch - ignore silently
                }
            },
            .priority = priority,
            .is_active = true
        };

        signal_influences_.push_back(std::move(influence));

        // Register with signal processor
        signal_processor_->template register_handler<SignalDataType>(signal_type,
            [this, signal_type_str = std::string(signal_type)](const signal::Signal<SignalDataType>& sig) {
                handle_signal(signal_type_str, sig.data);
            });
    }

    void register_output_stream(
        std::string_view stream_id,
        std::function<void(const state_type&, time_type)> output_func,
        std::chrono::microseconds interval = std::chrono::microseconds{1000}) {

        OutputStream stream{
            .stream_id = std::string(stream_id),
            .output_func = std::move(output_func),
            .interval = interval,
            .last_output = std::chrono::steady_clock::now(),
            .is_active = true
        };

        output_streams_.push_back(std::move(stream));
    }

    void add_trajectory_modifier(
        std::function<void(time_type, state_type&, state_type&)> modifier) {
        trajectory_modifiers_.push_back(std::move(modifier));
    }

    template<typename OriginalODE>
    auto make_signal_aware_ode(OriginalODE&& original_ode) {
        return [this, ode = std::forward<OriginalODE>(original_ode)]
               (time_type t, const state_type& y, state_type& dydt) {

            // Update current state for signal handling
            current_state_ = y;
            current_time_ = t;

            // Compute original dynamics
            ode(t, y, dydt);

            // Apply trajectory modifiers from continuous signal influences
            state_type modified_state = y;
            for (auto& modifier : trajectory_modifiers_) {
                modifier(t, modified_state, dydt);
            }

            // Handle real-time outputs
            process_output_streams(y, t);
        };
    }

    void apply_discrete_event(const std::string& signal_type, const std::any& signal_data) {
        if (!current_state_) return;

        for (auto& influence : signal_influences_) {
            if (influence.signal_type == signal_type &&
                influence.mode == InfluenceMode::DISCRETE_EVENT &&
                influence.is_active) {

                influence.handler(signal_data, *current_state_, current_time_);
            }
        }
    }

    std::optional<state_type> get_current_state() const {
        return current_state_;
    }

    time_type get_current_time() const {
        return current_time_;
    }

    void set_signal_influence_active(const std::string& signal_type, bool active) {
        for (auto& influence : signal_influences_) {
            if (influence.signal_type == signal_type) {
                influence.is_active = active;
            }
        }
    }

    void set_output_stream_active(const std::string& stream_id, bool active) {
        for (auto& stream : output_streams_) {
            if (stream.stream_id == stream_id) {
                stream.is_active = active;
            }
        }
    }

    std::shared_ptr<signal_processor_type> get_signal_processor() {
        return signal_processor_;
    }

private:
    void setup_signal_handling() {
        // Basic signal handling is set up when influences are registered
    }

    void handle_signal(const std::string& signal_type, const std::any& signal_data) {
        // Handle different influence modes
        for (auto& influence : signal_influences_) {
            if (influence.signal_type == signal_type && influence.is_active) {

                if (influence.mode == InfluenceMode::DISCRETE_EVENT && current_state_) {
                    // Apply immediate state modification
                    influence.handler(signal_data, *current_state_, current_time_);

                } else if (influence.mode == InfluenceMode::CONTINUOUS_SHIFT) {
                    // Add to trajectory modifiers for continuous influence
                    add_trajectory_modifier([influence, signal_data]
                        (time_type t, state_type& state, state_type& /* dydt */) {
                        influence.handler(signal_data, state, t);
                    });

                } else if (influence.mode == InfluenceMode::PARAMETER_UPDATE) {
                    // Handle parameter updates (integrator-specific)
                    if (current_state_) {
                        influence.handler(signal_data, *current_state_, current_time_);
                    }

                } else if (influence.mode == InfluenceMode::OUTPUT_TRIGGER && current_state_) {
                    // Trigger immediate output
                    for (auto& stream : output_streams_) {
                        if (stream.is_active) {
                            stream.output_func(*current_state_, current_time_);
                        }
                    }
                }
            }
        }
    }

    void process_output_streams(const state_type& state, time_type t) {
        auto now = std::chrono::steady_clock::now();

        for (auto& stream : output_streams_) {
            if (!stream.is_active) continue;

            if (now - stream.last_output >= stream.interval) {
                stream.output_func(state, t);
                stream.last_output = now;
            }
        }
    }
};

template<system_state StateType, can_be_time TimeType = double>
auto make_integration_interface(
    std::shared_ptr<signal::SignalProcessor<StateType>> processor) {
    return std::make_unique<IntegrationInterface<StateType, TimeType>>(processor);
}

template<system_state StateType, can_be_time TimeType = double>
auto make_integration_interface() {
    return std::make_unique<IntegrationInterface<StateType, TimeType>>();
}

} // namespace diffeq::interfaces