$darkmode
simulation_context.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 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  */
23 #pragma once
24 
25 #include <cstdint> // for uint64_t
26 #include <functional> // for function<>
27 #include <map> // for map<>
28 #include <memory> // for unique_ptr<>, shared_ptr<>
29 #include <string> // for string
30 #include <vector> // for vector<>
31 
32 #include <boost/optional.hpp> // for optional<>
33 
34 #include <cloe/controller.hpp> // for Controller
35 #include <cloe/core.hpp> // for Duration
36 #include <cloe/registrar.hpp> // for Registrar
37 #include <cloe/simulator.hpp> // for Simulator
38 #include <cloe/sync.hpp> // for Sync
39 #include <cloe/trigger/nil_event.hpp> // for DEFINE_NIL_EVENT
40 #include <cloe/utility/statistics.hpp> // for Accumulator
41 #include <cloe/utility/timer.hpp> // for DurationTimer
42 #include <cloe/vehicle.hpp> // for Vehicle
43 
44 #include "coordinator.hpp" // for Coordinator
45 #include "registrar.hpp" // for Registrar
46 #include "server.hpp" // for Server
47 #include "stack.hpp" // for Stack
48 #include "utility/command.hpp" // for CommandExecuter
49 #include "utility/progress.hpp" // for Progress
50 #include "utility/time_event.hpp" // for TimeCallback
51 
52 namespace engine {
53 
57 class SimulationSync : public cloe::Sync {
58  public: // Overrides
59  SimulationSync() = default;
60  explicit SimulationSync(const cloe::Duration& step_width) : step_width_(step_width) {}
61 
62  uint64_t step() const override { return step_; }
63  cloe::Duration step_width() const override { return step_width_; }
64  cloe::Duration time() const override { return time_; }
65  cloe::Duration eta() const override { return eta_; }
66 
73  double realtime_factor() const override { return realtime_factor_; }
74 
79  double achievable_realtime_factor() const override {
80  return static_cast<double>(step_width().count()) / static_cast<double>(cycle_time_.count());
81  }
82 
83  public: // Modification
91  void increment_step() {
92  step_ += 1;
93  time_ += step_width_;
94  }
95 
100  void set_realtime_factor(double s) { realtime_factor_ = s; }
101 
102  void set_eta(cloe::Duration d) { eta_ = d; }
103 
104  void reset() {
105  time_ = cloe::Duration(0);
106  step_ = 0;
107  }
108 
109  void set_cycle_time(cloe::Duration d) { cycle_time_ = d; }
110 
111  private:
112  // Simulation State
113  uint64_t step_{0};
114  cloe::Duration time_{0};
115  cloe::Duration eta_{0};
116  cloe::Duration cycle_time_;
117 
118  // Simulation Configuration
119  double realtime_factor_{1.0}; // realtime
120  cloe::Duration step_width_{20'000'000}; // should be 20ms
121 };
122 
128  using TimePoint = std::chrono::steady_clock::time_point;
129 
130  public:
131  std::string stage{""};
132  std::string message{"initializing engine"};
133 
134  Progress initialization;
135  size_t initialization_n;
136  size_t initialization_k;
137 
138  Progress execution;
139  cloe::Duration execution_eta{0};
140 
141  // Reporting:
142  double report_granularity_p{0.1};
143  cloe::Duration report_granularity_d{10'000'000'000};
144  double execution_report_p;
145  TimePoint execution_report_t;
146 
147  public:
148  void init_begin(size_t n) {
149  message = "initializing";
150  initialization.begin();
151  initialization_n = n;
152  initialization_k = 0;
153  }
154 
155  void init(const std::string& what) {
156  stage = what;
157  message = "initializing " + what;
158  initialization_k++;
159  double p = static_cast<double>(initialization_k) / static_cast<double>(initialization_n);
160  initialization.update(p);
161  }
162 
163  void init_end() {
164  initialization_k++;
165  assert(initialization_k == initialization_n);
166  initialization.end();
167  stage = "";
168  message = "initialization done";
169  }
170 
171  bool is_init_ended() const { return initialization.is_ended(); }
172 
173  cloe::Duration elapsed() const {
174  if (is_init_ended()) {
175  return initialization.elapsed() + execution.elapsed();
176  } else {
177  return initialization.elapsed();
178  }
179  }
180 
181  void exec_begin() {
182  stage = "simulation";
183  message = "executing simulation";
184  execution_report_p = 0;
185  execution_report_t = std::chrono::steady_clock::now();
186  execution.begin();
187  }
188 
189  void exec_update(double p) { execution.update_safe(p); }
190 
191  void exec_update(cloe::Duration now) {
192  if (execution_eta != cloe::Duration(0)) {
193  double now_d = static_cast<double>(now.count());
194  double eta_d = static_cast<double>(execution_eta.count());
195  exec_update(now_d / eta_d);
196  }
197  }
198 
199  void exec_end() {
200  stage = "";
201  message = "simulation done";
202  execution.end();
203  }
204 
205  bool is_exec_ended() const { return execution.is_ended(); }
206 
212  bool exec_report() {
213  // We should not report 100% more than once.
214  if (execution_report_p == 1.0) {
215  return false;
216  }
217 
218  // If there is no execution ETA, also don't report.
219  if (execution_eta == cloe::Duration(0)) {
220  return false;
221  }
222 
223  // Certain minimum percentage has passed.
224  auto now = std::chrono::steady_clock::now();
225  if (execution.is_ended()) {
226  // We should report 100% at least once.
227  execution_report_p = 1.0;
228  execution_report_t = now;
229  return true;
230  } else if (execution.percent() - execution_report_p > report_granularity_p) {
231  // We should report at least every report_granularity_p (percent).
232  execution_report_p = execution.percent();
233  execution_report_t = now;
234  return true;
235  } else if (cast_duration(now - execution_report_t) > report_granularity_d) {
236  // We should report at least every report_granularity_d (duration).
237  execution_report_p = execution.percent();
238  execution_report_t = now;
239  return true;
240  } else {
241  return false;
242  }
243  }
244 
245  friend void to_json(cloe::Json& j, const SimulationProgress& p) {
246  j = cloe::Json{
247  {"message", p.message},
248  {"initialization", p.initialization},
249  };
250  if (p.execution_eta > cloe::Duration(0)) {
251  j["execution"] = p.execution;
252  } else {
253  j["execution"] = nullptr;
254  }
255  }
256 };
257 
259  cloe::utility::Accumulator engine_time_ms;
260  cloe::utility::Accumulator cycle_time_ms;
261  cloe::utility::Accumulator simulator_time_ms;
262  cloe::utility::Accumulator controller_time_ms;
263  cloe::utility::Accumulator padding_time_ms;
264  cloe::utility::Accumulator controller_retries;
265 
266  void reset() {
267  engine_time_ms.reset();
268  cycle_time_ms.reset();
269  simulator_time_ms.reset();
270  controller_time_ms.reset();
271  padding_time_ms.reset();
272  controller_retries.reset();
273  }
274 
275  friend void to_json(cloe::Json& j, const SimulationStatistics& s) {
276  j = cloe::Json{
277  {"engine_time_ms", s.engine_time_ms}, {"simulator_time_ms", s.simulator_time_ms},
278  {"controller_time_ms", s.controller_time_ms}, {"padding_time_ms", s.padding_time_ms},
279  {"cycle_time_ms", s.cycle_time_ms}, {"controller_retries", s.controller_retries},
280  };
281  }
282 };
283 
287 enum class SimulationOutcome {
288  NoStart,
289  Aborted,
290  Stopped,
291  Failure,
292  Success,
293 };
294 
295 // If possible, the following exit codes should not be used as they are used
296 // by the Bash shell, among others: 1-2, 126-165, and 255. That leaves us
297 // primarily with the range 3-125, which should suffice for our purposes.
298 // The following exit codes should not be considered stable.
299 #define EXIT_OUTCOME_SUCCESS EXIT_SUCCESS // normally 0
300 #define EXIT_OUTCOME_UNKNOWN EXIT_FAILURE // normally 1
301 #define EXIT_OUTCOME_NOSTART 4 // 0b.....1..
302 #define EXIT_OUTCOME_STOPPED 8 // 0b....1...
303 #define EXIT_OUTCOME_FAILURE 9 // 0b....1..1
304 #define EXIT_OUTCOME_ABORTED 16 // 0b...1....
305 
306 // clang-format off
307 ENUM_SERIALIZATION(SimulationOutcome, ({
308  {SimulationOutcome::Aborted, "aborted"},
309  {SimulationOutcome::NoStart, "no-start"},
310  {SimulationOutcome::Failure, "failure"},
311  {SimulationOutcome::Success, "success"},
312  {SimulationOutcome::Stopped, "stopped"},
313 }))
314 // clang-format on
315 
316 namespace events {
317 
318 DEFINE_NIL_EVENT(Start, "start", "start of simulation")
319 DEFINE_NIL_EVENT(Stop, "stop", "stop of simulation")
320 DEFINE_NIL_EVENT(Success, "success", "simulation success")
321 DEFINE_NIL_EVENT(Failure, "failure", "simulation failure")
322 DEFINE_NIL_EVENT(Reset, "reset", "reset of simulation")
323 DEFINE_NIL_EVENT(Pause, "pause", "pausation of simulation")
324 DEFINE_NIL_EVENT(Resume, "resume", "resumption of simulation after pause")
325 DEFINE_NIL_EVENT(Loop, "loop", "begin of inner simulation loop each cycle")
326 
327 } // namespace events
328 
335  // Setup
336  std::unique_ptr<Server> server;
337  std::shared_ptr<Coordinator> coordinator;
338  std::shared_ptr<Registrar> registrar;
339  std::unique_ptr<CommandExecuter> commander;
340 
341  // Configuration
342  cloe::Stack config;
343  std::string uuid{};
344  bool report_progress{false};
345 
346  // State
347  SimulationSync sync;
348  SimulationProgress progress;
349  SimulationStatistics statistics;
350  cloe::Model* now_initializing{nullptr};
351  std::map<std::string, std::unique_ptr<cloe::Simulator>> simulators;
352  std::map<std::string, std::shared_ptr<cloe::Vehicle>> vehicles;
353  std::map<std::string, std::unique_ptr<cloe::Controller>> controllers;
354  boost::optional<SimulationOutcome> outcome;
356  bool pause_execution{false};
357 
358  // Events
359  std::shared_ptr<events::LoopCallback> callback_loop;
360  std::shared_ptr<events::PauseCallback> callback_pause;
361  std::shared_ptr<events::ResumeCallback> callback_resume;
362  std::shared_ptr<events::StartCallback> callback_start;
363  std::shared_ptr<events::StopCallback> callback_stop;
364  std::shared_ptr<events::SuccessCallback> callback_success;
365  std::shared_ptr<events::FailureCallback> callback_failure;
366  std::shared_ptr<events::ResetCallback> callback_reset;
367  std::shared_ptr<events::TimeCallback> callback_time;
368 
369  public:
370  std::string version() const;
371 
372  std::shared_ptr<cloe::Registrar> simulation_registrar();
373 
374  std::vector<std::string> model_ids() const;
375  std::vector<std::string> simulator_ids() const;
376  std::vector<std::string> controller_ids() const;
377  std::vector<std::string> vehicle_ids() const;
378  std::vector<std::string> plugin_ids() const;
379 
380  bool foreach_model(std::function<bool(cloe::Model&, const char* type)> f);
381  bool foreach_model(std::function<bool(const cloe::Model&, const char* type)> f) const;
382  bool foreach_simulator(std::function<bool(cloe::Simulator&)> f);
383  bool foreach_simulator(std::function<bool(const cloe::Simulator&)> f) const;
384  bool foreach_controller(std::function<bool(cloe::Controller&)> f);
385  bool foreach_controller(std::function<bool(const cloe::Controller&)> f) const;
386  bool foreach_vehicle(std::function<bool(cloe::Vehicle&)> f);
387  bool foreach_vehicle(std::function<bool(const cloe::Vehicle&)> f) const;
388 };
389 
390 } // namespace engine
Definition: controller.hpp:126
Definition: model.hpp:203
Definition: simulator.hpp:139
Definition: stack.hpp:901
Definition: sync.hpp:34
Definition: vehicle.hpp:106
Definition: statistics.hpp:110
void reset()
Definition: statistics.hpp:119
Definition: progress.hpp:35
void update_safe(double p)
Definition: progress.hpp:94
double percent() const
Definition: progress.hpp:106
Duration elapsed() const
Definition: progress.hpp:116
void update(double p)
Definition: progress.hpp:73
void end()
Definition: progress.hpp:59
bool is_ended() const
Definition: progress.hpp:68
void begin()
Definition: progress.hpp:50
Definition: simulation_context.hpp:127
bool exec_report()
Definition: simulation_context.hpp:212
Definition: simulation_context.hpp:57
double realtime_factor() const override
Definition: simulation_context.hpp:73
double achievable_realtime_factor() const override
Definition: simulation_context.hpp:79
cloe::Duration step_width() const override
Definition: simulation_context.hpp:63
cloe::Duration time() const override
Definition: simulation_context.hpp:64
cloe::Duration eta() const override
Definition: simulation_context.hpp:65
void increment_step()
Definition: simulation_context.hpp:91
void set_realtime_factor(double s)
Definition: simulation_context.hpp:100
uint64_t step() const override
Definition: simulation_context.hpp:62
std::chrono::nanoseconds Duration
Definition: cloe_fwd.hpp:36
#define ENUM_SERIALIZATION(xType, xMap)
Definition: enum.hpp:51
#define DEFINE_NIL_EVENT(xName, xname, xdescription)
Definition: nil_event.hpp:57
SimulationOutcome
Definition: simulation_context.hpp:287
@ Success
Simulation explicitly concluded with success.
@ Aborted
Simulation aborted due to technical problems or interrupt.
@ Stopped
Simulation concluded, but without valuation.
@ Failure
Simulation explicitly concluded with failure.
@ NoStart
Simulation unable to start.
Definition: simulation_context.hpp:334
Definition: simulation_context.hpp:258