INSTINCT Code Coverage Report


Directory: src/
File: internal/ConfigManager.cpp
Date: 2025-06-06 13:17:47
Exec Total Coverage
Lines: 64 122 52.5%
Functions: 5 9 55.6%
Branches: 136 510 26.7%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 #include "ConfigManager.hpp"
10
11 #include <string>
12 #include <filesystem>
13 #include <fstream>
14 #include <iostream>
15 #include <boost/program_options/parsers.hpp>
16 #include <implot.h>
17 #include <implot_internal.h>
18 #include <fmt/ranges.h>
19
20 #include "internal/FlowManager.hpp"
21 #include "util/Logger.hpp"
22
23 #include <boost/tokenizer.hpp>
24
25 #include "util/Json.hpp"
26 #include "util/Plot/Colormap.hpp"
27 #include "internal/gui/windows/Screenshotter.hpp"
28
29 namespace bpo = boost::program_options;
30
31 /// Program option description
32 bpo::options_description program_options{ "Allowed options" }; // NOLINT
33
34 /// Map which stores all options
35 boost::program_options::variables_map NAV::ConfigManager::vm; // NOLINT
36
37 389 void NAV::ConfigManager::initialize()
38 {
39 LOG_TRACE("called");
40
41
2/2
✓ Branch 2 taken 270 times.
✓ Branch 3 taken 119 times.
389 if (program_options.options().empty())
42 {
43 // clang-format off
44 // See https://www.boost.org/doc/libs/1_72_0/doc/html/program_options.html
45
1/2
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
270 program_options.add_options()
46
2/4
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 270 times.
✗ Branch 6 not taken.
270 ("config", bpo::value<std::vector<std::string>>()->multitoken(), "List of configuration files to read parameters from" )
47
1/2
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
270 ("version,v", "Display the version number" )
48
1/2
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
270 ("help,h", "Display this help message" )
49
3/6
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
270 ("sigterm", bpo::bool_switch()->default_value(false), "Programm waits for -SIGUSR1 / -SIGINT / -SIGTERM" )
50
3/6
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
270 ("duration", bpo::value<size_t>()->default_value(0), "Program execution duration [sec]" )
51
3/6
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
270 ("nogui", bpo::bool_switch()->default_value(false), "Launch without the gui" )
52
3/6
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
270 ("noinit", bpo::bool_switch()->default_value(false), "Do not initialize flows after loading them" )
53
2/4
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
270 ("load,l", bpo::value<std::string>(), "Flow file to load" )
54
3/6
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
270 ("rotate-output", bpo::bool_switch()->default_value(false), "Create new folders for output files" )
55
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
810 ("output-path,o", bpo::value<std::string>()->default_value("logs"), "Directory path for logs and output files" )
56
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
810 ("input-path,i", bpo::value<std::string>()->default_value("data"), "Directory path for searching input files" )
57
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
810 ("flow-path,f", bpo::value<std::string>()->default_value("flow"), "Directory path for searching flow files" )
58
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
810 ("implot-config", bpo::value<std::string>()->default_value("implot.json"), "Config file to read implot settings from" )
59
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
810 ("global-log-level", bpo::value<std::string>()->default_value("trace"), "Global log level of all sinks (possible values: trace/debug/info/warning/error/critical/off" )
60
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
810 ("console-log-level", bpo::value<std::string>()->default_value("info"), "Log level on the console (possible values: trace/debug/info/warning/error/critical/off" )
61
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
810 ("file-log-level", bpo::value<std::string>()->default_value("debug"), "Log level to the log file (possible values: trace/debug/info/warning/error/critical/off" )
62
4/8
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 270 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 270 times.
✗ Branch 11 not taken.
1080 ("flush-log-level", bpo::value<std::string>()->default_value("info"), "Log level to flush on (possible values: trace/debug/info/warning/error/critical/off" )
63
2/4
✓ Branch 1 taken 270 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 270 times.
✗ Branch 5 not taken.
270 ("log-filter", bpo::value<std::string>(), "Filter/Regex for log messages" )
64 ;
65 // clang-format on
66 }
67 389 }
68
69 389 void NAV::ConfigManager::deinitialize()
70 {
71 389 vm.clear();
72 389 }
73
74 const boost::program_options::options_description& NAV::ConfigManager::GetProgramOptions()
75 {
76 return program_options;
77 }
78
79 389 std::vector<std::string> NAV::ConfigManager::FetchConfigs(const int argc, const char* argv[]) // NOLINT
80 {
81
2/4
✓ Branch 2 taken 389 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 389 times.
✗ Branch 6 not taken.
389 bpo::store(bpo::parse_command_line(argc, argv, program_options), vm);
82
83 389 std::vector<std::string> failedConfigFiles;
84
85 // if config file is available, the parameters from file will be added
86
4/6
✓ Branch 1 taken 389 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 389 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✓ Branch 8 taken 387 times.
778 if (vm.count("config"))
87 {
88
5/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 15 taken 4 times.
✓ Branch 16 taken 2 times.
8 for (const std::string& configFile : vm["config"].as<std::vector<std::string>>())
89 {
90
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 std::filesystem::path filepath{ configFile };
91
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (filepath.is_relative())
92 {
93
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 filepath = NAV::flow::GetProgramRootPath();
94
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 filepath /= configFile;
95 }
96
97
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 std::ifstream ifs{ filepath };
98
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
4 if (ifs.good())
99 {
100
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 bpo::store(bpo::parse_config_file(ifs, program_options), vm);
101 }
102 else
103 {
104
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 failedConfigFiles.push_back(configFile);
105 }
106 4 }
107 }
108
109
1/2
✓ Branch 1 taken 389 times.
✗ Branch 2 not taken.
389 bpo::notify(vm);
110
111 389 return failedConfigFiles;
112 }
113
114 112 void NAV::ConfigManager::CheckOptions(const int argc, [[maybe_unused]] const char* argv[]) // NOLINT
115 {
116
1/2
✓ Branch 3 taken 112 times.
✗ Branch 4 not taken.
112 LOG_DEBUG("{} arguments were provided over the command line", argc);
117
118
2/2
✓ Branch 2 taken 448 times.
✓ Branch 3 taken 112 times.
560 for (const char* logger : { "global-log-level", "console-log-level", "file-log-level", "flush-log-level" })
119 {
120
5/14
✓ Branch 1 taken 448 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 448 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 448 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 448 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 448 times.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
1792 if (vm[logger].as<std::string>() != "trace"
121
8/18
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 112 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 112 times.
✗ Branch 13 not taken.
✓ Branch 15 taken 112 times.
✓ Branch 16 taken 336 times.
✓ Branch 17 taken 448 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
672 && vm[logger].as<std::string>() != "debug"
122
9/18
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 112 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 112 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 112 times.
✓ Branch 15 taken 112 times.
✓ Branch 16 taken 336 times.
✓ Branch 17 taken 112 times.
✓ Branch 18 taken 336 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
672 && vm[logger].as<std::string>() != "info"
123
3/18
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 448 times.
✓ Branch 17 taken 112 times.
✓ Branch 18 taken 336 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
448 && vm[logger].as<std::string>() != "warning"
124
2/18
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 448 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 448 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
448 && vm[logger].as<std::string>() != "error"
125
2/18
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✓ Branch 16 taken 448 times.
✗ Branch 17 not taken.
✓ Branch 18 taken 448 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
448 && vm[logger].as<std::string>() != "critical"
126
6/24
✓ Branch 1 taken 448 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 112 times.
✓ Branch 4 taken 336 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✓ Branch 20 taken 448 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 448 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 448 times.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
1456 && vm[logger].as<std::string>() != "off")
127 {
128 LOG_CRITICAL("The command line argument '{}' has to be one of 'trace/debug/info/warning/error/critical/off' but the value '{}' was provided", logger, vm[logger].as<std::string>());
129 }
130 }
131
132
2/2
✓ Branch 0 taken 897 times.
✓ Branch 1 taken 112 times.
1009 for (int i = 0; i < argc; i++)
133 {
134
2/4
✓ Branch 1 taken 897 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 897 times.
✗ Branch 6 not taken.
897 LOG_DEBUG("\targument[{}] = '{}'", i, argv[i]);
135 }
136
137
1/2
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
112 LOG_DEBUG("{} arguments are set in the allowed variable map", vm.size());
138
139
2/2
✓ Branch 5 taken 1568 times.
✓ Branch 6 taken 112 times.
1680 for (const auto& value : vm)
140 {
141
2/2
✓ Branch 2 taken 112 times.
✓ Branch 3 taken 1456 times.
1568 if ([[maybe_unused]] const auto* v = boost::any_cast<size_t>(&value.second.value()))
142 {
143
2/4
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 112 times.
✗ Branch 6 not taken.
112 LOG_DEBUG("\tvm[{}] = '{}'", value.first, *v);
144 }
145
2/2
✓ Branch 2 taken 448 times.
✓ Branch 3 taken 1008 times.
1456 else if ([[maybe_unused]] const auto* v = boost::any_cast<bool>(&value.second.value())) // NOLINT(bugprone-bool-pointer-implicit-conversion)
146 {
147
2/4
✓ Branch 1 taken 448 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 448 times.
✗ Branch 6 not taken.
448 LOG_DEBUG("\tvm[{}] = '{}'", value.first, *v);
148 }
149
1/2
✓ Branch 2 taken 1008 times.
✗ Branch 3 not taken.
1008 else if ([[maybe_unused]] const auto* v = boost::any_cast<std::string>(&value.second.value()))
150 {
151
2/4
✓ Branch 1 taken 1008 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1008 times.
✗ Branch 6 not taken.
1008 LOG_DEBUG("\tvm[{}] = '{}'", value.first, *v);
152 }
153 else if ([[maybe_unused]] const auto* v = boost::any_cast<std::vector<std::string>>(&value.second.value()))
154 {
155 LOG_DEBUG("\tvm[{}] = '{}'", value.first, fmt::join(v->begin(), v->end(), ", "));
156 }
157 else
158 {
159 LOG_ERROR("The Log option vm[{}] could not be casted. Please report this to the developers.", value.first);
160 }
161 }
162 112 }
163
164 660 bool NAV::ConfigManager::HasKey(const std::string& key)
165 {
166 660 return vm.count(key);
167 }
168
169 std::vector<std::string> NAV::ConfigManager::GetKeys()
170 {
171 std::vector<std::string> keys;
172
173 for (auto& param : vm)
174 {
175 keys.push_back(param.first);
176 }
177
178 return keys;
179 }
180
181 void NAV::ConfigManager::SaveGlobalSettings()
182 {
183 // Save also global settings
184 std::ofstream filestream(flow::GetConfigPath() / "globals.json");
185 json j;
186 #ifdef IMGUI_IMPL_OPENGL_LOADER_GL3W
187 j["plotScreenshotImPlotStyleFile"] = gui::windows::plotScreenshotImPlotStyleFile;
188 j["copyScreenshotsToClipboard"] = gui::windows::copyScreenshotsToClipboard;
189 #endif
190 j["colormaps"] = ColormapsGlobal;
191
192 ImPlotContext& gp = *ImPlot::GetCurrentContext();
193 j["selectedImPlotColormap"] = gp.Style.Colormap;
194
195 constexpr int CMAP_USER_START = ImPlotColormap_Greys + 2;
196 for (int i = CMAP_USER_START; i < gp.ColormapData.Count; ++i)
197 {
198 j["ImPlotColormaps"][static_cast<size_t>(i - CMAP_USER_START)]["name"] = gp.ColormapData.GetName(i);
199 j["ImPlotColormaps"][static_cast<size_t>(i - CMAP_USER_START)]["qual"] = gp.ColormapData.IsQual(i);
200 for (int c = 0; c < gp.ColormapData.GetKeyCount(i); ++c)
201 {
202 j["ImPlotColormaps"][static_cast<size_t>(i - CMAP_USER_START)]["colors"][static_cast<size_t>(c)] =
203 ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(i, c));
204 }
205 }
206
207 filestream << std::setw(4) << j << std::endl; // NOLINT(performance-avoid-endl)
208 }
209
210 void NAV::ConfigManager::LoadGlobalSettings()
211 {
212 auto filepath = flow::GetConfigPath() / "globals.json";
213 std::ifstream filestream(filepath);
214
215 if (!filestream.good())
216 {
217 LOG_ERROR("Load Flow error: Could not open file: {}", filepath);
218 return;
219 }
220 json j;
221 filestream >> j;
222
223 if (j.contains("colormaps"))
224 {
225 j.at("colormaps").get_to(ColormapsGlobal);
226 }
227 #ifdef IMGUI_IMPL_OPENGL_LOADER_GL3W
228 if (j.contains("plotScreenshotImPlotStyleFile"))
229 {
230 j.at("plotScreenshotImPlotStyleFile").get_to(gui::windows::plotScreenshotImPlotStyleFile);
231 }
232 if (j.contains("copyScreenshotsToClipboard"))
233 {
234 j.at("copyScreenshotsToClipboard").get_to(gui::windows::copyScreenshotsToClipboard);
235 }
236 #endif
237 if (j.contains("ImPlotColormaps"))
238 {
239 for (size_t i = 0; i < j["ImPlotColormaps"].size(); ++i)
240 {
241 ImVector<ImVec4> custom;
242 for (const auto& c : j.at("ImPlotColormaps").at(i).at("colors"))
243 {
244 custom.push_back(c.get<ImVec4>());
245 }
246
247 ImPlot::AddColormap(j.at("ImPlotColormaps").at(i).at("name").get<std::string>().c_str(),
248 custom.Data, custom.Size, j.at("ImPlotColormaps").at(i).at("qual").get<bool>());
249 ImPlot::BustItemCache();
250 }
251 }
252 if (j.contains("selectedImPlotColormap"))
253 {
254 ImPlotContext& gp = *ImPlot::GetCurrentContext();
255 gp.Style.Colormap = j.at("selectedImPlotColormap").get<int>();
256 ImPlot::BustItemCache();
257 }
258 }
259