6 #include <Zivid/Settings.h> 7 #include <Zivid/Settings2D.h> 9 #include <dynamic_reconfigure/config_tools.h> 11 #include <boost/algorithm/string.hpp> 12 #include <boost/algorithm/string/replace.hpp> 13 #include <boost/algorithm/string/join.hpp> 14 #include <boost/filesystem.hpp> 15 #include <boost/format.hpp> 29 struct DependentFalse : std::false_type
33 template <
typename T,
typename...>
34 struct IsInList : std::false_type
38 template <
typename T,
typename First,
typename... Rest>
39 struct IsInList<T, First, Rest...>
40 : std::integral_constant<bool, std::is_same<T, First>::value || IsInList<T, Rest...>::value>
44 template <
typename T,
typename Tuple>
47 template <
typename T,
typename... Ts>
48 struct IsInTuple<T,
std::tuple<Ts...>> : IsInList<T, Ts...>
52 template <
typename SettingsNode>
53 struct IsEnumSetting : std::is_enum<typename SettingsNode::ValueType>
57 void writeToFile(
const std::string& file_name,
const std::string& text)
59 if (boost::filesystem::exists(file_name))
61 std::ifstream file(file_name);
64 throw std::runtime_error(
"Unable to open file '" + file_name +
"' to check its contents!");
66 auto file_contents_ss = std::ostringstream{};
67 file_contents_ss << file.rdbuf();
68 if (file_contents_ss.str() == text)
76 std::ofstream cfg_file(file_name);
77 if (!cfg_file || !cfg_file.is_open())
79 throw std::runtime_error(
"Unable to open file '" + file_name +
"' for writing!");
85 throw std::runtime_error(
"Failed to write to file '" + file_name +
"'!");
89 std::string toUpperCaseFirst(std::string value)
93 throw std::invalid_argument(
"value is empty");
95 value[0] = std::toupper(value[0]);
99 template <
typename SettingsRootGroup,
typename SettingsNode>
100 std::string convertSettingsPathToConfigPath()
102 auto path = std::string(SettingsNode::path);
103 const auto root_path = std::string(SettingsRootGroup::path);
105 if (!root_path.empty())
107 const auto expected_prefix = root_path +
"/";
108 if (path.substr(0, expected_prefix.length()) != expected_prefix)
110 throw std::runtime_error(
"Expected path '" + path +
"' to begin with '" + expected_prefix +
"'");
112 path = path.substr(expected_prefix.length());
115 path = boost::replace_all_copy<std::string>(path,
"/",
"_");
116 const std::regex re(
"([^_^])([A-Z])");
117 path = std::regex_replace(path, re,
"$1_$2");
118 return boost::algorithm::to_lower_copy(path);
121 template <
typename SettingsRootGroup>
122 std::string zividSettingsTypeName()
124 if constexpr (std::is_same_v<SettingsRootGroup, Zivid::Settings> ||
125 IsInTuple<SettingsRootGroup, Zivid::Settings::Descendants>::value ||
126 std::is_same_v<SettingsRootGroup, Zivid::Settings::Acquisition> ||
127 IsInTuple<SettingsRootGroup, Zivid::Settings::Acquisition::Descendants>::value)
129 return Zivid::Settings::name;
131 else if constexpr (std::is_same_v<SettingsRootGroup, Zivid::Settings2D> ||
132 IsInTuple<SettingsRootGroup, Zivid::Settings2D::Descendants>::value ||
133 std::is_same_v<SettingsRootGroup, Zivid::Settings2D::Acquisition> ||
134 IsInTuple<SettingsRootGroup, Zivid::Settings2D::Acquisition::Descendants>::value)
136 return Zivid::Settings2D::name;
140 static_assert(DependentFalse<SettingsRootGroup>::value,
"Unhandled type.");
144 template <
typename SettingsNode>
145 std::string fullyQualifiedZividTypeName()
147 std::stringstream ss;
148 ss <<
"Zivid::" + zividSettingsTypeName<SettingsNode>();
149 const auto path = std::string(SettingsNode::path);
152 ss <<
"::" << boost::replace_all_copy<std::string>(path,
"/",
"::");
157 template <
typename SettingsNode>
158 std::string settingEnumValueToRosEnumName(
typename SettingsNode::ValueType value)
166 const auto value_str = toUpperCaseFirst(SettingsNode{ value }.toString());
167 const auto path = boost::replace_all_copy<std::string>(SettingsNode::path,
"/",
"");
168 return path + value_str;
171 template <
typename SettingsNode>
172 std::string generateEnumConstant(
typename SettingsNode::ValueType value)
174 static_assert(IsEnumSetting<SettingsNode>::value);
175 const auto name = settingEnumValueToRosEnumName<SettingsNode>(value);
176 const auto int_value =
static_cast<std::size_t
>(value);
177 const auto description = toUpperCaseFirst(SettingsNode{ value }.toString());
178 return (boost::format(R
"(gen.const("%1%", int_t, %2%, "%3%"))") % name % int_value % description).str(); 181 template <
typename SettingsRootGroup>
182 class DynamicReconfigureCfgGenerator
185 DynamicReconfigureCfgGenerator(
const std::string& class_name) : class_name_(class_name), insert_enabled_(false)
189 template <
typename SettingsNode>
190 auto cfgFileDefaultValue()
195 using ValueType =
typename SettingsNode::ValueType;
196 if constexpr (std::is_same_v<ValueType, bool>)
200 if constexpr (Zivid::DataModel::HasValidRange<SettingsNode>::value)
202 return SettingsNode::validRange().min();
204 if constexpr (IsEnumSetting<SettingsNode>::value)
206 return *SettingsNode::validValues().begin();
208 return ValueType{ 0 };
211 template <
typename ValueType>
212 auto convertValueToRosValue(ValueType value)
216 if constexpr (std::is_same_v<ValueType, bool> || std::is_same_v<ValueType, double>)
220 else if constexpr (std::is_same_v<ValueType, std::size_t>)
222 return static_cast<int>(value);
224 else if constexpr (std::is_same_v<ValueType, std::chrono::microseconds>)
226 return static_cast<int>(value.count());
228 else if constexpr (std::is_enum_v<ValueType>)
230 return static_cast<int>(value);
234 static_assert(DependentFalse<ValueType>::value,
"Could not convert ValueType to ROS type.");
238 template <
typename RosType>
239 std::string rosTypeName()
241 if constexpr (std::is_same_v<RosType, bool>)
245 else if constexpr (std::is_same_v<RosType, double>)
249 else if constexpr (std::is_same_v<RosType, int>)
255 static_assert(DependentFalse<RosType>::value,
"Could not convert RosType to a ROS typename string.");
259 template <
typename RosType>
260 std::string rosTypeToString(RosType v)
262 if constexpr (std::is_same_v<RosType, bool>)
264 return v ?
"True" :
"False";
266 else if constexpr (std::is_same_v<RosType, double> || std::is_same_v<RosType, int>)
268 return std::to_string(v);
272 static_assert(DependentFalse<RosType>::value,
"Could not convert RosType to a string value.");
276 template <
typename ValueType>
277 std::string valueTypeToRosTypeString(ValueType v)
279 return rosTypeToString(convertValueToRosValue(v));
282 std::string rosGeneratedEnumVariableName(
const std::string& setting_name)
284 return setting_name +
"_enum";
287 template <
typename SettingsNode>
288 void apply(
const SettingsNode& node)
290 const auto setting_name = convertSettingsPathToConfigPath<SettingsRootGroup, SettingsNode>();
291 const auto level =
"0";
293 const auto description = boost::replace_all_copy<std::string>(node.description,
"\n", R
"(\\n)"); 294 const auto default_value = cfgFileDefaultValue<SettingsNode>();
295 const auto type_name = rosTypeName<decltype(convertValueToRosValue(default_value))>();
296 const auto default_value_str = valueTypeToRosTypeString(default_value);
298 if constexpr (IsEnumSetting<SettingsNode>::value)
300 const auto valid_values = node.validValues();
301 std::vector<std::string> enum_constants;
302 std::transform(valid_values.cbegin(), valid_values.cend(), std::back_inserter(enum_constants),
303 [&](
const auto value) {
return generateEnumConstant<SettingsNode>(value); });
304 ss_ << rosGeneratedEnumVariableName(setting_name) <<
" = gen.enum([\n " 305 << boost::algorithm::join(enum_constants,
",\n ") <<
"\n],\n \"" << description <<
"\")\n";
308 ss_ <<
"gen.add(\"" << setting_name <<
"\", " << type_name <<
", " << level <<
", " 309 <<
"\"" << description <<
"\", " << default_value_str;
311 if constexpr (Zivid::DataModel::HasValidRange<SettingsNode>::value)
313 ss_ <<
", " << valueTypeToRosTypeString(node.validRange().min()) <<
", " 314 << valueTypeToRosTypeString(node.validRange().max());
316 else if constexpr (IsEnumSetting<SettingsNode>::value)
318 const auto min_index = 0;
319 const auto max_index = node.validValues().size() - 1;
320 ss_ <<
", " << min_index <<
", " << max_index <<
", edit_method=" << rosGeneratedEnumVariableName(setting_name);
327 insert_enabled_ =
true;
332 std::stringstream res;
333 res <<
"#!/usr/bin/env python\n\n" 334 "# This is an auto-generated cfg file. Do not edit!\n\n" 335 "PACKAGE = \"zivid_camera\"\n" 337 "roslib.load_manifest(PACKAGE);\n" 338 "from dynamic_reconfigure.parameter_generator_catkin import *\n\n";
340 res <<
"gen = ParameterGenerator()\n";
343 res <<
"gen.add(\"enabled\", bool_t, 0, \"When this acquisition is enabled it will be included in captures\", " 347 res <<
"gen.generate(PACKAGE, \"zivid_camera\", \"" + class_name_ +
"\")\n";
352 std::string class_name_;
353 bool insert_enabled_;
354 std::stringstream ss_;
357 template <
typename SettingsRootGroup>
358 class ApplyConfigToZividSettingsGenerator
361 ApplyConfigToZividSettingsGenerator(
const std::string& config_class_name) : config_class_name_(config_class_name)
365 template <
typename SettingsNode>
366 void apply(
const SettingsNode& node)
368 using ValueType =
typename SettingsNode::ValueType;
370 const auto cfg_id =
"cfg." + convertSettingsPathToConfigPath<SettingsRootGroup, SettingsNode>();
371 const auto setting_node_class_name = fullyQualifiedZividTypeName<SettingsNode>();
372 ss_ <<
" s.set(" + setting_node_class_name +
"{ ";
374 if constexpr (std::is_same_v<ValueType, std::size_t>)
376 ss_ <<
"static_cast<std::size_t>(" + cfg_id +
")";
378 else if constexpr (std::is_same_v<ValueType, std::chrono::microseconds>)
380 ss_ <<
"std::chrono::microseconds(" + cfg_id +
")";
382 else if constexpr (IsEnumSetting<SettingsNode>::value)
385 <<
" [value = " + cfg_id +
"](){\n" 386 <<
" switch(value) {\n";
387 const auto valid_values = node.validValues();
388 for (
const auto& enum_value : valid_values)
390 ss_ <<
" case zivid_camera::" << config_class_name_ <<
"_" 391 << settingEnumValueToRosEnumName<SettingsNode>(enum_value) <<
":\n" 392 <<
" return " + setting_node_class_name +
"::" + SettingsNode{ enum_value }.toString() +
";\n";
395 <<
" throw std::runtime_error(\"Could not convert int value \" + std::to_string(value) + \"" 396 <<
" to setting of type " + setting_node_class_name +
".\");\n" 408 const auto root_type_fq = fullyQualifiedZividTypeName<SettingsRootGroup>();
410 std::stringstream res;
411 res <<
"inline static void applyConfigToZividSettings(const zivid_camera::" << config_class_name_ <<
"Config& cfg, " 412 << root_type_fq <<
"& s)\n";
420 std::string config_class_name_;
421 std::stringstream ss_;
424 template <
typename SettingsRootGroup>
425 class ZividSettingsToConfigGenerator
428 ZividSettingsToConfigGenerator(
const std::string& config_class_name) : config_class_name_(config_class_name)
432 template <
typename SettingsNode>
433 void apply(
const SettingsNode&)
435 using ValueType =
typename SettingsNode::ValueType;
436 const auto cfg_id =
"cfg." + convertSettingsPathToConfigPath<SettingsRootGroup, SettingsNode>();
437 const auto zivid_node_class_name = fullyQualifiedZividTypeName<SettingsNode>();
438 const auto value_str =
"s.get<" + zivid_node_class_name +
">().value()";
439 ss_ <<
" " + cfg_id +
" = ";
440 if constexpr (std::is_same_v<ValueType, std::chrono::microseconds>)
442 ss_ <<
"static_cast<int>(" + value_str +
".count());\n";
444 else if constexpr (std::is_same_v<ValueType, std::size_t>)
446 ss_ <<
"static_cast<int>(" + value_str +
");\n";
448 else if constexpr (IsEnumSetting<SettingsNode>::value)
450 ss_ <<
"static_cast<int>(" + value_str +
");\n";
454 ss_ << value_str +
";\n";
458 std::string str()
const 460 const auto full_class_name =
"zivid_camera::" + config_class_name_ +
"Config";
461 const auto root_type_fq = fullyQualifiedZividTypeName<SettingsRootGroup>();
463 std::stringstream res;
464 res <<
"template<> inline " << full_class_name <<
" zividSettingsToConfig";
465 res <<
"<" << full_class_name <<
">(const " << root_type_fq <<
"& s)\n";
467 res <<
" auto cfg = " + full_class_name <<
"::__getDefault__();\n";
469 res <<
" return cfg;\n";
475 std::string config_class_name_;
476 std::stringstream ss_;
479 template <
typename SettingsRootGroup>
480 class MinMaxConfigGenerator
489 MinMaxConfigGenerator(
const std::string& config_class_name, Type type)
490 : config_class_name_(config_class_name), type_(type)
494 template <
typename SettingsNode>
495 void apply(
const SettingsNode&)
497 if constexpr (Zivid::DataModel::HasValidRange<SettingsNode>::value)
499 using ValueType =
typename SettingsNode::ValueType;
500 const auto cfg_id =
"cfg." + convertSettingsPathToConfigPath<SettingsRootGroup, SettingsNode>();
501 const auto zivid_node_class_name = fullyQualifiedZividTypeName<SettingsNode>();
503 const auto value_str = [&]() {
505 "Zivid::Experimental::SettingsInfo::validRange<" + zivid_node_class_name +
">(camera.info())";
509 return prefix +
".min()";
511 return prefix +
".max()";
513 throw std::runtime_error(std::string(__func__) +
": Unhandled enum: " + toString(type_));
516 ss_ <<
" " + cfg_id +
" = ";
518 if constexpr (std::is_same_v<ValueType, std::chrono::microseconds>)
520 ss_ <<
"static_cast<int>(" + value_str +
".count());\n";
522 else if constexpr (std::is_same_v<ValueType, std::size_t>)
524 ss_ <<
"static_cast<int>(" + value_str +
");\n";
528 ss_ << value_str +
";\n";
533 std::string initializeConfigFunction()
const 542 throw std::runtime_error(std::string(__func__) +
": Unhandled enum: " + toString(type_));
545 std::string str()
const 547 const auto full_class_name =
"zivid_camera::" + config_class_name_ +
"Config";
549 const auto function_name_config_type = [&]() {
557 throw std::runtime_error(std::string(__func__) +
": Unhandled enum: " + toString(type_));
560 const auto root_type_fq = fullyQualifiedZividTypeName<SettingsRootGroup>();
562 std::stringstream res;
563 res <<
"template<> inline " << full_class_name <<
" zividSettings" << function_name_config_type <<
"Config";
564 res <<
"<" << full_class_name <<
", " << root_type_fq <<
">(const Zivid::Camera& camera)\n";
566 res <<
" auto cfg = " + full_class_name <<
"::" << initializeConfigFunction() <<
"();\n";
568 res <<
" return cfg;\n";
573 static std::string toString(Type t)
575 return std::to_string(
static_cast<std::underlying_type_t<Type>
>(t));
579 std::string config_class_name_;
581 std::stringstream ss_;
584 template <
typename SettingsRootGroup>
585 class ConfigUtilsHeaderGenerator
588 ConfigUtilsHeaderGenerator(
const std::string& config_class_name)
589 : apply_config_zivid_settings_gen(config_class_name)
590 , zivid_settings_to_config_gen(config_class_name)
591 , min_config_gen(config_class_name, MinMaxConfigGenerator<SettingsRootGroup>::Type::Min)
592 , max_config_gen(config_class_name, MinMaxConfigGenerator<SettingsRootGroup>::Type::Max)
596 template <
typename SettingsNode>
597 void apply(
const SettingsNode& node)
599 apply_config_zivid_settings_gen.apply(node);
600 zivid_settings_to_config_gen.apply(node);
601 min_config_gen.apply(node);
602 max_config_gen.apply(node);
607 std::stringstream res;
608 res <<
"#pragma once\n\n";
609 res <<
"// This is an auto-generated header. Do not edit.\n\n";
610 res <<
"#include <Zivid/Camera.h>\n";
611 res <<
"#include <Zivid/Experimental/SettingsInfo.h>\n\n";
612 res <<
"#include \"config_utils_common.h\"\n\n";
614 res << apply_config_zivid_settings_gen.str() <<
"\n";
615 res << zivid_settings_to_config_gen.str() <<
"\n";
616 res << min_config_gen.str() <<
"\n";
617 res << max_config_gen.str() <<
"\n";
621 ApplyConfigToZividSettingsGenerator<SettingsRootGroup> apply_config_zivid_settings_gen;
622 ZividSettingsToConfigGenerator<SettingsRootGroup> zivid_settings_to_config_gen;
623 MinMaxConfigGenerator<SettingsRootGroup> min_config_gen;
624 MinMaxConfigGenerator<SettingsRootGroup> max_config_gen;
627 template <
typename SettingsRootGroup>
631 Generator(
const std::string& config_class_name)
632 : config_class_name_(config_class_name)
633 , dynamic_reconfigure_cfg_gen_(config_class_name)
634 , config_utils_header_gen_(config_class_name)
638 template <
typename SettingsNode>
639 void apply(
const SettingsNode& node)
641 dynamic_reconfigure_cfg_gen_.apply(node);
642 config_utils_header_gen_.apply(node);
647 dynamic_reconfigure_cfg_gen_.insertEnabled();
652 writeToFile(config_class_name_ +
".cfg", dynamic_reconfigure_cfg_gen_.str());
653 writeToFile(
"generated_headers/" + config_class_name_ +
"ConfigUtils.h", config_utils_header_gen_.str());
657 std::string config_class_name_;
658 DynamicReconfigureCfgGenerator<SettingsRootGroup> dynamic_reconfigure_cfg_gen_;
659 ConfigUtilsHeaderGenerator<SettingsRootGroup> config_utils_header_gen_;
662 template <
typename SettingsType,
typename SettingsNode,
typename GeneratorType>
663 void traverseSettingsTree(
const SettingsNode& node, GeneratorType& generator)
665 if constexpr (std::is_same_v<SettingsNode, typename SettingsType::Acquisitions>)
669 else if constexpr (SettingsNode::nodeType == Zivid::DataModel::NodeType::group)
671 node.forEach([&](
const auto& child) { traverseSettingsTree<SettingsType>(child, generator); });
675 generator.apply(node);
679 template <
typename SettingsType>
680 void addSettingsType(
const std::string& cfgPrefix)
682 Generator<SettingsType> capture_general_gen(cfgPrefix);
683 traverseSettingsTree<SettingsType>(SettingsType{}, capture_general_gen);
684 capture_general_gen.writeToFiles();
686 Generator<typename SettingsType::Acquisition> capture_acquisition_gen(cfgPrefix +
"Acquisition");
687 traverseSettingsTree<SettingsType>(
typename SettingsType::Acquisition{}, capture_acquisition_gen);
688 capture_acquisition_gen.insertEnabled();
689 capture_acquisition_gen.writeToFiles();
696 addSettingsType<Zivid::Settings>(
"Settings");
697 addSettingsType<Zivid::Settings2D>(
"Settings2D");