26 using state_type = StateType;
27 using time_type = TimeType;
45 std::string signal_type;
46 std::function<void(
const std::any&, state_type&, time_type)> handler;
47 double priority = 1.0;
48 bool is_active =
true;
55 std::string stream_id;
56 std::function<void(
const state_type&, time_type)> output_func;
57 std::chrono::microseconds interval{1000};
58 std::chrono::steady_clock::time_point last_output;
59 bool is_active =
true;
63 std::shared_ptr<signal_processor_type> signal_processor_;
64 std::vector<SignalInfluence> signal_influences_;
65 std::vector<OutputStream> output_streams_;
68 std::optional<state_type> current_state_;
69 time_type current_time_ = {};
72 std::vector<std::function<void(time_type, state_type&, state_type&)>> trajectory_modifiers_;
76 : signal_processor_(processor ? processor : signal::make_signal_processor<StateType>()) {
77 setup_signal_handling();
83 template<
typename SignalDataType>
85 std::string_view signal_type,
87 std::function<
void(
const SignalDataType&, state_type&, time_type)> handler,
88 double priority = 1.0) {
92 .signal_type = std::string(signal_type),
93 .handler = [h = std::move(handler)](
const std::any& signal_data, state_type& state, time_type t) {
95 const auto& typed_data = std::any_cast<const SignalDataType&>(signal_data);
96 h(typed_data, state, t);
97 }
catch (
const std::bad_any_cast&) {
101 .priority = priority,
105 signal_influences_.push_back(std::move(influence));
108 signal_processor_->template register_handler<SignalDataType>(signal_type,
110 handle_signal(signal_type_str, sig.data);
118 std::string_view stream_id,
119 std::function<
void(
const state_type&, time_type)> output_func,
120 std::chrono::microseconds interval = std::chrono::microseconds{1000}) {
123 .stream_id = std::string(stream_id),
124 .output_func = std::move(output_func),
125 .interval = interval,
126 .last_output = std::chrono::steady_clock::now(),
130 output_streams_.push_back(std::move(stream));
140 std::function<
void(time_type, state_type&, state_type&)> modifier) {
141 trajectory_modifiers_.push_back(std::move(modifier));
150 template<
typename OriginalODE>
152 return [
this, ode = std::forward<OriginalODE>(original_ode)]
153 (time_type t,
const state_type& y, state_type& dydt) {
163 state_type modified_state = y;
164 for (
auto& modifier : trajectory_modifiers_) {
165 modifier(t, modified_state, dydt);
169 process_output_streams(y, t);
177 if (!current_state_)
return;
179 for (
auto& influence : signal_influences_) {
180 if (influence.signal_type == signal_type &&
181 influence.mode == InfluenceMode::DISCRETE_EVENT &&
182 influence.is_active) {
184 influence.handler(signal_data, *current_state_, current_time_);
193 return current_state_;
200 return current_time_;
207 for (
auto& influence : signal_influences_) {
208 if (influence.signal_type == signal_type) {
209 influence.is_active = active;
218 for (
auto& stream : output_streams_) {
219 if (stream.stream_id == stream_id) {
220 stream.is_active = active;
229 return signal_processor_;
233 void setup_signal_handling() {
237 void handle_signal(
const std::string& signal_type,
const std::any& signal_data) {
239 for (
auto& influence : signal_influences_) {
240 if (influence.signal_type == signal_type && influence.is_active) {
242 if (influence.mode == InfluenceMode::DISCRETE_EVENT && current_state_) {
244 influence.handler(signal_data, *current_state_, current_time_);
246 }
else if (influence.mode == InfluenceMode::CONTINUOUS_SHIFT) {
249 (time_type t, state_type& state, state_type& ) {
250 influence.handler(signal_data, state, t);
253 }
else if (influence.mode == InfluenceMode::PARAMETER_UPDATE) {
255 if (current_state_) {
256 influence.handler(signal_data, *current_state_, current_time_);
259 }
else if (influence.mode == InfluenceMode::OUTPUT_TRIGGER && current_state_) {
261 for (
auto& stream : output_streams_) {
262 if (stream.is_active) {
263 stream.output_func(*current_state_, current_time_);
271 void process_output_streams(
const state_type& state, time_type t) {
272 auto now = std::chrono::steady_clock::now();
274 for (
auto& stream : output_streams_) {
275 if (!stream.is_active)
continue;
277 if (now - stream.last_output >= stream.interval) {
278 stream.output_func(state, t);
279 stream.last_output = now;
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)
Register a signal influence on the ODE system.
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})
Register an output stream for real-time data export.