$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 <filesystem> // for filesystem::path
27 #include <map> // for map<>
28 #include <memory> // for shared_ptr<>
29 #include <optional> // for optional<>
30 #include <set> // for set<>
31 #include <string> // for string
32 #include <utility> // for move
33 #include <vector> // for vector<>
34 
35 #include <fable/schema/custom.hpp> // for CustomDeserializer
36 #include <fable/schema/factory.hpp> // for Factory
37 
38 #include <cloe/component.hpp> // for ComponentFactory
39 #include <cloe/controller.hpp> // for ControllerFactory
40 #include <cloe/core.hpp> // for Conf, Confable, Json
41 #include <cloe/simulator.hpp> // for SimulatorFactory
42 #include <cloe/trigger.hpp> // for Source
43 #include <cloe/utility/command.hpp> // for Command
44 
45 #include "config.hpp"
46 #include "plugin.hpp" // for Plugin
47 
48 namespace cloe {
49 
56 class PersistentConfable : public Confable {
57  public:
58  const Conf& conf() const { return conf_; }
59 
60  void from_conf(const Conf& c) override {
62  conf_ = c;
63  }
64 
65  protected:
66  Conf conf_;
67 };
68 
69 inline auto id_prototype(std::string desc = "") {
70  return schema::make_prototype<std::string>(std::move(desc)).c_identifier();
71 }
72 
73 inline auto id_path_prototype(std::string desc = "") {
74  return schema::make_prototype<std::string>(std::move(desc))
75  .pattern("^([a-zA-Z_][a-zA-Z0-9_]*/?)+$");
76 }
77 
78 // --------------------------------------------------------------------------------------------- //
79 
84 using IncludeConf = std::filesystem::path;
85 using IncludeSchema = decltype(schema::make_schema(static_cast<IncludeConf*>(nullptr), ""));
87 
88 // --------------------------------------------------------------------------------------------- //
89 
139 struct LoggingConf : public Confable {
140  std::string name;
141  std::optional<std::string> pattern;
142  std::optional<LogLevel> level;
143 
144  public: // Special
145  void apply() const;
146 
147  public: // Confable Overrides
148  CONFABLE_SCHEMA(LoggingConf) {
149  using namespace schema; // NOLINT(build/namespaces)
150  return Schema{
151  {"name", make_schema(&name, "name of the logger to configure").require()},
152  {"pattern", make_schema(&pattern, "pattern of the logger")},
153  {"level", make_schema(&level, "level of the logger")},
154  };
155  }
156 
157  bool validate(const Conf& c, std::optional<SchemaError>& err) const override {
158  const auto& s = this->schema();
159  if (!s.validate(c, err)) {
160  return false;
161  }
162  if (!c.has("pattern") && !c.has("level")) {
163  err.emplace(SchemaError(c, s.json_schema(),
164  "require at least one of 'pattern' or 'level' properties"));
165  return false;
166  }
167  return true;
168  }
169 };
170 
171 // --------------------------------------------------------------------------------------------- //
172 
173 struct ServerConf : public Confable {
174  bool listen{true};
175  std::string listen_address{"127.0.0.1"};
176  uint16_t listen_port{8080};
177  uint16_t listen_threads{10};
178  std::string api_prefix{"/api"};
179  std::string static_prefix{""};
180 
181  public: // Confable Overrides
182  CONFABLE_SCHEMA(ServerConf) {
183  using namespace schema; // NOLINT(build/namespaces)
184  return Schema{
185  {"listen", make_schema(&listen, "whether web server is enabled")},
186  {"listen_address", make_schema(&listen_address, "address web server should listen at")},
187  {"listen_port", make_schema(&listen_port, "port web server should listen at")},
188  {"listen_threads", make_schema(&listen_threads, "threads web server should use")},
189  {"static_prefix", make_schema(&static_prefix, "endpoint prefix for static resources")},
190  {"api_prefix", make_schema(&api_prefix, "endpoint prefix for API resources")},
191  };
192  }
193 };
194 
195 // --------------------------------------------------------------------------------------------- //
196 
203  std::filesystem::path plugin_path{};
204 
206  std::optional<std::string> plugin_name{};
207 
209  std::optional<std::string> plugin_prefix{};
210 
212  std::optional<bool> ignore_missing{};
213 
219  std::optional<bool> ignore_failure{};
220 
227  std::optional<bool> allow_clobber{};
228 
229  public: // Constructors
230  PluginConf() = default;
231  explicit PluginConf(const std::string& p) : plugin_path(p) {}
232 
233  public: // Special
241  std::string canonical() const;
242 
243  public: // Confable Overrides
244  CONFABLE_SCHEMA(PluginConf) {
245  // clang-format off
246  using namespace schema; // NOLINT(build/namespaces)
247  auto proto = String(nullptr, "").c_identifier();
248  return Struct{
249  {"path", make_schema(&plugin_path, "absolute or relative path to plugin").require().not_empty().normalize(true)},
250  {"name", make_schema(&plugin_name, proto, "alternative name plugin is available by")},
251  {"prefix", make_schema(&plugin_prefix, proto, "prefix the plugin name with this")},
252  {"ignore_missing", make_schema(&ignore_missing, "ignore not-exist errors")},
253  {"ignore_failure", make_schema(&ignore_failure, "ignore plugin loading errors")},
254  {"allow_clobber", make_schema(&allow_clobber, "replace same-named plugins")},
255  };
256  // clang-format on
257  }
258 };
259 
260 using PluginsSchema = schema_type<std::vector<PluginConf>>::type;
261 
262 // --------------------------------------------------------------------------------------------- //
263 
270 enum class WatchdogMode {
271  Off,
272  Log,
273  Abort,
274  Kill,
275 };
276 
277 // clang-format off
278 ENUM_SERIALIZATION(WatchdogMode, ({
279  {WatchdogMode::Off, "off"},
280  {WatchdogMode::Log, "log"},
281  {WatchdogMode::Abort, "abort"},
282  {WatchdogMode::Kill, "kill"},
283 }))
284 // clang-format on
285 
286 struct EngineConf : public Confable {
287  // Parsing:
288  std::vector<std::string> ignore_sections{};
289 
290  // Security:
291  bool security_enable_hooks{true};
292  bool security_enable_commands{false};
293  bool security_enable_includes{true};
294  size_t security_max_include_depth{64};
295 
296  // Plugins:
297  std::vector<std::string> plugin_path{};
298  bool plugins_ignore_missing{false};
299  bool plugins_ignore_failure{false};
300  bool plugins_allow_clobber{true};
301 
302  // Hooks:
303  std::vector<Command> hooks_pre_connect{};
304  std::vector<Command> hooks_post_disconnect{};
305 
306  // Triggers:
307  bool triggers_ignore_source{false};
308 
309  // Output:
310  std::optional<std::filesystem::path> registry_path{CLOE_DATA_HOME "/registry"};
311  std::optional<std::filesystem::path> output_path{"${CLOE_SIMULATION_UUID}"};
312  std::optional<std::filesystem::path> output_file_config{"config.json"};
313  std::optional<std::filesystem::path> output_file_result{"result.json"};
314  std::optional<std::filesystem::path> output_file_triggers{"triggers.json"};
315  std::optional<std::filesystem::path> output_file_signals{"signals.json"};
316  std::optional<std::filesystem::path> output_file_signals_autocompletion;
317  std::optional<std::filesystem::path> output_file_data_stream;
318  bool output_clobber_files{true};
319 
325  std::chrono::milliseconds polling_interval{100};
326 
332  WatchdogMode watchdog_mode{WatchdogMode::Off};
333 
343  std::chrono::milliseconds watchdog_default_timeout{90'000};
344 
372  std::map<std::string, std::optional<std::chrono::milliseconds>> watchdog_state_timeouts{
373  {"CONNECT", std::chrono::milliseconds{300'000}},
374  {"ABORT", std::chrono::milliseconds{90'000}},
375  {"STOP", std::chrono::milliseconds{300'000}},
376  {"DISCONNECT", std::chrono::milliseconds{600'000}},
377  };
378 
385  bool keep_alive{false};
386 
387  public: // Confable Overrides
388  CONFABLE_SCHEMA(EngineConf) {
389  // clang-format off
390  using namespace schema; // NOLINT(build/namespaces)
391  auto dir_proto = []() { return make_prototype<std::filesystem::path>().not_file(); };
392  auto file_proto = []() { return make_prototype<std::filesystem::path>().not_dir().resolve(false); };
393  return Struct{
394  {"ignore", make_schema(&ignore_sections, "JSON pointers to sections that should be ignored").extend(true)},
395  {"security", Struct{
396  {"enable_hooks_section", make_schema(&security_enable_hooks, "whether to enable engine hooks")},
397  {"enable_command_action", make_schema(&security_enable_commands, "whether to enable the command action")},
398  {"enable_include_section", make_schema(&security_enable_includes, "whether to allow config files to include other files")},
399  {"max_include_depth", make_schema(&security_max_include_depth, "how many recursive includes are allowed")},
400  }},
401  {"hooks", Struct{
402  {"pre_connect", make_schema(&hooks_pre_connect, "pre-connect hooks to execute").extend(true)},
403  {"post_disconnect", make_schema(&hooks_post_disconnect, "post-disconnect hooks to execute").extend(true)},
404  }},
405  {"plugin_path", make_schema(&plugin_path, "list of directories to scan for plugins").extend(false)},
406  {"plugins", Struct{
407  {"ignore_missing", make_schema(&plugins_ignore_missing, "ignore not-exist errors")},
408  {"ignore_failure", make_schema(&plugins_ignore_failure, "ignore plugin loading errors")},
409  {"allow_clobber", make_schema(&plugins_allow_clobber, "replace same-named plugins")},
410  }},
411  {"registry_path", make_schema(&registry_path, dir_proto(), "cloe registry directory")},
412  {"output", Struct{
413  {"path", make_schema(&output_path, dir_proto().resolve(false), "directory to dump output files in, relative to registry path")},
414  {"clobber", make_schema(&output_clobber_files, "whether to clobber existing files or not")},
415  {"files", Struct{
416  {"config", make_schema(&output_file_config, file_proto(), "file to store config in")},
417  {"result", make_schema(&output_file_result, file_proto(), "file to store simulation result in")},
418  {"triggers", make_schema(&output_file_triggers, file_proto(), "file to store triggers in")},
419  {"signals", make_schema(&output_file_signals, file_proto(), "file to store signals in")},
420  {"signals_autocompletion", make_schema(&output_file_signals_autocompletion, file_proto(), "file to store signal autocompletion in")},
421  {"api_recording", make_schema(&output_file_data_stream, file_proto(), "file to store api data stream")},
422  }},
423  }},
424  {"triggers", Struct{
425  {"ignore_source", make_schema(&triggers_ignore_source, "ignore trigger source when reading in triggers")},
426  }},
427  {"polling_interval", make_schema(&polling_interval, "milliseconds to sleep when polling for next state")},
428  {"watchdog", Struct{
429  {"mode", make_schema(&watchdog_mode, "modus operandi of watchdog [one of: off, log, abort, kill]")},
430  {"default_timeout", make_schema(&watchdog_default_timeout, "default timeout if not overridden, 0 for no timeout")},
431  {"state_timeouts", make_schema(&watchdog_state_timeouts, "timeout specific to a given state, 0 for no timeout").unique_properties(false)},
432  }},
433  {"keep_alive", make_schema(&keep_alive, "keep simulation alive after termination")},
434  };
435  // clang-format on
436  }
437 };
438 
439 using EngineSchema = schema_type<EngineConf>::type;
440 
441 // --------------------------------------------------------------------------------------------- //
442 
453 struct DefaultConf : public Confable {
454  std::optional<std::string> name;
455  std::optional<std::string> binding;
456  Conf args;
457 
458  public: // Confable Overrides
459  CONFABLE_SCHEMA(DefaultConf) {
460  using namespace schema; // NOLINT(build/namespaces)
461  return Struct{
462  {"binding", make_schema(&binding, "name of binding")},
463  {"name", make_schema(&name, id_prototype(), "globally unique identifier for component")},
464  {"args", make_schema(&args, "defaults to set for binding/name combination").require()},
465  };
466  }
467 };
468 
469 // --------------------------------------------------------------------------------------------- //
470 
471 template <typename C, typename F>
473  public:
474  FactoryPlugin() {
475  this->set_factory_key("binding");
476  this->set_args_subset(false);
477  }
478  virtual ~FactoryPlugin() = default;
479 
480  void add_plugin(const std::string& name, std::shared_ptr<Plugin> p) {
481  this->set_factory(name, p->make<F>()->schema(), [p, name](const Conf& c) {
482  C tmp{name, p->make<F>()};
483  tmp.from_conf(c);
484  return tmp;
485  });
486  }
487 };
488 
489 // --------------------------------------------------------------------------------------------- //
490 
494 struct SimulatorConf : public Confable {
495  const std::string binding;
496  std::optional<std::string> name;
497  std::shared_ptr<SimulatorFactory> factory;
498  Conf args;
499 
500  public: // Constructors
501  SimulatorConf(const std::string& b, std::shared_ptr<SimulatorFactory> f)
502  : binding(b), factory(std::move(f)) {}
503 
504  public: // Confable Overrides
505  CONFABLE_SCHEMA(SimulatorConf) {
506  using namespace schema; // NOLINT(build/namespaces)
507  return Struct{
508  {"binding", make_const_schema(binding, "name of simulator binding").require()},
509  {"name", make_schema(&name, id_prototype(), "identifier override for binding")},
510  {"args", make_schema(&args, factory->schema(), "factory-specific arguments")},
511  };
512  }
513 };
514 
515 class SimulatorSchema : public FactoryPlugin<SimulatorConf, SimulatorFactory> {
516  public:
517  SimulatorSchema();
518  virtual ~SimulatorSchema() = default;
519 };
520 
521 // --------------------------------------------------------------------------------------------- //
522 
526 struct ControllerConf : public Confable {
527  const std::string binding;
528  std::optional<std::string> name;
529  std::string vehicle;
530  std::shared_ptr<ControllerFactory> factory;
531  Conf args;
532 
533  public: // Constructors
534  ControllerConf(const std::string& b, std::shared_ptr<ControllerFactory> f)
535  : binding(b), factory(std::move(f)) {}
536 
537  public: // Confable Overrides
538  CONFABLE_SCHEMA(ControllerConf) {
539  // clang-format off
540  using namespace schema; // NOLINT(build/namespaces)
541  return Struct{
542  {"binding", make_const_schema(binding, "name of controller binding").require()},
543  {"name", make_schema(&name, id_prototype(), "identifier override for binding")},
544  {"vehicle", make_schema(&vehicle, "vehicle controller is assigned to").c_identifier().require()},
545  {"args", make_schema(&args, factory->schema(), "factory-specific arguments")},
546  };
547  // clang-format on
548  }
549 };
550 
551 class ControllerSchema : public FactoryPlugin<ControllerConf, ControllerFactory> {
552  public:
554  virtual ~ControllerSchema() = default;
555 };
556 
557 // --------------------------------------------------------------------------------------------- //
558 
559 struct FromSimulator : public Confable {
560  std::string simulator;
561  std::string index_str;
562  size_t index_num;
563 
564  public: // Special
565  bool is_by_name() const { return !index_str.empty(); }
566  bool is_by_index() const { return index_str.empty(); }
567  void clear() {
568  simulator.clear();
569  index_str.clear();
570  index_num = 0;
571  }
572 
573  public: // Confable Overrides
574  CONFABLE_SCHEMA(FromSimulator) {
575  // clang-format off
576  using namespace schema; // NOLINT(build/namespaces)
577  return Variant{
578  Struct{
579  {"simulator", make_schema(&simulator, "simulator").not_empty().require()},
580  {"index", make_schema(&index_num, "index of vehicle in simulator").require()},
581  },
582  Struct{
583  {"simulator", make_schema(&simulator, "simulator").not_empty().require()},
584  {"name", make_schema(&index_str, "name of vehicle in simulator").not_empty().require()},
585  },
586  };
587  // clang-format on
588  }
589 
590  void from_conf(const Conf& c) override {
591  clear(); // Avoid inconsistent state
593  }
594 
595  void to_json(Json& j) const override {
596  if (is_by_index()) {
597  j = Json{
598  {"simulator", simulator},
599  {"index", index_num},
600  };
601  } else {
602  j = Json{
603  {"simulator", simulator},
604  {"name", index_str},
605  };
606  }
607  }
608 };
609 
610 struct ComponentConf : public Confable {
611  const std::string binding;
612  std::optional<std::string> name;
613  std::vector<std::string> from;
614  std::shared_ptr<ComponentFactory> factory;
615  Conf args;
616 
617  public: // Constructors
618  ComponentConf(const std::string& b, std::shared_ptr<ComponentFactory> f)
619  : binding(b), factory(std::move(f)) {}
620 
621  public: // Confable Overrides
622  CONFABLE_SCHEMA(ComponentConf) {
623  // clang-format off
624  using namespace schema; // NOLINT(build/namespaces)
625  return Struct{
626  {"binding", make_const_schema(binding, "name of binding").require()},
627  {"name", make_schema(&name, id_prototype(), "globally unique identifier for component")},
628  {"from", Variant{
629  make_schema(&from, "component inputs for binding"),
630  CustomDeserializer(
631  make_prototype<std::string>("component input for binding"),
632  [this](CustomDeserializer*, const Conf& c) {
633  this->from.push_back(c.get<std::string>());
634  }
635  ),
636  }},
637  {"args", make_schema(&args, factory->schema(), "factory-specific args")},
638  };
639  // clang-format on
640  }
641 };
642 
643 class ComponentSchema : public FactoryPlugin<ComponentConf, ComponentFactory> {
644  public:
645  ComponentSchema();
646  virtual ~ComponentSchema() = default;
647 };
648 
649 // TODO(ben): Add AliasConf as alternative to ComponentConf.
650 
672 struct VehicleConf : public Confable {
673  std::string name;
674  FromSimulator from_sim;
675  std::string from_veh;
676  std::map<std::string, ComponentConf> components;
677 
678  private: // Schemas
679  ComponentSchema component_schema;
680 
681  public: // Constructors
682  explicit VehicleConf(const ComponentSchema& s) : component_schema(s) {}
683 
684  public: // Special
685  bool is_from_simulator() const { return from_veh.empty(); }
686  bool is_from_vehicle() const { return !from_veh.empty(); }
687  void clear() {
688  name.clear();
689  from_sim.clear();
690  from_veh.clear();
691  }
692 
693  public: // Confable Overrides
694  CONFABLE_SCHEMA(VehicleConf) {
695  // clang-format off
696  using namespace schema; // NOLINT(build/namespaces)
697  return Struct{
698  {"name", make_schema(&name, "globally unique identifier for vehicle").c_identifier().require()},
699  {"from", Variant{
700  make_schema(&from_sim, "simulator source"),
701  make_schema(&from_veh, "vehicle source").c_identifier(),
702  }.require()},
703  {"components", make_schema(&components, component_schema, "component configuration of vehicle")},
704  };
705  // clang-format on
706  }
707 
708  void from_conf(const Conf& c) override {
709  clear(); // Avoid inconsistent state
711  }
712 
713  void to_json(Json& j) const override {
714  Json from = is_from_simulator() ? Json(from_sim) : Json(from_veh);
715  j = Json{
716  {"name", name},
717  {"from", from},
718  {"components", components},
719  };
720  }
721 };
722 
723 class VehicleSchema : public fable::schema::Base<VehicleSchema> {
724  public: // Constructors
725  using Type = VehicleConf;
726  using MakeFunc = ComponentSchema::MakeFunc;
727  using TypeFactory = ComponentSchema::TypeFactory;
728 
729  explicit VehicleSchema(std::string desc = "") : Base(std::move(desc)) {}
730 
731  public: // Special
732  bool has_factory(const std::string& name) const { return components_.has_factory(name); }
733  void add_plugin(const std::string& name, std::shared_ptr<Plugin> p) {
734  components_.add_plugin(name, p);
735  }
736 
737  public: // Overrides
738  Json json_schema() const override {
739  VehicleConf v{components_};
740  return v.schema().json_schema();
741  }
742 
743  bool validate(const Conf& c, std::optional<SchemaError>& err) const override {
744  VehicleConf v{components_};
745  return v.schema().validate(c, err);
746  }
747 
748  Json serialize(const Type& x) const { return x.to_json(); }
749 
750  Type make(const Conf& c) const { return deserialize(c); }
751 
752  Type deserialize(const Conf& c) const {
753  VehicleConf v{components_};
754  v.from_conf(c);
755  return v;
756  }
757 
758  void from_conf(const Conf&) override {
759  throw std::logic_error("VehicleSchema does not implement from_conf");
760  }
761 
762  void to_json(Json&) const override {
763  throw std::logic_error("VehicleSchema does not implement to_json");
764  }
765 
766  void reset_ptr() override {}
767 
768  private: // State
769  ComponentSchema components_;
770 };
771 
772 // --------------------------------------------------------------------------------------------- //
773 
775  std::optional<std::string> label{};
776  Source source{Source::FILESYSTEM};
777  Conf action{};
778  Conf event{};
779  bool conceal{false};
780  bool sticky{false};
781  bool optional{false};
782 
783  public: // Confable Overrides
784  CONFABLE_SCHEMA(TriggerConf) {
785  // clang-format off
786  using namespace schema; // NOLINT(build/namespaces)
787  auto EANDA_SCHEMA = Variant{
788  String{nullptr, "inline format"}.pattern("^[a-zA-Z0-9_/]+(=.*)?$"),
789  Struct{
790  {"name", id_path_prototype().require()}
791  }.additional_properties(true),
792  };
793  return Struct{
794  {"label", make_schema(&label, "description of trigger")},
795  {"source", make_schema(&source, "source from which trigger originates")},
796  {"event", make_schema(&event, EANDA_SCHEMA, "event").require()},
797  {"action", make_schema(&action, EANDA_SCHEMA, "action").require()},
798  {"sticky", make_schema(&sticky, "whether trigger should be sticky")},
799  {"conceal", make_schema(&conceal, "whether trigger should be concealed in history")},
800  {"optional", make_schema(&optional, "whether errors creating event or action should be ignored")},
801  {"at", Ignore("time at which trigger was executed", JsonType::string)},
802  {"since", Ignore("time since which trigger was in queue", JsonType::string)},
803  };
804  // clang-format on
805  }
806 };
807 
808 // --------------------------------------------------------------------------------------------- //
809 
814 struct SimulationConf : public Confable {
818  std::optional<std::string> name{};
819 
823  Duration model_step_width = Duration{20'000'000}; // 20 ms
824 
830  int64_t controller_retry_limit{1000};
831 
835  std::chrono::milliseconds controller_retry_sleep{1};
836 
843  bool abort_on_controller_failure{true};
844 
845  public: // Confable Overrides
846  CONFABLE_SCHEMA(SimulationConf) {
847  // clang-format off
848  using namespace schema; // NOLINT(build/namespaces)
849  return Struct{
850  {"namespace", make_schema(&name, id_prototype(), "namespace for simulation events and actions")},
851  {"model_step_width", make_schema(&model_step_width, "default model time step in ns")},
852  {"controller_retry_limit", make_schema(&controller_retry_limit, "times to retry controller processing before aborting")},
853  {"controller_retry_sleep", make_schema(&controller_retry_sleep, "time to sleep before retrying controller process")},
854  {"abort_on_controller_failure", make_schema(&abort_on_controller_failure, "abort simulation on controller failure")},
855  };
856  // clang-format on
857  }
858 };
859 
860 // --------------------------------------------------------------------------------------------- //
861 
862 class StackIncompleteError : public Error {
863  public:
864  explicit StackIncompleteError(std::vector<std::string>&& missing);
865 
866  [[nodiscard]] std::string all_sections_missing(const std::string& sep = ", ") const;
867  [[nodiscard]] const std::vector<std::string>& sections_missing() const { return sections_missing_; }
868 
869  private:
870  std::vector<std::string> sections_missing_;
871 };
872 
873 using ConfReader = std::function<Conf(const std::string&)>;
874 
879 class Stack : public Confable {
880  private: // Constants (1)
881  std::vector<std::string> reserved_ids_;
882  std::optional<std::string> schema_ref_;
883 
884  public: // Configuration (13)
885  std::string version;
886  EngineConf engine;
887  ServerConf server;
888  std::vector<IncludeConf> include;
889  std::vector<LoggingConf> logging;
890  std::vector<PluginConf> plugins;
891  std::vector<DefaultConf> simulator_defaults;
892  std::vector<SimulatorConf> simulators;
893  std::vector<DefaultConf> controller_defaults;
894  std::vector<ControllerConf> controllers;
895  std::vector<DefaultConf> component_defaults;
896  std::vector<VehicleConf> vehicles;
897  std::vector<TriggerConf> triggers;
898  SimulationConf simulation;
899 
900  private: // Schemas (3) & Prototypes (3)
901  EngineSchema engine_schema;
902  IncludesSchema include_schema;
903  PluginsSchema plugins_schema;
904 
905  SimulatorSchema simulator_prototype;
906  ControllerSchema controller_prototype;
907  VehicleSchema vehicle_prototype;
908 
909  private: // State (3)
910  std::set<std::string> scanned_plugin_paths_;
911  std::map<std::string, std::shared_ptr<Plugin>> all_plugins_;
912  std::vector<Conf> applied_confs_;
913  ConfReader conf_reader_func_;
914 
915  public: // Constructors
916  Stack();
917  Stack(const Stack& other);
918  Stack(Stack&& other) noexcept;
919  Stack& operator=(const Stack& other);
920  Stack& operator=(Stack&& other) noexcept;
921  ~Stack() override = default;
922 
923  friend void swap(Stack& left, Stack& right);
924 
925  public: // Special
926  Logger logger() const { return logger::get("cloe"); }
927 
936  void set_conf_reader(ConfReader fn) {
937  assert(fn != nullptr);
938  conf_reader_func_ = std::move(fn);
939  }
940 
944  void merge_stackfile(const std::string& filepath);
945 
949  void apply_plugin_conf(const PluginConf& c);
950 
957  void insert_plugin(const PluginConf& c);
958 
965  void insert_plugin(std::shared_ptr<Plugin> p, const PluginConf& c = {});
966 
970  bool has_plugin_with_name(const std::string& key) const;
971 
975  bool has_plugin_with_path(const std::string& path) const;
976 
982  std::shared_ptr<Plugin> get_plugin_with_name(const std::string& key) const;
983 
989  std::shared_ptr<Plugin> get_plugin_with_path(const std::string& key) const;
990 
994  std::shared_ptr<Plugin> get_plugin_or_load(const std::string& key_or_path) const;
995 
999  const std::map<std::string, std::shared_ptr<Plugin>>& get_all_plugins() const {
1000  return all_plugins_;
1001  }
1002 
1003  std::vector<DefaultConf> get_simulator_defaults(std::string binding, std::string name) const;
1004  std::vector<DefaultConf> get_controller_defaults(std::string binding, std::string name) const;
1005  std::vector<DefaultConf> get_vehicle_defaults(std::string name) const;
1006  std::vector<DefaultConf> get_component_defaults(std::string binding, std::string name) const;
1007 
1015  void validate_self() const;
1016 
1020  bool is_valid() const;
1021 
1034  void check_consistency() const;
1035 
1039  void check_defaults() const;
1040 
1051  bool is_complete() const;
1052 
1056  void check_completeness() const;
1057 
1062  void initialize() { from_conf(Conf{Json{{"version", CLOE_STACK_VERSION}}}); }
1063 
1067  Json active_config() const;
1068 
1072  Json input_config() const;
1073 
1074  public: // Confable Overrides
1084  bool validate(const Conf& c, std::optional<SchemaError>& err) const override;
1085  void validate_or_throw(const Conf& c) const override;
1086 
1087  void to_json(Json& j) const override;
1088  void from_conf(const Conf& c) override { from_conf(c, 0); }
1089  void reset_schema() override;
1090 
1091  CONFABLE_SCHEMA(Stack) {
1092  // clang-format off
1093  using namespace schema; // NOLINT(build/namespaces)
1094 
1095  return Struct{
1096  {"$schema", make_schema(&schema_ref_, "valid URI to schema describing this cloe stack version")},
1097  {"version", make_schema(&version, "version of stackfile").require().enum_of(
1098  CLOE_STACK_SUPPORTED_VERSIONS
1099  )},
1100  {"engine", engine_schema},
1101  {"include", include_schema},
1102  {"logging", make_schema(&logging, "logging configuration").extend(true)},
1103  {"plugins", plugins_schema},
1104  {"server", make_schema(&server, "server configuration")},
1105  {"defaults", Struct{
1106  {"simulators", make_schema(&simulator_defaults, "simulator default configurations").extend(true)},
1107  {"controllers", make_schema(&controller_defaults, "controller default configurations").extend(true)},
1108  {"components", make_schema(&component_defaults, "component default configurations").extend(true)},
1109  }},
1110  {"vehicles", make_schema(&vehicles, vehicle_prototype, "vehicle configuration").extend(true)},
1111  {"simulators", make_schema(&simulators, simulator_prototype, "simulator configuration").extend(true)},
1112  {"controllers", make_schema(&controllers, controller_prototype, "controller configuration").extend(true)},
1113  {"triggers", make_schema(&triggers, "triggers").extend(true)},
1114  {"simulation", make_schema(&simulation, "simulation configuration")},
1115  };
1116  // clang-format on
1117  }
1118 
1119  protected:
1124  void from_conf(const Conf& c, size_t depth);
1125 };
1126 
1127 } // namespace cloe
Definition: stack.hpp:643
Definition: stack.hpp:551
Definition: error.hpp:35
Definition: stack.hpp:472
Definition: stack.hpp:56
void from_conf(const Conf &c) override
Definition: stack.hpp:60
Definition: stack.hpp:515
Definition: stack.hpp:862
Definition: stack.hpp:879
void initialize()
Definition: stack.hpp:1062
void set_conf_reader(ConfReader fn)
Definition: stack.hpp:936
const std::map< std::string, std::shared_ptr< Plugin > > & get_all_plugins() const
Definition: stack.hpp:999
void from_conf(const Conf &c) override
Definition: stack.hpp:1088
Definition: stack.hpp:723
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: stack.hpp:743
void from_conf(const Conf &) override
Definition: stack.hpp:758
void reset_ptr() override
Definition: stack.hpp:766
Json json_schema() const override
Definition: stack.hpp:738
Definition: conf.hpp:76
bool has(const std::string &key) const
Definition: conf.hpp:132
T get() const
Definition: conf.hpp:166
Definition: confable.hpp:43
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:130
Definition: schema.hpp:175
Json json_schema() const override
Definition: schema.hpp:256
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: schema.hpp:257
Definition: interface.hpp:398
Definition: factory.hpp:406
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
std::filesystem::path IncludeConf
Definition: stack.hpp:84
WatchdogMode
Definition: stack.hpp:270
@ 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:610
Definition: stack.hpp:526
Definition: stack.hpp:453
Definition: stack.hpp:559
void from_conf(const Conf &c) override
Definition: stack.hpp:590
Definition: stack.hpp:139
bool validate(const Conf &c, std::optional< SchemaError > &err) const override
Definition: stack.hpp:157
Definition: stack.hpp:201
Definition: stack.hpp:173
Definition: stack.hpp:814
Definition: stack.hpp:494
Definition: stack.hpp:774
Definition: stack.hpp:672
void from_conf(const Conf &c) override
Definition: stack.hpp:708
Source
Definition: trigger.hpp:351