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