$darkmode
stack.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  */
24 #pragma once
25 
26 #include <map> // for map<>
27 #include <memory> // for shared_ptr<>
28 #include <set> // for set<>
29 #include <string> // for string
30 #include <utility> // for move
31 #include <vector> // for vector<>
32 
33 #include <boost/filesystem/path.hpp> // for path
34 #include <boost/optional.hpp> // for optional<>
35 #include <fable/schema/boost_optional.hpp> // for Optional<>
36 #include <fable/schema/boost_path.hpp> // for Path
37 #include <fable/schema/custom.hpp> // for CustomDeserializer
38 #include <fable/schema/factory.hpp> // for Factory
39 
40 #include <cloe/component.hpp> // for ComponentFactory
41 #include <cloe/controller.hpp> // for ControllerFactory
42 #include <cloe/core.hpp> // for Conf, Confable, Json
43 #include <cloe/simulator.hpp> // for SimulatorFactory
44 #include <cloe/trigger.hpp> // for Source
45 #include <cloe/utility/command.hpp> // for Command
46 
47 #include "plugin.hpp" // for Plugin
48 
49 #ifndef CLOE_STACK_VERSION
50 #define CLOE_STACK_VERSION "4.1"
51 #endif
52 
53 #ifndef CLOE_STACK_SUPPORTED_VERSIONS
54 #define CLOE_STACK_SUPPORTED_VERSIONS \
55  { "4", "4.0", "4.1" }
56 #endif
57 
58 #ifndef CLOE_XDG_SUFFIX
59 #define CLOE_XDG_SUFFIX "cloe"
60 #endif
61 
62 #ifndef CLOE_CONFIG_HOME
63 #define CLOE_CONFIG_HOME "${XDG_CONFIG_HOME-${HOME}/.config}/" CLOE_XDG_SUFFIX
64 #endif
65 
66 #ifndef CLOE_DATA_HOME
67 #define CLOE_DATA_HOME "${XDG_DATA_HOME-${HOME}/.local/share}/" CLOE_XDG_SUFFIX
68 #endif
69 
70 #define CLOE_SIMULATION_UUID_VAR "CLOE_SIMULATION_UUID"
71 
72 namespace cloe {
73 
80 class PersistentConfable : public Confable {
81  public:
82  const Conf& conf() const { return conf_; }
83 
84  void from_conf(const Conf& c) {
86  conf_ = c;
87  }
88 
89  protected:
90  Conf conf_;
91 };
92 
93 inline auto id_prototype(std::string desc = "") {
94  return schema::make_prototype<std::string>(std::move(desc)).c_identifier();
95 }
96 
97 inline auto id_path_prototype(std::string desc = "") {
98  return schema::make_prototype<std::string>(std::move(desc))
99  .pattern("^([a-zA-Z_][a-zA-Z0-9_]*/?)+$");
100 }
101 
102 // --------------------------------------------------------------------------------------------- //
103 
108 using IncludeConf = boost::filesystem::path;
109 using IncludeSchema = decltype(schema::make_schema(static_cast<IncludeConf*>(nullptr), ""));
111 
112 // --------------------------------------------------------------------------------------------- //
113 
163 struct LoggingConf : public Confable {
164  std::string name;
165  boost::optional<std::string> pattern;
166  boost::optional<LogLevel> level;
167 
168  public: // Special
169  void apply() const;
170 
171  public: // Confable Overrides
172  CONFABLE_SCHEMA(LoggingConf) {
173  using namespace schema; // NOLINT(build/namespaces)
174  return Schema{
175  {"name", make_schema(&name, "name of the logger to configure").require()},
176  {"pattern", make_schema(&pattern, "pattern of the logger")},
177  {"level", make_schema(&level, "level of the logger")},
178  };
179  }
180 
181  bool validate(const Conf& c, std::optional<SchemaError>& err) const override {
182  const auto& s = this->schema();
183  if (!s.validate(c, err)) {
184  return false;
185  }
186  if (!c.has("pattern") && !c.has("level")) {
187  err.emplace(SchemaError(c, s.json_schema(),
188  "require at least one of 'pattern' or 'level' properties"));
189  return false;
190  }
191  return true;
192  }
193 };
194 
195 // --------------------------------------------------------------------------------------------- //
196 
197 struct ServerConf : public Confable {
198  bool listen{true};
199  std::string listen_address{"127.0.0.1"};
200  uint16_t listen_port{8080};
201  uint16_t listen_threads{10};
202  std::string api_prefix{"/api"};
203  std::string static_prefix{""};
204 
205  public: // Confable Overrides
206  CONFABLE_SCHEMA(ServerConf) {
207  using namespace schema; // NOLINT(build/namespaces)
208  return Schema{
209  {"listen", make_schema(&listen, "whether web server is enabled")},
210  {"listen_address", make_schema(&listen_address, "address web server should listen at")},
211  {"listen_port", make_schema(&listen_port, "port web server should listen at")},
212  {"listen_threads", make_schema(&listen_threads, "threads web server should use")},
213  {"static_prefix", make_schema(&static_prefix, "endpoint prefix for static resources")},
214  {"api_prefix", make_schema(&api_prefix, "endpoint prefix for API resources")},
215  };
216  }
217 };
218 
219 // --------------------------------------------------------------------------------------------- //
220 
227  boost::filesystem::path plugin_path{};
228 
230  boost::optional<std::string> plugin_name{};
231 
233  boost::optional<std::string> plugin_prefix{};
234 
236  boost::optional<bool> ignore_missing{};
237 
243  boost::optional<bool> ignore_failure{};
244 
251  boost::optional<bool> allow_clobber{};
252 
253  public: // Constructors
254  PluginConf() = default;
255  explicit PluginConf(const std::string& p) : plugin_path(p) {}
256 
257  public: // Special
265  std::string canonical() const;
266 
267  public: // Confable Overrides
268  CONFABLE_SCHEMA(PluginConf) {
269  // clang-format off
270  using namespace schema; // NOLINT(build/namespaces)
271  auto proto = String(nullptr, "").c_identifier();
272  return Struct{
273  {"path", make_schema(&plugin_path, "absolute or relative path to plugin").require().not_empty().normalize(true)},
274  {"name", make_schema(&plugin_name, proto, "alternative name plugin is available by")},
275  {"prefix", make_schema(&plugin_prefix, proto, "prefix the plugin name with this")},
276  {"ignore_missing", make_schema(&ignore_missing, "ignore not-exist errors")},
277  {"ignore_failure", make_schema(&ignore_failure, "ignore plugin loading errors")},
278  {"allow_clobber", make_schema(&allow_clobber, "replace same-named plugins")},
279  };
280  // clang-format on
281  }
282 };
283 
284 using PluginsSchema = schema_type<std::vector<PluginConf>>::type;
285 
286 // --------------------------------------------------------------------------------------------- //
287 
294 enum class WatchdogMode {
295  Off,
296  Log,
297  Abort,
298  Kill,
299 };
300 
301 // clang-format off
302 ENUM_SERIALIZATION(WatchdogMode, ({
303  {WatchdogMode::Off, "off"},
304  {WatchdogMode::Log, "log"},
305  {WatchdogMode::Abort, "abort"},
306  {WatchdogMode::Kill, "kill"},
307 }))
308 // clang-format on
309 
310 struct EngineConf : public Confable {
311  // Parsing:
312  std::vector<std::string> ignore_sections{};
313 
314  // Security:
315  bool security_enable_hooks{true};
316  bool security_enable_commands{false};
317  bool security_enable_includes{true};
318  size_t security_max_include_depth{64};
319 
320  // Plugins:
321  std::vector<std::string> plugin_path{};
322  bool plugins_ignore_missing{false};
323  bool plugins_ignore_failure{false};
324  bool plugins_allow_clobber{true};
325 
326  // Hooks:
327  std::vector<Command> hooks_pre_connect{};
328  std::vector<Command> hooks_post_disconnect{};
329 
330  // Triggers:
331  bool triggers_ignore_source{false};
332 
333  // Output:
334  boost::optional<boost::filesystem::path> registry_path{CLOE_DATA_HOME "/registry"};
335  boost::optional<boost::filesystem::path> output_path{"${CLOE_SIMULATION_UUID}"};
336  boost::optional<boost::filesystem::path> output_file_config{"config.json"};
337  boost::optional<boost::filesystem::path> output_file_result{"result.json"};
338  boost::optional<boost::filesystem::path> output_file_triggers{"triggers.json"};
339  boost::optional<boost::filesystem::path> output_file_data_stream;
340  bool output_clobber_files{true};
341 
347  std::chrono::milliseconds polling_interval{100};
348 
354  WatchdogMode watchdog_mode{WatchdogMode::Off};
355 
365  std::chrono::milliseconds watchdog_default_timeout{90'000};
366 
394  std::map<std::string, boost::optional<std::chrono::milliseconds>> watchdog_state_timeouts{
395  {"CONNECT", std::chrono::milliseconds{300'000}},
396  {"ABORT", std::chrono::milliseconds{90'000}},
397  {"STOP", std::chrono::milliseconds{300'000}},
398  {"DISCONNECT", std::chrono::milliseconds{600'000}},
399  };
400 
407  bool keep_alive{false};
408 
409  public: // Confable Overrides
410  CONFABLE_SCHEMA(EngineConf) {
411  // clang-format off
412  using namespace schema; // NOLINT(build/namespaces)
413  auto dir_proto = []() { return make_prototype<boost::filesystem::path>().not_file(); };
414  auto file_proto = []() { return make_prototype<boost::filesystem::path>().not_dir().resolve(false); };
415  return Struct{
416  {"ignore", make_schema(&ignore_sections, "JSON pointers to sections that should be ignored").extend(true)},
417  {"security", Struct{
418  {"enable_hooks_section", make_schema(&security_enable_hooks, "whether to enable engine hooks")},
419  {"enable_command_action", make_schema(&security_enable_commands, "whether to enable the command action")},
420  {"enable_include_section", make_schema(&security_enable_includes, "whether to allow config files to include other files")},
421  {"max_include_depth", make_schema(&security_max_include_depth, "how many recursive includes are allowed")},
422  }},
423  {"hooks", Struct{
424  {"pre_connect", make_schema(&hooks_pre_connect, "pre-connect hooks to execute").extend(true)},
425  {"post_disconnect", make_schema(&hooks_post_disconnect, "post-disconnect hooks to execute").extend(true)},
426  }},
427  {"plugin_path", make_schema(&plugin_path, "list of directories to scan for plugins").extend(false)},
428  {"plugins", Struct{
429  {"ignore_missing", make_schema(&plugins_ignore_missing, "ignore not-exist errors")},
430  {"ignore_failure", make_schema(&plugins_ignore_failure, "ignore plugin loading errors")},
431  {"allow_clobber", make_schema(&plugins_allow_clobber, "replace same-named plugins")},
432  }},
433  {"registry_path", make_schema(&registry_path, dir_proto(), "cloe registry directory")},
434  {"output", Struct{
435  {"path", make_schema(&output_path, dir_proto().resolve(false), "directory to dump output files in, relative to registry path")},
436  {"clobber", make_schema(&output_clobber_files, "whether to clobber existing files or not")},
437  {"files", Struct{
438  {"config", make_schema(&output_file_config, file_proto(), "file to store config in")},
439  {"result", make_schema(&output_file_result, file_proto(), "file to store simulation result in")},
440  {"triggers", make_schema(&output_file_triggers, file_proto(), "file to store triggers in")},
441  {"api_recording", make_schema(&output_file_data_stream, file_proto(), "file to store api data stream")},
442  }},
443  }},
444  {"triggers", Struct{
445  {"ignore_source", make_schema(&triggers_ignore_source, "ignore trigger source when reading in triggers")},
446  }},
447  {"polling_interval", make_schema(&polling_interval, "milliseconds to sleep when polling for next state")},
448  {"watchdog", Struct{
449  {"mode", make_schema(&watchdog_mode, "modus operandi of watchdog [one of: off, log, abort, kill]")},
450  {"default_timeout", make_schema(&watchdog_default_timeout, "default timeout if not overridden, 0 for no timeout")},
451  {"state_timeouts", make_schema(&watchdog_state_timeouts, "timeout specific to a given state, 0 for no timeout").unique_properties(false)},
452  }},
453  {"keep_alive", make_schema(&keep_alive, "keep simulation alive after termination")},
454  };
455  // clang-format on
456  }
457 };
458 
459 using EngineSchema = schema_type<EngineConf>::type;
460 
461 // --------------------------------------------------------------------------------------------- //
462 
473 struct DefaultConf : public Confable {
474  boost::optional<std::string> name;
475  boost::optional<std::string> binding;
476  Conf args;
477 
478  public: // Confable Overrides
479  CONFABLE_SCHEMA(DefaultConf) {
480  using namespace schema; // NOLINT(build/namespaces)
481  return Struct{
482  {"binding", make_schema(&binding, "name of binding")},
483  {"name", make_schema(&name, id_prototype(), "globally unique identifier for component")},
484  {"args", make_schema(&args, "defaults to set for binding/name combination").require()},
485  };
486  }
487 };
488 
489 // --------------------------------------------------------------------------------------------- //
490 
491 template <typename C, typename F>
493  public:
494  FactoryPlugin() {
495  this->set_factory_key("binding");
496  this->set_args_subset(false);
497  }
498  virtual ~FactoryPlugin() = default;
499 
500  void add_plugin(const std::string& name, std::shared_ptr<Plugin> p) {
501  this->set_factory(name, p->make<F>()->schema(), [p, name](const Conf& c) {
502  C tmp{name, p->make<F>()};
503  tmp.from_conf(c);
504  return tmp;
505  });
506  }
507 };
508 
509 // --------------------------------------------------------------------------------------------- //
510 
514 struct SimulatorConf : public Confable {
515  const std::string binding;
516  boost::optional<std::string> name;
517  std::shared_ptr<SimulatorFactory> factory;
518  Conf args;
519 
520  public: // Constructors
521  SimulatorConf(const std::string& b, std::shared_ptr<SimulatorFactory> f)
522  : binding(b), factory(std::move(f)) {}
523 
524  public: // Confable Overrides
525  CONFABLE_SCHEMA(SimulatorConf) {
526  using namespace schema; // NOLINT(build/namespaces)
527  return Struct{
528  {"binding", make_const_schema(binding, "name of simulator binding").require()},
529  {"name", make_schema(&name, id_prototype(), "identifier override for binding")},
530  {"args", make_schema(&args, factory->schema(), "factory-specific arguments")},
531  };
532  }
533 };
534 
535 class SimulatorSchema : public FactoryPlugin<SimulatorConf, SimulatorFactory> {
536  public:
537  SimulatorSchema();
538  virtual ~SimulatorSchema() = default;
539 
540  bool validate(const Conf& c, std::optional<SchemaError>& err) const override;
541 };
542 
543 // --------------------------------------------------------------------------------------------- //
544 
548 struct ControllerConf : public Confable {
549  const std::string binding;
550  boost::optional<std::string> name;
551  std::string vehicle;
552  std::shared_ptr<ControllerFactory> factory;
553  Conf args;
554 
555  public: // Constructors
556  ControllerConf(const std::string& b, std::shared_ptr<ControllerFactory> f)
557  : binding(b), factory(std::move(f)) {}
558 
559  public: // Confable Overrides
560  CONFABLE_SCHEMA(ControllerConf) {
561  // clang-format off
562  using namespace schema; // NOLINT(build/namespaces)
563  return Struct{
564  {"binding", make_const_schema(binding, "name of controller binding").require()},
565  {"name", make_schema(&name, id_prototype(), "identifier override for binding")},
566  {"vehicle", make_schema(&vehicle, "vehicle controller is assigned to").c_identifier().require()},
567  {"args", make_schema(&args, factory->schema(), "factory-specific arguments")},
568  };
569  // clang-format on
570  }
571 };
572 
573 class ControllerSchema : public FactoryPlugin<ControllerConf, ControllerFactory> {
574  public:
576  virtual ~ControllerSchema() = default;
577 
578  bool validate(const Conf& c, std::optional<SchemaError>& err) const override;
579 };
580 
581 // --------------------------------------------------------------------------------------------- //
582 
583 struct FromSimulator : public Confable {
584  std::string simulator;
585  std::string index_str;
586  size_t index_num;
587 
588  public: // Special
589  bool is_by_name() const { return !index_str.empty(); }
590  bool is_by_index() const { return index_str.empty(); }
591  void clear() {
592  simulator.clear();
593  index_str.clear();
594  index_num = 0;
595  }
596 
597  public: // Confable Overrides
598  CONFABLE_SCHEMA(FromSimulator) {
599  // clang-format off
600  using namespace schema; // NOLINT(build/namespaces)
601  return Variant{
602  Struct{
603  {"simulator", make_schema(&simulator, "simulator").not_empty().require()},
604  {"index", make_schema(&index_num, "index of vehicle in simulator").require()},
605  },
606  Struct{
607  {"simulator", make_schema(&simulator, "simulator").not_empty().require()},
608  {"name", make_schema(&index_str, "name of vehicle in simulator").not_empty().require()},
609  },
610  };
611  // clang-format on
612  }
613 
614  void from_conf(const Conf& c) override {
615  clear(); // Avoid inconsistent state
617  }
618 
619  void to_json(Json& j) const override {
620  if (is_by_index()) {
621  j = Json{
622  {"simulator", simulator},
623  {"index", index_num},
624  };
625  } else {
626  j = Json{
627  {"simulator", simulator},
628  {"name", index_str},
629  };
630  }
631  }
632 };
633 
634 struct ComponentConf : public Confable {
635  const std::string binding;
636  boost::optional<std::string> name;
637  std::vector<std::string> from;
638  std::shared_ptr<ComponentFactory> factory;
639  Conf args;
640 
641  public: // Constructors
642  ComponentConf(const std::string& b, std::shared_ptr<ComponentFactory> f)
643  : binding(b), factory(std::move(f)) {}
644 
645  public: // Confable Overrides
646  CONFABLE_SCHEMA(ComponentConf) {
647  // clang-format off
648  using namespace schema; // NOLINT(build/namespaces)
649  return Struct{
650  {"binding", make_const_schema(binding, "name of binding").require()},
651  {"name", make_schema(&name, id_prototype(), "globally unique identifier for component")},
652  {"from", Variant{
653  make_schema(&from, "component inputs for binding"),
654  CustomDeserializer(
655  make_prototype<std::string>("component input for binding"),
656  [this](CustomDeserializer*, const Conf& c) {
657  this->from.push_back(c.get<std::string>());
658  }
659  ),
660  }},
661  {"args", make_schema(&args, factory->schema(), "factory-specific args")},
662  };
663  // clang-format on
664  }
665 };
666 
667 class ComponentSchema : public FactoryPlugin<ComponentConf, ComponentFactory> {
668  public:
669  ComponentSchema();
670  virtual ~ComponentSchema() = default;
671 
672  bool validate(const Conf& c, std::optional<SchemaError>& err) const override;
673 };
674 
675 // TODO(ben): Add AliasConf as alternative to ComponentConf.
676 
698 struct VehicleConf : public Confable {
699  std::string name;
700  FromSimulator from_sim;
701  std::string from_veh;
702  std::map<std::string, ComponentConf> components;
703 
704  private: // Schemas
705  ComponentSchema component_schema;
706 
707  public: // Constructors
708  explicit VehicleConf(const ComponentSchema& s) : component_schema(s) {}
709 
710  public: // Special
711  bool is_from_simulator() const { return from_veh.empty(); }
712  bool is_from_vehicle() const { return !from_veh.empty(); }
713  void clear() {
714  name.clear();
715  from_sim.clear();
716  from_veh.clear();
717  }
718 
719  public: // Confable Overrides
720  CONFABLE_SCHEMA(VehicleConf) {
721  // clang-format off
722  using namespace schema; // NOLINT(build/namespaces)
723  return Struct{
724  {"name", make_schema(&name, "globally unique identifier for vehicle").c_identifier().require()},
725  {"from", Variant{
726  make_schema(&from_sim, "simulator source"),
727  make_schema(&from_veh, "vehicle source").c_identifier(),
728  }.require()},
729  {"components", make_schema(&components, component_schema, "component configuration of vehicle")},
730  };
731  // clang-format on
732  }
733 
734  void from_conf(const Conf& c) override {
735  clear(); // Avoid inconsistent state
737  }
738 
739  void to_json(Json& j) const override {
740  Json from = is_from_simulator() ? Json(from_sim) : Json(from_veh);
741  j = Json{
742  {"name", name},
743  {"from", from},
744  {"components", components},
745  };
746  }
747 };
748 
749 class VehicleSchema : public fable::schema::Base<VehicleSchema> {
750  public: // Constructors
751  using Type = VehicleConf;
752  using MakeFunc = ComponentSchema::MakeFunc;
753  using TypeFactory = ComponentSchema::TypeFactory;
754 
755  explicit VehicleSchema(std::string desc = "") : Base(std::move(desc)) {}
756 
757  public: // Special
758  bool has_factory(const std::string& name) const { return components_.has_factory(name); }
759  void add_plugin(const std::string& name, std::shared_ptr<Plugin> p) {
760  components_.add_plugin(name, p);
761  }
762 
763  public: // Overrides
764  Json json_schema() const override {
765  VehicleConf v{components_};
766  return v.schema().json_schema();
767  }
768 
769  bool validate(const Conf& c, std::optional<SchemaError>& err) const override {
770  VehicleConf v{components_};
771  return v.schema().validate(c, err);
772  }
773 
774  Json serialize(const Type& x) const { return x.to_json(); }
775 
776  Type make(const Conf& c) const { return deserialize(c); }
777 
778  Type deserialize(const Conf& c) const {
779  VehicleConf v{components_};
780  v.from_conf(c);
781  return v;
782  }
783 
784  void from_conf(const Conf&) override {
785  throw std::logic_error("VehicleSchema does not implement from_conf");
786  }
787 
788  void to_json(Json&) const override {
789  throw std::logic_error("VehicleSchema does not implement to_json");
790  }
791 
792  void reset_ptr() override {}
793 
794  private: // State
795  ComponentSchema components_;
796 };
797 
798 // --------------------------------------------------------------------------------------------- //
799 
801  boost::optional<std::string> label{boost::none};
802  Source source{Source::FILESYSTEM};
803  Conf action{};
804  Conf event{};
805  bool conceal{false};
806  bool sticky{false};
807  bool optional{false};
808 
809  public: // Confable Overrides
810  CONFABLE_SCHEMA(TriggerConf) {
811  // clang-format off
812  using namespace schema; // NOLINT(build/namespaces)
813  auto EANDA_SCHEMA = Variant{
814  String{nullptr, "inline format"}.pattern("^[a-zA-Z0-9_/]+(=.*)?$"),
815  Struct{
816  {"name", id_path_prototype().require()}
817  }.additional_properties(true),
818  };
819  return Struct{
820  {"label", make_schema(&label, "description of trigger")},
821  {"source", make_schema(&source, "source from which trigger originates")},
822  {"event", make_schema(&event, EANDA_SCHEMA, "event").require()},
823  {"action", make_schema(&action, EANDA_SCHEMA, "action").require()},
824  {"sticky", make_schema(&sticky, "whether trigger should be sticky")},
825  {"conceal", make_schema(&conceal, "whether trigger should be concealed in history")},
826  {"optional", make_schema(&optional, "whether errors creating event or action should be ignored")},
827  {"at", Ignore("time at which trigger was executed", JsonType::string)},
828  {"since", Ignore("time since which trigger was in queue", JsonType::string)},
829  };
830  // clang-format on
831  }
832 };
833 
834 // --------------------------------------------------------------------------------------------- //
835 
840 struct SimulationConf : public Confable {
844  boost::optional<std::string> name{boost::none};
845 
849  Duration model_step_width = Duration{20'000'000}; // 20 ms
850 
856  int64_t controller_retry_limit{1000};
857 
861  std::chrono::milliseconds controller_retry_sleep{1};
862 
869  bool abort_on_controller_failure{true};
870 
871  public: // Confable Overrides
872  CONFABLE_SCHEMA(SimulationConf) {
873  // clang-format off
874  using namespace schema; // NOLINT(build/namespaces)
875  return Struct{
876  {"namespace", make_schema(&name, id_prototype(), "namespace for simulation events and actions")},
877  {"model_step_width", make_schema(&model_step_width, "default model time step in ns")},
878  {"controller_retry_limit", make_schema(&controller_retry_limit, "times to retry controller processing before aborting")},
879  {"controller_retry_sleep", make_schema(&controller_retry_sleep, "time to sleep before retrying controller process")},
880  {"abort_on_controller_failure", make_schema(&abort_on_controller_failure, "abort simulation on controller failure")},
881  };
882  // clang-format on
883  }
884 };
885 
886 // --------------------------------------------------------------------------------------------- //
887 
888 class StackIncompleteError : public Error {
889  public:
890  explicit StackIncompleteError(std::vector<std::string>&& missing);
891 
892  std::string all_sections_missing(const std::string& sep = ", ") const;
893  const std::vector<std::string>& sections_missing() const { return sections_missing_; }
894 
895  private:
896  std::vector<std::string> sections_missing_;
897 };
898 
899 using ConfReader = std::function<Conf(const std::string&)>;
900 
901 class Stack : public Confable {
902  private: // Constants (1)
903  std::vector<std::string> reserved_ids_;
904  boost::optional<std::string> schema_ref_;
905 
906  public: // Configuration (13)
907  std::string version;
908  EngineConf engine;
909  ServerConf server;
910  std::vector<IncludeConf> include;
911  std::vector<LoggingConf> logging;
912  std::vector<PluginConf> plugins;
913  std::vector<DefaultConf> simulator_defaults;
914  std::vector<SimulatorConf> simulators;
915  std::vector<DefaultConf> controller_defaults;
916  std::vector<ControllerConf> controllers;
917  std::vector<DefaultConf> component_defaults;
918  std::vector<VehicleConf> vehicles;
919  std::vector<TriggerConf> triggers;
920  SimulationConf simulation;
921 
922  private: // Schemas (3) & Prototypes (3)
923  EngineSchema engine_schema;
924  IncludesSchema include_schema;
925  PluginsSchema plugins_schema;
926 
927  SimulatorSchema simulator_prototype;
928  ControllerSchema controller_prototype;
929  VehicleSchema vehicle_prototype;
930 
931  private: // State (3)
932  std::set<std::string> scanned_plugin_paths_;
933  std::map<std::string, std::shared_ptr<Plugin>> all_plugins_;
934  std::vector<Conf> applied_confs_;
935  ConfReader conf_reader_func_;
936 
937  public: // Constructors
938  Stack();
939  Stack(const Stack& other);
940  Stack(Stack&& other);
941  Stack& operator=(Stack other);
942  ~Stack() = default;
943 
944  friend void swap(Stack& left, Stack& right);
945 
946  public: // Special
947  Logger logger() const { return logger::get("cloe"); }
948 
957  void set_conf_reader(ConfReader fn) {
958  assert(fn != nullptr);
959  conf_reader_func_ = fn;
960  }
961 
965  void apply_plugin_conf(const PluginConf& c);
966 
973  void insert_plugin(const PluginConf& c);
974 
981  void insert_plugin(std::shared_ptr<Plugin> p, const PluginConf& c = {});
982 
986  bool has_plugin_with_name(const std::string& key) const;
987 
991  bool has_plugin_with_path(const std::string& path) const;
992 
998  std::shared_ptr<Plugin> get_plugin_with_name(const std::string& key) const;
999 
1005  std::shared_ptr<Plugin> get_plugin_with_path(const std::string& key) const;
1006 
1010  std::shared_ptr<Plugin> get_plugin_or_load(const std::string& key_or_path) const;
1011 
1015  const std::map<std::string, std::shared_ptr<Plugin>>& get_all_plugins() const {
1016  return all_plugins_;
1017  }
1018 
1019  std::vector<DefaultConf> get_simulator_defaults(std::string binding, std::string name) const;
1020  std::vector<DefaultConf> get_controller_defaults(std::string binding, std::string name) const;
1021  std::vector<DefaultConf> get_vehicle_defaults(std::string name) const;
1022  std::vector<DefaultConf> get_component_defaults(std::string binding, std::string name) const;
1023 
1031  void validate_self() const;
1032 
1036  bool is_valid() const;
1037 
1050  void check_consistency() const;
1051 
1055  void check_defaults() const;
1056 
1067  bool is_complete() const;
1068 
1072  void check_completeness() const;
1073 
1078  void initialize() { from_conf(Conf{Json{{"version", CLOE_STACK_VERSION}}}); }
1079 
1083  Json active_config() const;
1084 
1088  Json input_config() const;
1089 
1090  public: // Confable Overrides
1100  bool validate(const Conf& c, std::optional<SchemaError>& err) const override;
1101  void validate_or_throw(const Conf& c) const override;
1102 
1103  void to_json(Json& j) const override;
1104  void from_conf(const Conf& c) override { from_conf(c, 0); }
1105  void reset_schema() override;
1106 
1107  CONFABLE_SCHEMA(Stack) {
1108  // clang-format off
1109  using namespace schema; // NOLINT(build/namespaces)
1110 
1111  return Struct{
1112  {"$schema", make_schema(&schema_ref_, "valid URI to schema describing this cloe stack version")},
1113  {"version", make_schema(&version, "version of stackfile").require().enum_of(
1114  CLOE_STACK_SUPPORTED_VERSIONS
1115  )},
1116  {"engine", engine_schema},
1117  {"include", include_schema},
1118  {"logging", make_schema(&logging, "logging configuration").extend(true)},
1119  {"plugins", plugins_schema},
1120  {"server", make_schema(&server, "server configuration")},
1121  {"defaults", Struct{
1122  {"simulators", make_schema(&simulator_defaults, "simulator default configurations").extend(true)},
1123  {"controllers", make_schema(&controller_defaults, "controller default configurations").extend(true)},
1124  {"components", make_schema(&component_defaults, "component default configurations").extend(true)},
1125  }},
1126  {"vehicles", make_schema(&vehicles, vehicle_prototype, "vehicle configuration").extend(true)},
1127  {"simulators", make_schema(&simulators, simulator_prototype, "simulator configuration").extend(true)},
1128  {"controllers", make_schema(&controllers, controller_prototype, "controller configuration").extend(true)},
1129  {"triggers", make_schema(&triggers, "triggers").extend(true)},
1130  {"simulation", make_schema(&simulation, "simulation configuration")},
1131  };
1132  // clang-format on
1133  }
1134 
1135  protected:
1140  void from_conf(const Conf& c, size_t depth);
1141 };
1142 
1143 } // namespace cloe
Definition: stack.hpp:667
Definition: stack.hpp:573
Definition: error.hpp:35
Definition: stack.hpp:492
Definition: stack.hpp:80
void from_conf(const Conf &c)
Definition: stack.hpp:84
Definition: stack.hpp:535
Definition: stack.hpp:888
Definition: stack.hpp:901
void initialize()
Definition: stack.hpp:1078
void set_conf_reader(ConfReader fn)
Definition: stack.hpp:957
const std::map< std::string, std::shared_ptr< Plugin > > & get_all_plugins() const
Definition: stack.hpp:1015
void from_conf(const Conf &c) override
Definition: stack.hpp:1104
Definition: stack.hpp:749
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: stack.hpp:769
void from_conf(const Conf &) override
Definition: stack.hpp:784
void reset_ptr() override
Definition: stack.hpp:792
Json json_schema() const override
Definition: stack.hpp:764
Definition: conf.hpp:82
bool has(const std::string &key) const
Definition: conf.hpp:166
T get() const
Definition: conf.hpp:298
Definition: confable.hpp:98
virtual void from_conf(const Conf &c)
Definition: confable.cpp:70
virtual void reset_schema()
Definition: confable.cpp:54
virtual void validate_or_throw(const Conf &c) const
Definition: confable.cpp:58
Json to_json() const
Definition: confable.cpp:78
Schema & schema()
Definition: confable.cpp:47
Definition: error.hpp:149
Definition: schema.hpp:173
Json json_schema() const override
Definition: schema.hpp:254
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: schema.hpp:255
Definition: interface.hpp:398
Definition: factory.hpp:417
std::chrono::nanoseconds Duration
Definition: cloe_fwd.hpp:36
#define ENUM_SERIALIZATION(xType, xMap)
Definition: enum.hpp:51
nlohmann::json Json
Definition: fable_fwd.hpp:35
boost::filesystem::path IncludeConf
Definition: stack.hpp:108
WatchdogMode
Definition: stack.hpp:294
@ Kill
Kill the program immediately.
@ Abort
Abort after the state returns.
@ Log
Log infractions but nothing else.
@ Off
Disable the watchdog entirely.
Definition: stack.hpp:634
Definition: stack.hpp:548
Definition: stack.hpp:473
Definition: stack.hpp:583
void from_conf(const Conf &c) override
Definition: stack.hpp:614
Definition: stack.hpp:163
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: stack.hpp:181
Definition: stack.hpp:225
Definition: stack.hpp:197
Definition: stack.hpp:840
Definition: stack.hpp:514
Definition: stack.hpp:800
Definition: stack.hpp:698
void from_conf(const Conf &c) override
Definition: stack.hpp:734
Source
Definition: trigger.hpp:351