$darkmode
main_usage.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 <iostream> // for ostream
28 #include <memory> // for shared_ptr<>
29 #include <string> // for string
30 #include <utility> // for pair<>, move
31 #include <vector> // for vector<>
32 
33 #include <cloe/utility/xdg.hpp> // for find_all_config
34 
35 #include "main_stack.hpp" // for Stack, new_stack
36 
37 namespace engine {
38 
39 struct UsageOptions {
40  cloe::StackOptions stack_options;
41  std::ostream& output = std::cout;
42  std::ostream& error = std::cerr;
43 
44  // Flags:
45  bool plugin_usage = false;
46  bool output_json = false;
47  int json_indent = 2;
48 };
49 
50 void show_usage(cloe::Stack s, std::ostream& output);
51 void show_plugin_usage(std::shared_ptr<cloe::Plugin> p, std::ostream& os, bool json, size_t indent);
52 
53 inline int usage(const UsageOptions& opt, const std::string& argument) {
54  cloe::Stack s;
55  try {
56  s = cloe::new_stack(opt.stack_options);
57  } catch (cloe::ConcludedError& e) {
58  return EXIT_FAILURE;
59  }
60 
61  // Show requested usage.
62  bool result = true;
63  if (argument.empty()) {
64  if (opt.output_json) {
65  opt.output << s.schema().json_schema().dump(opt.json_indent) << std::endl;
66  } else {
67  show_usage(s, opt.output);
68  }
69  } else {
70  std::shared_ptr<cloe::Plugin> p = s.get_plugin_or_load(argument);
71  show_plugin_usage(p, opt.output, opt.output_json, opt.json_indent);
72  }
73  return result ? EXIT_SUCCESS : EXIT_FAILURE;
74 }
75 
76 // --------------------------------------------------------------------------------------------- //
77 
78 template <typename T>
79 void print_plugin_usage(std::ostream& os, const cloe::Plugin& p, const std::string& prefix = " ") {
80  auto f = p.make<T>();
81  auto u = f->schema().usage_compact();
82  os << dump_json(u, prefix) << std::endl;
83 }
84 
88 inline void print_available_plugins(const cloe::Stack& s, std::ostream& os,
89  const std::string& word = "Available") {
90  const std::string prefix = " ";
91  auto print_available = [&](const std::string& type) {
92  os << word << " " << type << "s:" << std::endl;
93 
94  std::vector<std::pair<std::string, std::string>> vec;
95  for (auto& kv : s.get_all_plugins()) {
96  if (kv.second->type() == type) {
97  vec.emplace_back(std::make_pair(kv.second->name(), kv.first));
98  }
99  }
100 
101  if (vec.empty()) {
102  os << prefix << "n/a" << std::endl << std::endl;
103  return;
104  }
105 
106  // Calculate how wide the first column needs to be:
107  size_t max_length = 0;
108  for (auto x : vec) {
109  if (x.first.size() > max_length) {
110  max_length = x.first.size();
111  }
112  }
113 
114  // Print the available names:
115  for (auto x : vec) {
116  auto n = x.first.size();
117  os << prefix << x.first << std::string(max_length - n, ' ') << " [" << x.second << "]\n";
118  }
119  os << std::endl;
120  };
121 
122  print_available("simulator");
123  print_available("controller");
124  print_available("component");
125 }
126 
130 inline void show_usage(cloe::Stack s, std::ostream& os) {
131  os << fmt::format("Cloe {} ({})", CLOE_ENGINE_VERSION, CLOE_ENGINE_TIMESTAMP) << std::endl;
132 
133  os << R"(
134 Cloe is a simulation middleware tool that ties multiple plugins together into a
135 cohesive and coherent simulation. This is performed based on JSON input that we
136 name "stack files".
137 
138 In general, stack files are combined to form a single stack file configuration.
139 Thus it is possible to reduce duplicate information by including one stack file
140 from another, or augmenting a configuration on the command line by specifying
141 a further configuration.
142 
143 By default, Cloe will include certain discovered system and user configuration
144 files by sourcing them, if they are available:
145 
146  /etc/xdg/cloe/config.json
147  ${XDG_CONFIG_HOME-${HOME}/.config}/cloe/config.json
148 
149 While this is useful for user- or system-specific configurations, it can be
150 undesirable when reproducibility is of importance, such as common during testing.
151 Thus, this behavior can be disabled with the --no-system-confs flag.
152 
153 Several subcommands are available:
154 
155  version
156  Show program version information.
157 
158  As a middleware solution, Cloe provides several interfaces that are versioned
159  according to the semantic versioning standard (see https://semver.org/).
160  The version command shows this information, along with other useful facts,
161  such as the date of compilation. One of the most important version numbers is
162  that of the stack file. This defines the format of the JSON schema, which all
163  input stack files must match.
164 
165  Examples:
166  cloe-engine version
167  cloe-engine version -jJ4
168 
169  usage
170  Show schema or plugin usage information.
171 
172  A stack file does not only contain configuration data for Cloe itself. Each
173  component involved in the simulation is configured through the stack file.
174  This command provides usage information for the entire stack file or for
175  individual plugins. These plugins can be referred to by name, key, or path.
176  When the --json flag is specified, the JSON schema is printed, which allows
177  automatic validation of input stack files.
178 
179  Examples:
180  cloe-engine usage -j
181  cloe-engine usage builtin://controller/nop
182  cloe-engine -p build/plugins usage basic
183  cloe-engine usage -j build/plugins/controller_basic.so
184 
185  dump
186  Dump configuration of merged stack files.
187 
188  A stack file as stored on disk does not necessarily represent the exact
189  configuration that is used by Cloe, as default values are not specified and
190  the stack file may include other stack files. This command prints the final
191  merged configuration of a set of stack files. This is useful for guaranteeing
192  future reproducibility or for debugging purposes.
193 
194  Examples:
195  cloe-engine dump tests/config_nop_infinite.json
196 
197  check
198  Validate individual or merged stack files.
199 
200  We may check any number of stack files to find errors before we run them.
201  Plugins are loaded and used to validate the stack file to the fullest
202  extent possible. Note that this cannot find errors that only exhibit at
203  runtime, such as simulator that is inconsistently configured or a scenario
204  that uses other vehicle names.
205 
206  The output from the check command follows the UNIX philosophy by default,
207  but this can be altered with the --summarize option flag.
208 
209  Examples:
210  cloe-engine check tests/test_nop_smoketest.json tests/option_timestep_60.json
211  cloe-engine --no-system-confs check -ds tests/*.json
212 
213  run
214  Run a single simulation with merged stack files.
215 
216  Examples:
217  cloe-engine -l trace run cloe-stackfile.json debug-conf.json
218  cloe-engine --no-system-confs -l warn run tests/build_config.json
219 
220 Please report any bugs to: cloe-dev@eclipse.org
221 
222 ---
223 
224 )";
225 
226  {
227  auto files = cloe::utility::find_all_config(CLOE_XDG_SUFFIX "/config.json");
228  if (files.size() != 0) {
229  os << "Discovered default configuration files:" << std::endl;
230  for (auto& f : files) {
231  os << " " << f.native() << std::endl;
232  }
233  os << std::endl;
234  }
235  }
236 
237  print_available_plugins(std::move(s), os);
238 }
239 
240 inline void show_plugin_usage(std::shared_ptr<cloe::Plugin> p, std::ostream& os, bool json,
241  size_t indent) {
242  auto m = p->make<cloe::ModelFactory>();
243 
244  if (json) {
245  cloe::Json js = m->schema().json_schema_qualified(p->path());
246  js["title"] = m->name();
247  js["description"] = m->description();
248  os << js.dump(indent) << std::endl;
249  return;
250  }
251 
252  os << "Name: " << m->name() << std::endl;
253  os << "Type: " << p->type() << std::endl;
254  os << "Path: ";
255  if (p->path() == "") {
256  os << "n/a" << std::endl;
257  } else {
258  os << p->path() << std::endl;
259  }
260  os << "Usage: " << m->schema().usage().dump(indent) << std::endl;
261  os << "Defaults: " << m->to_json().dump(indent) << std::endl;
262 }
263 
264 } // namespace engine
Definition: error.hpp:80
Definition: model.hpp:403
Definition: plugin.hpp:61
std::unique_ptr< F > make() const
Definition: plugin.hpp:179
Definition: stack.hpp:901
std::shared_ptr< Plugin > get_plugin_or_load(const std::string &key_or_path) const
Definition: stack.cpp:372
const std::map< std::string, std::shared_ptr< Plugin > > & get_all_plugins() const
Definition: stack.hpp:1015
Schema & schema()
Definition: confable.cpp:47
Json json_schema() const override
Definition: schema.hpp:254
void show_usage(cloe::Stack s, std::ostream &output)
Definition: main_usage.hpp:130
void print_available_plugins(const cloe::Stack &s, std::ostream &os, const std::string &word="Available")
Definition: main_usage.hpp:88
Definition: main_stack.hpp:38
Definition: main_usage.hpp:39