$darkmode
simulation_events.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2024 Robert Bosch GmbH
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  * SPDX-License-Identifier: Apache-2.0
17  */
24 #pragma once
25 
26 #include <chrono> // for duration_cast<>
27 #include <memory> // for unique_ptr<>, make_unique<>
28 #include <queue> // for priority_queue<>
29 
30 #include <cloe/core.hpp> // for Json, Duration, Seconds
31 #include <cloe/sync.hpp> // for Sync
32 #include <cloe/trigger.hpp> // for Trigger, Event, EventFactory, ...
33 #include <cloe/trigger/nil_event.hpp> // for DEFINE_NIL_EVENT
34 
35 namespace engine::events {
36 
37 DEFINE_NIL_EVENT(Start, "start", "start of simulation")
38 
39 DEFINE_NIL_EVENT(Stop, "stop", "stop of simulation")
40 
41 DEFINE_NIL_EVENT(Success, "success", "simulation success")
42 
43 DEFINE_NIL_EVENT(Failure, "failure", "simulation failure")
44 
45 DEFINE_NIL_EVENT(Reset, "reset", "reset of simulation")
46 
47 DEFINE_NIL_EVENT(Pause, "pause", "pausation of simulation")
48 
49 DEFINE_NIL_EVENT(Resume, "resume", "resumption of simulation after pause")
50 
51 DEFINE_NIL_EVENT(Loop, "loop", "begin of inner simulation loop each cycle")
52 
53 class NextCallback;
54 
55 class TimeEvent : public cloe::Event {
56  public:
57  TimeEvent(const std::string& name, cloe::Duration t) : Event(name), time_(t) {}
58  cloe::Duration time() const { return time_; }
59  cloe::EventPtr clone() const override { return std::make_unique<TimeEvent>(name(), time_); }
60  void to_json(cloe::Json& j) const override {
61  j = cloe::Json{
62  {"time", cloe::Seconds{time_}.count()},
63  };
64  }
65 
66  private:
67  friend NextCallback;
68 
69  private:
70  cloe::Duration time_;
71 };
72 
74  public:
75  TimeFactory() : cloe::EventFactory("time", "at simulation time") {}
76 
77  cloe::TriggerSchema schema() const override {
78  using namespace cloe::schema; // NOLINT(build/namespaces)
79  static const char* desc = "absolute number of seconds in simulation time";
80  return cloe::TriggerSchema{
81  this->name(),
82  this->description(),
83  cloe::InlineSchema(desc, cloe::Json::value_t::number_float),
85  {"time", Number<double>(nullptr, desc).require()},
86  },
87  };
88  }
89 
90  cloe::EventPtr make(const cloe::Conf& c) const override {
91  auto secs = cloe::Seconds{c.get<double>("time")};
92  return std::make_unique<TimeEvent>(name(), std::chrono::duration_cast<cloe::Duration>(secs));
93  }
94 
95  cloe::EventPtr make(const std::string& s) const override {
96  return make(cloe::Conf{cloe::Json{
97  {"time", std::stod(s)},
98  }});
99  }
100 };
101 
102 struct TimeTrigger {
103  TimeTrigger(cloe::Duration t, cloe::TriggerPtr&& tp) : time(t), trigger(std::move(tp)) {}
104 
105  friend void to_json(cloe::Json& j, const TimeTrigger& t) { j = t.trigger; }
106 
107  public:
108  cloe::Duration time;
109  cloe::TriggerPtr trigger;
110 };
111 
112 using TimeEmplaceHook = std::function<void(const cloe::Trigger&, cloe::Duration)>;
113 
114 class TimeCallback : public cloe::Callback {
115  public:
116  TimeCallback(cloe::Logger log, TimeEmplaceHook h) : log_(log), hook_(h) {}
117 
118  void emplace(cloe::TriggerPtr&& t, const cloe::Sync& sync) override {
119  auto now = sync.time();
120  auto when = dynamic_cast<const TimeEvent&>(t->event()).time();
121  hook_(*t, when);
122  if (when < now) {
123  log_->error("Inserting timed trigger for the past!");
124  log_->error("> trigger time = {} s", std::chrono::duration_cast<cloe::Seconds>(when).count());
125  log_->error("> current time = {} s", std::chrono::duration_cast<cloe::Seconds>(now).count());
126  }
127  if (t->is_sticky()) {
128  log_->error("Inserting timed trigger that is sticky discards stickiness!");
129  }
130  storage_.emplace(std::make_shared<TimeTrigger>(when, std::move(t)));
131  }
132 
133  void to_json(cloe::Json& j) const override {
134  // Make a copy of the storage, and then empty it.
135  decltype(storage_) st_copy{storage_};
136  while (!st_copy.empty()) {
137  j.push_back(st_copy.top());
138  st_copy.pop();
139  }
140  }
141 
142  void trigger(const cloe::Sync& sync) {
143  auto now = sync.time();
144  while (!storage_.empty() && storage_.top()->time <= now) {
145  auto tt = storage_.top();
146  storage_.pop();
147  this->execute(std::move(tt->trigger), sync);
148  }
149  }
150 
151  private:
152  cloe::Logger log_;
153  TimeEmplaceHook hook_;
154 
155  // This is ridiculous: We need to wrap the unique_ptr of Trigger in
156  // a shared_ptr because you can only get objects by copy out of
157  // a priority_queue.
158  std::priority_queue<
159  std::shared_ptr<TimeTrigger>,
160  std::vector<std::shared_ptr<TimeTrigger>>,
161  std::function<bool(const std::shared_ptr<TimeTrigger>&, const std::shared_ptr<TimeTrigger>&)>>
162  storage_{[](const std::shared_ptr<TimeTrigger>& x,
163  const std::shared_ptr<TimeTrigger>& y) -> bool { return x->time > y->time; }};
164 };
165 
167  public:
168  NextFactory() : cloe::EventFactory("next", "next step in simulation") {}
169 
170  cloe::TriggerSchema schema() const override {
171  using namespace cloe::schema; // NOLINT(build/namespaces)
172  static const char* desc = "optional number of seconds from current simulation time";
173  return cloe::TriggerSchema{
174  name(),
175  description(),
176  cloe::InlineSchema(desc, cloe::Json::value_t::number_float, false),
177  cloe::Schema{
178  {"time", Number<double>(nullptr, desc)},
179  },
180  };
181  }
182 
183  std::unique_ptr<cloe::Event> make(const cloe::Conf& c) const override {
184  auto next_time = cloe::Duration{0};
185  if (c.has("time")) {
186  auto secs = cloe::Seconds{c.get<double>("time")};
187  next_time += std::chrono::duration_cast<cloe::Duration>(secs);
188  }
189  return std::make_unique<TimeEvent>(name(), next_time);
190  }
191 
192  std::unique_ptr<cloe::Event> make(const std::string& s) const override {
193  if (!s.empty()) {
194  return make(cloe::Conf{cloe::Json{
195  {"time", std::stod(s)},
196  }});
197  }
198  return make(cloe::Conf{});
199  }
200 };
201 
203  public:
204  using cloe::AliasCallback::AliasCallback;
205  void emplace(cloe::TriggerPtr&& t, const cloe::Sync& s) override {
206  auto& time_event = const_cast<TimeEvent&>(dynamic_cast<const TimeEvent&>(t->event()));
207  time_event.set_name("time");
208  time_event.time_ += s.time();
209  cloe::AliasCallback::emplace(std::move(t), s);
210  }
211 };
212 
213 } // namespace engine::events
Definition: trigger.hpp:582
void emplace(TriggerPtr &&t, const Sync &s) override
Definition: trigger.hpp:590
Definition: trigger.hpp:545
friend void to_json(fable::Json &j, const Entity &e)
Definition: entity.hpp:120
void set_name(std::string name)
Definition: entity.cpp:30
const std::string & description() const
Definition: entity.hpp:90
const std::string & name() const
Definition: entity.hpp:67
Definition: sync.hpp:34
virtual Duration time() const =0
Definition: trigger.hpp:290
Definition: trigger.hpp:400
Definition: simulation_events.hpp:202
void emplace(cloe::TriggerPtr &&t, const cloe::Sync &s) override
Definition: simulation_events.hpp:205
Definition: simulation_events.hpp:166
std::unique_ptr< cloe::Event > make(const cloe::Conf &c) const override
Definition: simulation_events.hpp:183
std::unique_ptr< cloe::Event > make(const std::string &s) const override
Definition: simulation_events.hpp:192
cloe::TriggerSchema schema() const override
Definition: simulation_events.hpp:170
Definition: simulation_events.hpp:114
void emplace(cloe::TriggerPtr &&t, const cloe::Sync &sync) override
Definition: simulation_events.hpp:118
Definition: simulation_events.hpp:55
cloe::EventPtr clone() const override
Definition: simulation_events.hpp:59
Definition: simulation_events.hpp:73
cloe::EventPtr make(const cloe::Conf &c) const override
Definition: simulation_events.hpp:90
cloe::EventPtr make(const std::string &s) const override
Definition: simulation_events.hpp:95
cloe::TriggerSchema schema() const override
Definition: simulation_events.hpp:77
Definition: conf.hpp:81
bool has(const std::string &key) const
Definition: conf.hpp:165
T get() const
Definition: conf.hpp:297
Definition: schema.hpp:173
Definition: number.hpp:36
std::chrono::nanoseconds Duration
Definition: cloe_fwd.hpp:36
#define DEFINE_NIL_EVENT(xName, xname, xdescription)
Definition: nil_event.hpp:58
Definition: trigger.hpp:87
Definition: trigger.hpp:207
Definition: simulation_events.hpp:102