$darkmode
main_run.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  */
25 #pragma once
26 
27 #include <csignal> // for signal
28 #include <cstdlib> // for getenv
29 #include <iostream> // for cerr
30 
31 // NOTE: Unfortunately, <boost/uuid/uuid_generators.hpp> includes Boost headers
32 // that make use of deprecated headers. This is fixed in Boost 1.70.0, which we
33 // cannot use until we migrate oak::Server away from cppnetlib.
34 // See: https://github.com/boostorg/random/issues/49
35 #define BOOST_ALLOW_DEPRECATED_HEADERS
36 
37 #include <boost/lexical_cast.hpp> // for lexical_cast
38 #include <boost/uuid/uuid_generators.hpp> // for random_generator
39 #include <boost/uuid/uuid_io.hpp>
40 
41 #include <cloe/core.hpp> // for logger::get
42 #include <fable/utility.hpp> // for read_conf
43 
44 #include "main_stack.hpp" // for Stack, new_stack
45 #include "simulation.hpp" // for Simulation, SimulationResult
46 #include "stack.hpp" // for Stack
47 
48 namespace engine {
49 
50 void handle_signal(int);
51 
52 struct RunOptions {
53  cloe::StackOptions stack_options;
54  std::ostream& output = std::cout;
55  std::ostream& error = std::cerr;
56 
57  // Options
58  std::string uuid;
59 
60  // Flags:
61  int json_indent = 2;
62  bool allow_empty = false;
63  bool write_output = true;
64  bool require_success = false;
65  bool report_progress = true;
66 };
67 
68 Simulation* GLOBAL_SIMULATION_INSTANCE{nullptr};
69 
70 template <typename Func>
71 auto handle_cloe_error(std::ostream& out, Func f) -> decltype(f()) {
72  try {
73  return f();
74  } catch (cloe::Error& e) {
75  out << "Error: " << e.what() << std::endl;
76  if (e.has_explanation()) {
77  out << " Note:\n" << fable::indent_string(e.explanation(), " ") << std::endl;
78  }
79  throw cloe::ConcludedError(e);
80  }
81 }
82 
83 inline int run(const RunOptions& opt, const std::vector<std::string>& filepaths) {
84  cloe::logger::get("cloe")->info("Cloe {}", CLOE_ENGINE_VERSION);
85  cloe::StackOptions stack_opt = opt.stack_options;
86 
87  // Set the UUID of the simulation:
88  std::string uuid;
89  if (!opt.uuid.empty()) {
90  uuid = opt.uuid;
91  } else if (std::getenv(CLOE_SIMULATION_UUID_VAR) != nullptr) {
92  uuid = std::getenv(CLOE_SIMULATION_UUID_VAR);
93  } else {
94  uuid = boost::lexical_cast<std::string>(boost::uuids::random_generator()());
95  }
96  stack_opt.environment->set(CLOE_SIMULATION_UUID_VAR, uuid);
97 
98  // Load the stack file:
99  cloe::Stack s;
100  try {
101  handle_cloe_error(*stack_opt.error, [&]() {
102  s = cloe::new_stack(stack_opt, filepaths);
103  if (!opt.allow_empty) {
104  s.check_completeness();
105  }
106  });
107  } catch (cloe::ConcludedError& e) {
108  return EXIT_FAILURE;
109  }
110 
111  // Create simulation:
112  Simulation sim(s, uuid);
113  GLOBAL_SIMULATION_INSTANCE = &sim;
114  std::signal(SIGINT, handle_signal);
115 
116  // Set options:
117  sim.set_report_progress(opt.report_progress);
118 
119  // Run simulation:
120  auto result = handle_cloe_error(*stack_opt.error, [&]() { return sim.run(); });
121  if (result.outcome == SimulationOutcome::NoStart) {
122  // If we didn't get past the initialization phase, don't output any
123  // statistics or write any files, just go home.
124  return EXIT_FAILURE;
125  }
126 
127  // Write results:
128  if (opt.write_output) {
129  sim.write_output(result);
130  }
131  opt.output << cloe::Json(result).dump(opt.json_indent) << std::endl;
132 
133  switch (result.outcome) {
134  case SimulationOutcome::Success:
135  return EXIT_OUTCOME_SUCCESS;
136  case SimulationOutcome::Stopped:
137  return (opt.require_success ? EXIT_OUTCOME_STOPPED : EXIT_OUTCOME_SUCCESS);
138  case SimulationOutcome::Aborted:
139  return EXIT_OUTCOME_ABORTED;
140  case SimulationOutcome::NoStart:
141  return EXIT_OUTCOME_NOSTART;
142  case SimulationOutcome::Failure:
143  return EXIT_OUTCOME_FAILURE;
144  default:
145  return EXIT_OUTCOME_UNKNOWN;
146  }
147 }
148 
164 inline void handle_signal(int sig) {
165  static size_t interrupts = 0;
166  switch (sig) {
167  case SIGSEGV:
168  case SIGABRT:
169  abort();
170  break;
171  case SIGINT:
172  default:
173  std::cerr << std::endl; // print newline so that ^C is on its own line
174  if (++interrupts == 3) {
175  std::signal(sig, SIG_DFL); // third time goes to the default handler
176  }
177  if (GLOBAL_SIMULATION_INSTANCE) {
178  GLOBAL_SIMULATION_INSTANCE->signal_abort();
179  }
180  break;
181  }
182 }
183 
184 } // namespace engine
size_t write_output(const SimulationResult &) const
Definition: simulation.cpp:1308
Definition: error.hpp:80
Definition: error.hpp:35
Definition: main_stack.hpp:38
void set_report_progress(bool value)
Definition: simulation.hpp:138
Definition: stack.hpp:888
Definition: coordinator.cpp:39
Definition: simulation.hpp:102
void handle_signal(int)
Definition: main_run.hpp:164
void signal_abort()
Definition: simulation.cpp:1384
Definition: main_run.hpp:52