INSTINCT Code Coverage Report


Directory: src/
File: Nodes/DataProvider/CSV/CsvFile.cpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 12 122 9.8%
Functions: 4 15 26.7%
Branches: 10 224 4.5%

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 "CsvFile.hpp"
10
11 #include "util/Logger.hpp"
12 #include "util/StringUtil.hpp"
13
14 #include "internal/NodeManager.hpp"
15 namespace nm = NAV::NodeManager;
16 #include "internal/FlowManager.hpp"
17 #include "internal/gui/widgets/imgui_ex.hpp"
18
19 112 NAV::CsvFile::CsvFile()
20
3/6
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 112 times.
✗ Branch 9 not taken.
112 : Node(typeStatic())
21 {
22 LOG_TRACE("{}: called", name);
23
24 112 _hasConfig = true;
25 112 _guiConfigDefaultWindowSize = { 530, 271 };
26
27
5/10
✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 112 times.
✗ Branch 11 not taken.
✓ Branch 14 taken 112 times.
✓ Branch 15 taken 112 times.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
448 nm::CreateOutputPin(this, CsvData::type().c_str(), Pin::Type::Object, { CsvData::type() }, &_data);
28 224 }
29
30 224 NAV::CsvFile::~CsvFile()
31 {
32 LOG_TRACE("{}: called", nameId());
33 224 }
34
35 224 std::string NAV::CsvFile::typeStatic()
36 {
37
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
448 return "CsvFile";
38 }
39
40 std::string NAV::CsvFile::type() const
41 {
42 return typeStatic();
43 }
44
45 112 std::string NAV::CsvFile::category()
46 {
47
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
224 return "Data Provider";
48 }
49
50 void NAV::CsvFile::guiConfig()
51 {
52 if (auto res = FileReader::guiConfig(".csv,.*", { ".csv" }, size_t(id), nameId()))
53 {
54 LOG_DEBUG("{}: Path changed to {}", nameId(), _path);
55 flow::ApplyChanges();
56 if (res == FileReader::PATH_CHANGED)
57 {
58 doReinitialize();
59 }
60 else
61 {
62 doDeinitialize();
63 }
64 }
65
66 struct TextFilters
67 {
68 // Cuts off characters if the length exceeds 1
69 static int FilterSingleCharacter(ImGuiInputTextCallbackData* data)
70 {
71 while (data->BufTextLen > 1)
72 {
73 data->BufDirty = true;
74 data->DeleteChars(1, 1);
75 }
76 return 0;
77 }
78 };
79
80 std::string tmpStr(1, _delimiter);
81 if (ImGui::InputText(fmt::format("Delimiter character##{}", size_t(id)).c_str(), &tmpStr, ImGuiInputTextFlags_CallbackEdit, TextFilters::FilterSingleCharacter))
82 {
83 _delimiter = tmpStr.empty() ? '\0' : tmpStr.at(0);
84 LOG_DEBUG("{}: Delimiter character changed to {}", nameId(), _delimiter);
85 flow::ApplyChanges();
86 if (_delimiter)
87 {
88 doInitialize();
89 }
90 }
91
92 tmpStr = std::string(1, _comment);
93 if (ImGui::InputText(fmt::format("Comment character##{}", size_t(id)).c_str(), &tmpStr, ImGuiInputTextFlags_CallbackEdit, TextFilters::FilterSingleCharacter))
94 {
95 _comment = tmpStr.empty() ? '\0' : tmpStr.at(0);
96 LOG_DEBUG("{}: Comment character changed to {}", nameId(), _comment);
97 flow::ApplyChanges();
98 doInitialize();
99 }
100
101 if (ImGui::InputIntL(fmt::format("Skip lines##{}", size_t(id)).c_str(), &_skipLines, 0, std::numeric_limits<int>::max()))
102 {
103 LOG_DEBUG("{}: Skip lines changed to {}", nameId(), _skipLines);
104 flow::ApplyChanges();
105 doInitialize();
106 }
107
108 if (ImGui::Checkbox(fmt::format("Header line##{}", size_t(id)).c_str(), &_hasHeaderLine))
109 {
110 LOG_DEBUG("{}: HasHeaderLine changed to {}", nameId(), _hasHeaderLine);
111 flow::ApplyChanges();
112 doInitialize();
113 }
114
115 ImGui::Separator();
116
117 ImGui::Text("Amount of data lines in file: %zu", _data.lines.size());
118
119 // Header info
120 if (ImGui::BeginTable(fmt::format("##CSVHeaders ({})", size_t(id)).c_str(), 2,
121 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg))
122 {
123 ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
124 ImGui::TableSetupColumn("Description", ImGuiTableColumnFlags_WidthFixed);
125 ImGui::TableHeadersRow();
126
127 for (size_t i = 0; i < _data.description.size(); i++)
128 {
129 ImGui::TableNextRow();
130 ImGui::TableNextColumn();
131 ImGui::Text("%zu", i);
132 ImGui::TableNextColumn();
133 ImGui::TextUnformatted(_data.description[i].c_str());
134 }
135
136 ImGui::EndTable();
137 }
138 }
139
140 [[nodiscard]] json NAV::CsvFile::save() const
141 {
142 LOG_TRACE("{}: called", nameId());
143
144 json j;
145
146 j["FileReader"] = FileReader::save();
147 j["delimiter"] = _delimiter;
148 j["comment"] = _comment;
149 j["skipLines"] = _skipLines;
150 j["hasHeaderLine"] = _hasHeaderLine;
151
152 return j;
153 }
154
155 void NAV::CsvFile::restore(json const& j)
156 {
157 LOG_TRACE("{}: called", nameId());
158
159 if (j.contains("FileReader"))
160 {
161 FileReader::restore(j.at("FileReader"));
162 }
163 if (j.contains("delimiter"))
164 {
165 j.at("delimiter").get_to(_delimiter);
166 }
167 if (j.contains("comment"))
168 {
169 j.at("comment").get_to(_comment);
170 }
171 if (j.contains("skipLines"))
172 {
173 j.at("skipLines").get_to(_skipLines);
174 }
175 if (j.contains("hasHeaderLine"))
176 {
177 j.at("hasHeaderLine").get_to(_hasHeaderLine);
178 }
179 }
180
181 bool NAV::CsvFile::initialize()
182 {
183 LOG_TRACE("{}: called", nameId());
184
185 _data.description.clear();
186 _data.lines.clear();
187
188 if (!FileReader::initialize())
189 {
190 return false;
191 }
192
193 std::string line;
194 while (!eof())
195 {
196 getline(line);
197 if (line.empty() || line.at(0) == _comment) { continue; } // Skip empty and comment lines
198
199 auto splittedData = str::split(line, _delimiter);
200 if (!splittedData.empty()) { _data.lines.emplace_back(); }
201
202 for (const auto& cell : splittedData)
203 {
204 CsvData::CsvElement value;
205 try
206 {
207 value = std::stod(cell);
208 }
209 catch (...)
210 {
211 value = cell;
212 }
213 _data.lines.back().push_back(value);
214 }
215 }
216
217 LOG_TRACE("{}: initialize() finished. Read {} columns over {} lines.", nameId(), _data.description.size(), _data.lines.size());
218
219 return true;
220 }
221
222 bool NAV::CsvFile::resetNode()
223 {
224 LOG_TRACE("{}: called", nameId());
225 return true;
226 }
227
228 void NAV::CsvFile::deinitialize()
229 {
230 LOG_TRACE("{}: called", nameId());
231
232 _data.description.clear();
233 _data.lines.clear();
234
235 FileReader::deinitialize();
236 }
237
238 NAV::FileReader::FileType NAV::CsvFile::determineFileType()
239 {
240 return FileReader::FileType::ASCII;
241 }
242
243 void NAV::CsvFile::readHeader()
244 {
245 for (int i = 0; i < _skipLines; i++) { ignore(std::numeric_limits<std::streamsize>::max(), '\n'); } // Skip lines at the start of the file
246
247 std::string line;
248 if (_hasHeaderLine)
249 {
250 getline(line);
251 _data.description = str::split(line, _delimiter);
252 for (auto& desc : _data.description)
253 {
254 desc.erase(std::ranges::find_if(desc, [](int ch) { return std::iscntrl(ch); }), desc.end());
255 }
256 }
257 }
258