33 #include <boost/filesystem/path.hpp>
34 #include <boost/optional.hpp>
49 #ifndef CLOE_STACK_VERSION
50 #define CLOE_STACK_VERSION "4.1"
53 #ifndef CLOE_STACK_SUPPORTED_VERSIONS
54 #define CLOE_STACK_SUPPORTED_VERSIONS \
58 #ifndef CLOE_XDG_SUFFIX
59 #define CLOE_XDG_SUFFIX "cloe"
62 #ifndef CLOE_CONFIG_HOME
63 #define CLOE_CONFIG_HOME "${XDG_CONFIG_HOME-${HOME}/.config}/" CLOE_XDG_SUFFIX
66 #ifndef CLOE_DATA_HOME
67 #define CLOE_DATA_HOME "${XDG_DATA_HOME-${HOME}/.local/share}/" CLOE_XDG_SUFFIX
70 #define CLOE_SIMULATION_UUID_VAR "CLOE_SIMULATION_UUID"
82 const Conf& conf()
const {
return conf_; }
93 inline auto id_prototype(std::string desc =
"") {
94 return schema::make_prototype<std::string>(std::move(desc)).c_identifier();
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_]*/?)+$");
109 using IncludeSchema = decltype(schema::make_schema(
static_cast<IncludeConf*
>(
nullptr),
""));
165 boost::optional<std::string> pattern;
166 boost::optional<LogLevel> level;
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")},
181 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override {
182 const auto& s = this->
schema();
183 if (!s.validate(c, err)) {
186 if (!c.
has(
"pattern") && !c.
has(
"level")) {
188 "require at least one of 'pattern' or 'level' properties"));
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{
""};
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")},
227 boost::filesystem::path plugin_path{};
230 boost::optional<std::string> plugin_name{};
233 boost::optional<std::string> plugin_prefix{};
236 boost::optional<bool> ignore_missing{};
243 boost::optional<bool> ignore_failure{};
251 boost::optional<bool> allow_clobber{};
255 explicit PluginConf(
const std::string& p) : plugin_path(p) {}
265 std::string canonical()
const;
268 CONFABLE_SCHEMA(PluginConf) {
271 auto proto = String(
nullptr,
"").c_identifier();
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")},
284 using PluginsSchema = schema_type<std::vector<PluginConf>>::type;
303 {WatchdogMode::Off,
"off"},
304 {WatchdogMode::Log,
"log"},
305 {WatchdogMode::Abort,
"abort"},
306 {WatchdogMode::Kill,
"kill"},
310 struct EngineConf : public Confable {
312 std::vector<std::string> ignore_sections{};
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};
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};
327 std::vector<Command> hooks_pre_connect{};
328 std::vector<Command> hooks_post_disconnect{};
331 bool triggers_ignore_source{
false};
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};
347 std::chrono::milliseconds polling_interval{100};
365 std::chrono::milliseconds watchdog_default_timeout{90'000};
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}},
407 bool keep_alive{
false};
410 CONFABLE_SCHEMA(EngineConf) {
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); };
416 {
"ignore", make_schema(&ignore_sections,
"JSON pointers to sections that should be ignored").extend(
true)},
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")},
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)},
427 {
"plugin_path", make_schema(&plugin_path,
"list of directories to scan for plugins").extend(
false)},
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")},
433 {
"registry_path", make_schema(®istry_path, dir_proto(),
"cloe registry directory")},
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")},
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")},
445 {
"ignore_source", make_schema(&triggers_ignore_source,
"ignore trigger source when reading in triggers")},
447 {
"polling_interval", make_schema(&polling_interval,
"milliseconds to sleep when polling for next state")},
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)},
453 {
"keep_alive", make_schema(&keep_alive,
"keep simulation alive after termination")},
459 using EngineSchema = schema_type<EngineConf>::type;
474 boost::optional<std::string> name;
475 boost::optional<std::string> binding;
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()},
491 template <
typename C,
typename F>
495 this->set_factory_key(
"binding");
496 this->set_args_subset(
false);
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>()};
515 const std::string binding;
516 boost::optional<std::string> name;
517 std::shared_ptr<SimulatorFactory> factory;
521 SimulatorConf(
const std::string& b, std::shared_ptr<SimulatorFactory> f)
522 : binding(b), factory(std::move(f)) {}
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")},
540 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override;
549 const std::string binding;
550 boost::optional<std::string> name;
552 std::shared_ptr<ControllerFactory> factory;
556 ControllerConf(
const std::string& b, std::shared_ptr<ControllerFactory> f)
557 : binding(b), factory(std::move(f)) {}
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")},
578 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override;
584 std::string simulator;
585 std::string index_str;
589 bool is_by_name()
const {
return !index_str.empty(); }
590 bool is_by_index()
const {
return index_str.empty(); }
603 {
"simulator", make_schema(&simulator,
"simulator").not_empty().require()},
604 {
"index", make_schema(&index_num,
"index of vehicle in simulator").require()},
607 {
"simulator", make_schema(&simulator,
"simulator").not_empty().require()},
608 {
"name", make_schema(&index_str,
"name of vehicle in simulator").not_empty().require()},
619 void to_json(Json& j)
const override {
622 {
"simulator", simulator},
623 {
"index", index_num},
627 {
"simulator", simulator},
635 const std::string binding;
636 boost::optional<std::string> name;
637 std::vector<std::string> from;
638 std::shared_ptr<ComponentFactory> factory;
642 ComponentConf(
const std::string& b, std::shared_ptr<ComponentFactory> f)
643 : binding(b), factory(std::move(f)) {}
650 {
"binding", make_const_schema(binding,
"name of binding").require()},
651 {
"name", make_schema(&name, id_prototype(),
"globally unique identifier for component")},
653 make_schema(&from,
"component inputs for binding"),
655 make_prototype<std::string>(
"component input for binding"),
656 [
this](CustomDeserializer*,
const Conf& c) {
657 this->from.push_back(c.
get<std::string>());
661 {
"args", make_schema(&args, factory->schema(),
"factory-specific args")},
672 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override;
701 std::string from_veh;
702 std::map<std::string, ComponentConf> components;
711 bool is_from_simulator()
const {
return from_veh.empty(); }
712 bool is_from_vehicle()
const {
return !from_veh.empty(); }
724 {
"name", make_schema(&name,
"globally unique identifier for vehicle").c_identifier().require()},
726 make_schema(&from_sim,
"simulator source"),
727 make_schema(&from_veh,
"vehicle source").c_identifier(),
729 {
"components", make_schema(&components, component_schema,
"component configuration of vehicle")},
739 void to_json(Json& j)
const override {
740 Json from = is_from_simulator() ? Json(from_sim) : Json(from_veh);
744 {
"components", components},
752 using MakeFunc = ComponentSchema::MakeFunc;
753 using TypeFactory = ComponentSchema::TypeFactory;
755 explicit VehicleSchema(std::string desc =
"") : Base(std::move(desc)) {}
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);
769 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override {
774 Json serialize(
const Type& x)
const {
return x.to_json(); }
776 Type make(
const Conf& c)
const {
return deserialize(c); }
778 Type deserialize(
const Conf& c)
const {
779 VehicleConf v{components_};
785 throw std::logic_error(
"VehicleSchema does not implement from_conf");
788 void to_json(Json&)
const override {
789 throw std::logic_error(
"VehicleSchema does not implement to_json");
801 boost::optional<std::string> label{boost::none};
802 Source source{Source::FILESYSTEM};
807 bool optional{
false};
813 auto EANDA_SCHEMA = Variant{
814 String{
nullptr,
"inline format"}.pattern(
"^[a-zA-Z0-9_/]+(=.*)?$"),
816 {
"name", id_path_prototype().require()}
817 }.additional_properties(
true),
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)},
844 boost::optional<std::string> name{boost::none};
856 int64_t controller_retry_limit{1000};
861 std::chrono::milliseconds controller_retry_sleep{1};
869 bool abort_on_controller_failure{
true};
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")},
892 std::string all_sections_missing(
const std::string& sep =
", ")
const;
893 const std::vector<std::string>& sections_missing()
const {
return sections_missing_; }
896 std::vector<std::string> sections_missing_;
899 using ConfReader = std::function<
Conf(
const std::string&)>;
903 std::vector<std::string> reserved_ids_;
904 boost::optional<std::string> schema_ref_;
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;
923 EngineSchema engine_schema;
925 PluginsSchema plugins_schema;
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_;
947 Logger logger()
const {
return logger::get(
"cloe"); }
958 assert(fn !=
nullptr);
959 conf_reader_func_ = fn;
981 void insert_plugin(std::shared_ptr<Plugin> p,
const PluginConf& c = {});
986 bool has_plugin_with_name(
const std::string& key)
const;
991 bool has_plugin_with_path(
const std::string& path)
const;
998 std::shared_ptr<Plugin> get_plugin_with_name(
const std::string& key)
const;
1005 std::shared_ptr<Plugin> get_plugin_with_path(
const std::string& key)
const;
1010 std::shared_ptr<Plugin> get_plugin_or_load(
const std::string& key_or_path)
const;
1016 return all_plugins_;
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;
1031 void validate_self()
const;
1036 bool is_valid()
const;
1050 void check_consistency()
const;
1055 void check_defaults()
const;
1067 bool is_complete()
const;
1072 void check_completeness()
const;
1083 Json active_config()
const;
1088 Json input_config()
const;
1100 bool validate(
const Conf& c, std::optional<SchemaError>& err)
const override;
1103 void to_json(Json& j)
const override;
1107 CONFABLE_SCHEMA(
Stack) {
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
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)},
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")},
1140 void from_conf(
const Conf& c,
size_t depth);
Definition: stack.hpp:667
Definition: stack.hpp:573
Definition: stack.hpp:492
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
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