INSTINCT Code Coverage Report


Directory: src/
File: Nodes/Utility/Combiner.cpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 159 384 41.4%
Functions: 15 22 68.2%
Branches: 187 886 21.1%

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 "Combiner.hpp"
10 #include "NodeRegistry.hpp"
11 #include "Navigation/Time/InsTime.hpp"
12 #include "Navigation/Time/TimeSystem.hpp"
13 #include "internal/Node/Pin.hpp"
14 #include "internal/gui/NodeEditorApplication.hpp"
15
16 #include <algorithm>
17 #include <cmath>
18 #include <cstddef>
19 #include <imgui.h>
20 #include <imgui_internal.h>
21 #include <limits>
22 #include <vector>
23 #include <spdlog/common.h>
24
25 #include "internal/gui/widgets/imgui_ex.hpp"
26 #include "util/Assert.h"
27 #include "util/Logger.hpp"
28
29 #include "internal/NodeManager.hpp"
30 #include <fmt/core.h>
31 namespace nm = NAV::NodeManager;
32 #include "internal/FlowManager.hpp"
33
34 #include "internal/gui/widgets/HelpMarker.hpp"
35
36 #include "NodeData/General/DynamicData.hpp"
37
38 namespace NAV
39 {
40
41 /// @brief Write info to a json object
42 /// @param[out] j Json output
43 /// @param[in] data Object to read info from
44 void to_json(json& j, const Combiner::Combination::Term& data)
45 {
46 j = json{
47 { "factor", data.factor },
48 { "pinIndex", data.pinIndex },
49 { "dataSelection", data.dataSelection },
50 };
51 }
52 /// @brief Read info from a json object
53 /// @param[in] j Json variable to read info from
54 /// @param[out] data Output object
55 12 void from_json(const json& j, Combiner::Combination::Term& data)
56 {
57
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 if (j.contains("factor")) { j.at("factor").get_to(data.factor); }
58
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 if (j.contains("pinIndex")) { j.at("pinIndex").get_to(data.pinIndex); }
59
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 if (j.contains("dataSelection")) { j.at("dataSelection").get_to(data.dataSelection); }
60 12 }
61
62 /// @brief Write info to a json object
63 /// @param[out] j Json output
64 /// @param[in] data Object to read info from
65 void to_json(json& j, const Combiner::Combination& data)
66 {
67 j = json{
68 { "terms", data.terms },
69 };
70 }
71 /// @brief Read info from a json object
72 /// @param[in] j Json variable to read info from
73 /// @param[out] data Output object
74 6 void from_json(const json& j, Combiner::Combination& data)
75 {
76
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 if (j.contains("terms")) { j.at("terms").get_to(data.terms); }
77 6 }
78
79 113 Combiner::Combiner()
80
7/14
✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 113 times.
✗ Branch 5 not taken.
✓ Branch 8 taken 113 times.
✗ Branch 9 not taken.
✓ Branch 11 taken 113 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 113 times.
✓ Branch 14 taken 113 times.
✓ Branch 21 taken 113 times.
✗ Branch 22 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
452 : Node(typeStatic())
81 {
82 LOG_TRACE("{}: called", name);
83
84 113 _hasConfig = true;
85 113 _guiConfigDefaultWindowSize = { 430, 410 };
86
87
1/2
✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
113 _dynamicInputPins.addPin(this);
88
1/2
✓ Branch 1 taken 113 times.
✗ Branch 2 not taken.
113 _dynamicInputPins.addPin(this);
89
4/8
✓ Branch 2 taken 113 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 113 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 113 times.
✓ Branch 10 taken 113 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
452 nm::CreateOutputPin(this, "Comb", Pin::Type::Flow, { DynamicData::type() });
90 339 }
91
92 228 Combiner::~Combiner()
93 {
94 LOG_TRACE("{}: called", nameId());
95 228 }
96
97 225 std::string Combiner::typeStatic()
98 {
99
1/2
✓ Branch 1 taken 225 times.
✗ Branch 2 not taken.
450 return "Combiner";
100 }
101
102 std::string Combiner::type() const
103 {
104 return typeStatic();
105 }
106
107 112 std::string Combiner::category()
108 {
109
1/2
✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
224 return "Utility";
110 }
111
112 void Combiner::guiConfig()
113 {
114 ImGui::SetNextItemWidth(150.0F * gui::NodeEditorApplication::windowFontRatio());
115 if (ImGui::BeginCombo(fmt::format("Reference pin##{}", size_t(id)).c_str(), inputPins.at(_refPinIdx).name.c_str()))
116 {
117 for (size_t i = 0; i < inputPins.size(); i++)
118 {
119 const bool is_selected = _refPinIdx == i;
120 if (ImGui::Selectable(inputPins.at(i).name.c_str(), is_selected))
121 {
122 _refPinIdx = i;
123 flow::ApplyChanges();
124 }
125 if (is_selected) { ImGui::SetItemDefaultFocus(); }
126 }
127 ImGui::EndCombo();
128 }
129 ImGui::SameLine();
130 gui::widgets::HelpMarker("Outputs will be sent at epochs of this input pin");
131
132 ImGui::SameLine();
133 if (ImGui::Checkbox(fmt::format("Output missing as NaN##{}", size_t(id)).c_str(), &_outputMissingAsNaN))
134 {
135 flow::ApplyChanges();
136 }
137
138 if (ImGui::Checkbox(fmt::format("##_noOutputIfTimeDiffLarge{}", size_t(id)).c_str(), &_noOutputIfTimeDiffLarge))
139 {
140 flow::ApplyChanges();
141 }
142 ImGui::SameLine();
143 if (!_noOutputIfTimeDiffLarge) { ImGui::BeginDisabled(); }
144 ImGui::SetNextItemWidth(200.0F * gui::NodeEditorApplication::windowFontRatio());
145 if (ImGui::DragDouble(fmt::format("Max time diff to interpolate##{}", size_t(id)).c_str(),
146 &_maxTimeDiffMultiplierFrequency, 1.0F, 0.0, std::numeric_limits<double>::max(), "%.1f * dt_min"))
147 {
148 flow::ApplyChanges();
149 }
150 if (!_noOutputIfTimeDiffLarge) { ImGui::EndDisabled(); }
151
152 if (ImGui::Checkbox(fmt::format("##_noOutputIfTimeStepLarge{}", size_t(id)).c_str(), &_noOutputIfTimeStepLarge))
153 {
154 flow::ApplyChanges();
155 }
156 ImGui::SameLine();
157 if (!_noOutputIfTimeStepLarge) { ImGui::BeginDisabled(); }
158 ImGui::SetNextItemWidth(200.0F * gui::NodeEditorApplication::windowFontRatio());
159 if (ImGui::DragDouble(fmt::format("Max observation time diff##{}", size_t(id)).c_str(),
160 &_maxTimeStepMultiplierFrequency, 1.0F, 0.0, std::numeric_limits<double>::max(), "%.1f * dt_min"))
161 {
162 flow::ApplyChanges();
163 }
164 if (!_noOutputIfTimeStepLarge) { ImGui::EndDisabled(); }
165
166 if (CommonLog::ShowOriginInput(nameId().c_str()))
167 {
168 flow::ApplyChanges();
169 }
170
171 ImGui::SetNextItemOpen(false, ImGuiCond_FirstUseEver);
172 if (ImGui::CollapsingHeader(fmt::format("Pins##{}", size_t(id)).c_str()))
173 {
174 std::vector<size_t> pinIds;
175 pinIds.reserve(inputPins.size());
176 for (const auto& pin : inputPins) { pinIds.push_back(size_t(pin.id)); }
177 if (_dynamicInputPins.ShowGuiWidgets(size_t(id), inputPins, this))
178 {
179 if (_refPinIdx > inputPins.size()) { _refPinIdx--; }
180 std::vector<size_t> inputPinIds;
181 inputPinIds.reserve(inputPins.size());
182 for (const auto& pin : inputPins) { inputPinIds.push_back(size_t(pin.id)); }
183 LOG_DATA("{}: old Input pin ids {}", nameId(), fmt::join(pinIds, ", "));
184 LOG_DATA("{}: new Input pin ids {}", nameId(), fmt::join(inputPinIds, ", "));
185 if (inputPins.size() == pinIds.size()) // Reorder performed
186 {
187 for (size_t i = 0; i < pinIds.size(); i++)
188 {
189 auto pinId = pinIds.at(i);
190 const auto& inputPinId = size_t(inputPins.at(i).id);
191 if (pinId != inputPinId)
192 {
193 size_t newPinIdx = inputPinIndexFromId(pinId);
194 LOG_DATA("{}: Pin {} moved index {} -> {}", nameId(), pinId, i, newPinIdx);
195 for (auto& comb : _combinations)
196 {
197 for (auto& term : comb.terms)
198 {
199 if (term.pinIndex == i) { term.pinIndex = newPinIdx + 10000; }
200 }
201 }
202 if (_refPinIdx == i) { _refPinIdx = newPinIdx + 10000; }
203 }
204 }
205 for (auto& comb : _combinations)
206 {
207 for (auto& term : comb.terms)
208 {
209 if (term.pinIndex >= 10000) { term.pinIndex -= 10000; }
210 }
211 }
212 if (_refPinIdx >= 10000) { _refPinIdx -= 10000; }
213 }
214 flow::ApplyChanges();
215 }
216 }
217
218 std::vector<size_t> combToDelete;
219 for (size_t c = 0; c < _combinations.size(); c++)
220 {
221 auto& comb = _combinations.at(c);
222
223 bool keepCombination = true;
224 if (ImGui::CollapsingHeader(fmt::format("{}##id{} c{}", comb.description(this), size_t(id), c).c_str(),
225 _combinations.size() > 1 ? &keepCombination : nullptr, ImGuiTreeNodeFlags_DefaultOpen))
226 {
227 constexpr int COL_PER_TERM = 3;
228 const float COL_SIGN_WIDTH = 50.0F * gui::NodeEditorApplication::windowFontRatio();
229 const float COL_PIN_WIDTH = 100.0F * gui::NodeEditorApplication::windowFontRatio();
230
231 std::vector<size_t> termToDelete;
232 bool addTerm = false;
233 if (ImGui::BeginTable(fmt::format("##Table id{} c{}", size_t(id), c).c_str(), COL_PER_TERM * static_cast<int>(comb.terms.size()),
234 ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX,
235 ImVec2(0, 70.0F * gui::NodeEditorApplication::windowFontRatio())))
236 {
237 ImGui::TableNextRow();
238
239 for (size_t t = 0; t < comb.terms.size(); t++)
240 {
241 auto& term = comb.terms.at(t);
242
243 ImGui::TableSetColumnIndex(static_cast<int>(t) * COL_PER_TERM);
244 ImGui::SetNextItemWidth(COL_SIGN_WIDTH);
245 if (ImGui::InputDouble(fmt::format("##factor id{} c{} t{}", size_t(id), c, t).c_str(), &term.factor, 0.0, 0.0, "%.2f"))
246 {
247 flow::ApplyChanges();
248 }
249 ImGui::SameLine();
250 ImGui::TextUnformatted("*");
251
252 ImGui::SameLine();
253 ImGui::SetNextItemWidth(COL_PIN_WIDTH);
254 if (ImGui::BeginCombo(fmt::format("##pinIndex id{} c{} t{}", size_t(id), c, t).c_str(), inputPins.at(term.pinIndex).name.c_str()))
255 {
256 for (size_t i = 0; i < inputPins.size(); i++)
257 {
258 const bool is_selected = term.pinIndex == i;
259 if (ImGui::Selectable(inputPins.at(i).name.c_str(), is_selected))
260 {
261 term.pinIndex = i;
262 term.dataSelection = size_t(0);
263 flow::ApplyChanges();
264 }
265 if (is_selected) { ImGui::SetItemDefaultFocus(); }
266 }
267 ImGui::EndCombo();
268 }
269
270 ImGui::TableSetColumnIndex(static_cast<int>(t) * COL_PER_TERM + 1);
271 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 2.0F);
272 float radius = 8.0F * gui::NodeEditorApplication::windowFontRatio();
273 ImGui::GetWindowDrawList()->AddCircleFilled(ImGui::GetCursorScreenPos() + ImVec2(radius, ImGui::GetTextLineHeight() / 2.0F + 2.0F), radius,
274 term.polyReg.empty() ? IM_COL32(255, 0, 0, 255) : IM_COL32(0, 255, 0, 255));
275 ImGui::Dummy(ImVec2(radius * 2.0F, ImGui::GetTextLineHeight()));
276 if (ImGui::IsItemHovered()) { ImGui::SetTooltip(term.polyReg.empty() ? "Signal was not received" : "Signal was received"); }
277
278 ImGui::TableSetColumnIndex(static_cast<int>(t) * COL_PER_TERM + 2);
279 if (static_cast<int>(t) * COL_PER_TERM + 2 == COL_PER_TERM * static_cast<int>(comb.terms.size()) - 1)
280 {
281 addTerm |= ImGui::Button(fmt::format("+## Add Term id{} c{}", size_t(id), c).c_str());
282 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Add Term?"); }
283 }
284 else
285 {
286 ImGui::TextUnformatted("+");
287 }
288 }
289
290 ImGui::TableNextRow();
291 for (size_t t = 0; t < comb.terms.size(); t++)
292 {
293 auto& term = comb.terms.at(t);
294
295 ImGui::TableSetColumnIndex(static_cast<int>(t) * COL_PER_TERM);
296 auto dataDescriptors = getDataDescriptors(term.pinIndex);
297 if ((std::holds_alternative<size_t>(term.dataSelection) && std::get<size_t>(term.dataSelection) < dataDescriptors.size())
298 || std::holds_alternative<std::string>(term.dataSelection))
299 {
300 std::string previewText = std::holds_alternative<size_t>(term.dataSelection)
301 ? dataDescriptors.at(std::get<size_t>(term.dataSelection))
302 : std::get<std::string>(term.dataSelection);
303 float comboWidth = COL_SIGN_WIDTH + COL_PIN_WIDTH + ImGui::CalcTextSize("*").x + 2.0F * ImGui::GetStyle().ItemSpacing.x;
304 ImGui::SetNextItemWidth(comboWidth);
305 if (ImGui::BeginCombo(fmt::format("##dataIndex id{} c{} t{}", size_t(id), c, t).c_str(), previewText.c_str()))
306 {
307 for (size_t i = 0; i < dataDescriptors.size(); i++)
308 {
309 const bool is_selected = std::holds_alternative<size_t>(term.dataSelection)
310 ? std::get<size_t>(term.dataSelection) == i
311 : dataDescriptors.at(i) == previewText;
312 if (ImGui::Selectable(dataDescriptors.at(i).c_str(), is_selected))
313 {
314 if (dataDescriptors.size() - i <= _pinData.at(term.pinIndex).dynDataDescriptors.size())
315 {
316 term.dataSelection = dataDescriptors.at(i);
317 }
318 else
319 {
320 term.dataSelection = i;
321 }
322 for (size_t t2 = 0; t2 < comb.terms.size(); t2++) // Set other terms to same data if possible
323 {
324 if (t2 == t) { continue; }
325 auto& term2 = comb.terms.at(t2);
326 auto dataDescriptors2 = getDataDescriptors(term2.pinIndex);
327 auto iter = std::ranges::find(dataDescriptors2,
328 std::holds_alternative<size_t>(term.dataSelection)
329 ? dataDescriptors.at(std::get<size_t>(term.dataSelection))
330 : std::get<std::string>(term.dataSelection));
331 if (iter != dataDescriptors2.end())
332 {
333 if (std::holds_alternative<size_t>(term.dataSelection))
334 {
335 term2.dataSelection = static_cast<size_t>(std::distance(dataDescriptors2.begin(), iter));
336 }
337 else
338 {
339 term2.dataSelection = term.dataSelection;
340 }
341 }
342 }
343
344 flow::ApplyChanges();
345 }
346 if (is_selected) { ImGui::SetItemDefaultFocus(); }
347 }
348 ImGui::EndCombo();
349 }
350 if (ImGui::IsItemHovered() && ImGui::CalcTextSize(previewText.c_str()).x > comboWidth - 15.0F)
351 {
352 ImGui::SetTooltip("%s", previewText.c_str());
353 }
354 }
355 else if (inputPins.at(term.pinIndex).isPinLinked())
356 {
357 ImGui::TextUnformatted("Please run the flow");
358 ImGui::SameLine();
359 gui::widgets::HelpMarker("Available data is collected when running the flow");
360 }
361
362 if (comb.terms.size() > 1)
363 {
364 ImGui::TableSetColumnIndex(static_cast<int>(t) * COL_PER_TERM + 1);
365 if (ImGui::Button(fmt::format("X##id{} c{} t{}", size_t(id), c, t).c_str()))
366 {
367 flow::ApplyChanges();
368 termToDelete.push_back(t);
369 }
370 if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Remove Term?"); }
371 }
372 }
373
374 ImGui::EndTable();
375 }
376
377 for (const auto& t : termToDelete) { comb.terms.erase(std::next(comb.terms.begin(), static_cast<std::ptrdiff_t>(t))); }
378 if (addTerm)
379 {
380 flow::ApplyChanges();
381 comb.terms.emplace_back();
382 }
383 }
384
385 if (!keepCombination) { combToDelete.push_back(c); }
386 }
387 for (const auto& c : combToDelete) { _combinations.erase(std::next(_combinations.begin(), static_cast<std::ptrdiff_t>(c))); }
388
389 ImGui::Separator();
390 if (ImGui::Button(fmt::format("Add Combination##id{}", size_t(id)).c_str()))
391 {
392 flow::ApplyChanges();
393 _combinations.emplace_back();
394 }
395 }
396
397 [[nodiscard]] json Combiner::save() const
398 {
399 LOG_TRACE("{}: called", nameId());
400
401 return {
402 { "dynamicInputPins", _dynamicInputPins },
403 { "combinations", _combinations },
404 { "refPinIdx", _refPinIdx },
405 { "outputMissingAsNaN", _outputMissingAsNaN },
406 { "noOutputIfTimeDiffLarge", _noOutputIfTimeDiffLarge },
407 { "maxTimeDiffMultiplierFrequency", _maxTimeDiffMultiplierFrequency },
408 { "noOutputIfTimeStepLarge", _noOutputIfTimeStepLarge },
409 { "maxTimeStepMultiplierFrequency", _maxTimeStepMultiplierFrequency },
410 };
411 }
412
413 1 void Combiner::restore(json const& j)
414 {
415 LOG_TRACE("{}: called", nameId());
416
417
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (j.contains("dynamicInputPins")) { NAV::gui::widgets::from_json(j.at("dynamicInputPins"), _dynamicInputPins, this); }
418
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (j.contains("combinations")) { j.at("combinations").get_to(_combinations); }
419
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (j.contains("refPinIdx")) { j.at("refPinIdx").get_to(_refPinIdx); }
420
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (j.contains("outputMissingAsNaN")) { j.at("outputMissingAsNaN").get_to(_outputMissingAsNaN); }
421
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (j.contains("noOutputIfTimeDiffLarge")) { j.at("noOutputIfTimeDiffLarge").get_to(_noOutputIfTimeDiffLarge); }
422
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (j.contains("maxTimeDiffMultiplierFrequency")) { j.at("maxTimeDiffMultiplierFrequency").get_to(_maxTimeDiffMultiplierFrequency); }
423
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (j.contains("noOutputIfTimeStepLarge")) { j.at("noOutputIfTimeStepLarge").get_to(_noOutputIfTimeStepLarge); }
424
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (j.contains("maxTimeStepMultiplierFrequency")) { j.at("maxTimeStepMultiplierFrequency").get_to(_maxTimeStepMultiplierFrequency); }
425 1 }
426
427 227 void Combiner::pinAddCallback(Node* node)
428 {
429 227 auto* combiner = static_cast<Combiner*>(node); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
430 227 combiner->_pinData.emplace_back();
431
1/2
✓ Branch 5 taken 227 times.
✗ Branch 6 not taken.
454 nm::CreateInputPin(node, fmt::format("Pin {}", node->inputPins.size() + 1).c_str(), Pin::Type::Flow, _dataIdentifier, &Combiner::receiveData);
432 227 }
433
434 void Combiner::pinDeleteCallback(Node* node, size_t pinIdx)
435 {
436 auto* combiner = static_cast<Combiner*>(node); // NOLINT(cppcoreguidelines-pro-type-static-cast-downcast)
437 if (pinIdx == combiner->_refPinIdx && combiner->_refPinIdx && combiner->_refPinIdx >= combiner->inputPins.size()) { combiner->_refPinIdx--; }
438 combiner->_pinData.erase(std::next(combiner->_pinData.begin(), static_cast<int64_t>(pinIdx)));
439 nm::DeleteInputPin(node->inputPins.at(pinIdx));
440
441 for (auto& comb : combiner->_combinations)
442 {
443 for (int64_t t = 0; t < static_cast<int64_t>(comb.terms.size()); ++t)
444 {
445 auto& term = comb.terms.at(static_cast<size_t>(t));
446 if (term.pinIndex == pinIdx) // The index we want to delete
447 {
448 comb.terms.erase(comb.terms.begin() + t);
449 --t;
450 }
451 else if (term.pinIndex > pinIdx) // Index higher -> Decrement
452 {
453 --(term.pinIndex);
454 }
455 }
456 }
457 }
458
459 3 bool Combiner::initialize()
460 {
461 LOG_TRACE("{}: called", nameId());
462
463 3 CommonLog::initialize();
464
465
2/2
✓ Branch 5 taken 18 times.
✓ Branch 6 taken 3 times.
21 for (auto& comb : _combinations)
466 {
467
2/2
✓ Branch 5 taken 36 times.
✓ Branch 6 taken 18 times.
54 for (auto& term : comb.terms)
468 {
469
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 term.polyReg.reset();
470 }
471 }
472
473
2/2
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 3 times.
12 for (auto& pinData : _pinData)
474 {
475 9 pinData.lastTime.reset();
476 9 pinData.minTimeStep = std::numeric_limits<double>::infinity();
477 9 pinData.dynDataDescriptors.clear();
478 }
479
480 3 _sendRequests.clear();
481
482 3 return true;
483 }
484
485 1 void Combiner::deinitialize()
486 {
487 LOG_TRACE("{}: called", nameId());
488 1 }
489
490 581328 std::vector<std::string> Combiner::getDataDescriptors(size_t pinIndex) const
491 {
492 581328 std::vector<std::string> dataDescriptors;
493
3/6
✓ Branch 1 taken 581328 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 581328 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 581328 times.
✗ Branch 7 not taken.
581328 if (OutputPin* sourcePin = inputPins.at(pinIndex).link.getConnectedPin())
494 {
495
1/2
✓ Branch 1 taken 581328 times.
✗ Branch 2 not taken.
581328 dataDescriptors = NAV::NodeRegistry::GetStaticDataDescriptors(sourcePin->dataIdentifier);
496 }
497
2/4
✓ Branch 1 taken 581328 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 581328 times.
581328 if (!_pinData.at(pinIndex).dynDataDescriptors.empty())
498 {
499 dataDescriptors.reserve(dataDescriptors.size() + _pinData.at(pinIndex).dynDataDescriptors.size());
500 std::copy(_pinData.at(pinIndex).dynDataDescriptors.begin(), _pinData.at(pinIndex).dynDataDescriptors.end(), std::back_inserter(dataDescriptors));
501 }
502 581328 return dataDescriptors;
503 }
504
505 48444 void Combiner::receiveData(InputPin::NodeDataQueue& queue, size_t pinIdx)
506 {
507
1/2
✓ Branch 1 taken 48444 times.
✗ Branch 2 not taken.
48444 auto nodeData = queue.extract_front();
508
2/4
✓ Branch 2 taken 48444 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 48444 times.
✗ Branch 6 not taken.
48444 auto nodeDataTimeIntoRun = math::round(calcTimeIntoRun(nodeData->insTime), 8);
509 LOG_DATA("{}: [{:.3f}s][{} ({})] Received obs for time [{} GPST] ", nameId(),
510 nodeDataTimeIntoRun, inputPins.at(pinIdx).name, pinIdx, nodeData->insTime.toYMDHMS(GPST));
511
512
3/4
✓ Branch 1 taken 48444 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48442 times.
✓ Branch 5 taken 2 times.
48444 if (!_pinData.at(pinIdx).lastTime.empty())
513 {
514
2/4
✓ Branch 1 taken 48442 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 48442 times.
✗ Branch 6 not taken.
48442 auto dt = static_cast<double>((nodeData->insTime - _pinData.at(pinIdx).lastTime).count());
515
3/6
✓ Branch 0 taken 48442 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 48442 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 48442 times.
✗ Branch 8 not taken.
48442 if (dt > 1e-6) { _pinData.at(pinIdx).minTimeStep = std::min(_pinData.at(pinIdx).minTimeStep, dt); }
516 }
517
1/2
✓ Branch 2 taken 48444 times.
✗ Branch 3 not taken.
48444 _pinData.at(pinIdx).lastTime = nodeData->insTime;
518
519
2/2
✓ Branch 0 taken 24222 times.
✓ Branch 1 taken 24222 times.
48444 if (pinIdx == _refPinIdx)
520 {
521 24222 std::vector<SendRequest> requests;
522
2/2
✓ Branch 1 taken 145332 times.
✓ Branch 2 taken 24222 times.
169554 for (size_t c = 0; c < _combinations.size(); ++c)
523 {
524
3/6
✓ Branch 1 taken 145332 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 145332 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 145332 times.
145332 if (std::any_of(_combinations.at(c).terms.begin(),
525
1/2
✓ Branch 1 taken 145332 times.
✗ Branch 2 not taken.
145332 _combinations.at(c).terms.end(),
526 290664 [&](const Combination::Term& term) { return _pinData.at(term.pinIndex).lastTime.empty()
527
4/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 290658 times.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 6 times.
290664 && (inputPins.at(term.pinIndex).queue.empty() || inputPins.at(term.pinIndex).queue.front()->insTime != nodeData->insTime); }))
528 {
529 continue; // We cannot add a term when the first data appears later
530 }
531 145332 SendRequest sr{
532 .combIndex = c,
533 .termIndices = {},
534 .result = 0.0,
535 .rawData = {},
536 145332 };
537
1/2
✓ Branch 1 taken 145332 times.
✗ Branch 2 not taken.
145332 requests.push_back(sr);
538 145332 }
539
1/2
✓ Branch 1 taken 24222 times.
✗ Branch 2 not taken.
24222 if (!requests.empty())
540 {
541 LOG_DATA("{}: Adding new send request with", nameId(), nodeDataTimeIntoRun, nodeData->insTime.toYMDHMS(GPST));
542
2/2
✓ Branch 5 taken 145332 times.
✓ Branch 6 taken 24222 times.
169554 for ([[maybe_unused]] const auto& request : requests)
543 {
544 LOG_DATA("{}: Combination: {}", nameId(), _combinations.at(request.combIndex).description(this));
545 }
546
1/2
✓ Branch 2 taken 24222 times.
✗ Branch 3 not taken.
24222 _sendRequests.emplace(nodeData->insTime, requests);
547 }
548 24222 }
549
550 // Add dynamic data descriptors to display in GUI
551
1/2
✓ Branch 1 taken 48444 times.
✗ Branch 2 not taken.
48444 auto& dataDescriptors = _pinData.at(pinIdx).dynDataDescriptors;
552
1/2
✓ Branch 2 taken 48444 times.
✗ Branch 3 not taken.
48444 const std::vector<std::string> nodeDataDescriptors = nodeData->dynamicDataDescriptors();
553
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 48444 times.
48444 for (const auto& desc : nodeDataDescriptors)
554 {
555 if (std::ranges::find(dataDescriptors, desc) == dataDescriptors.end())
556 {
557 dataDescriptors.push_back(desc);
558 }
559 }
560
561
2/4
✓ Branch 1 taken 48444 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 48444 times.
✗ Branch 5 not taken.
48444 auto* sourcePin = inputPins.at(pinIdx).link.getConnectedPin();
562
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48444 times.
48444 if (sourcePin == nullptr) { return; }
563
564
2/2
✓ Branch 1 taken 290664 times.
✓ Branch 2 taken 48444 times.
339108 for (size_t c = 0; c < _combinations.size(); ++c)
565 {
566
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 auto& comb = _combinations.at(c);
567 LOG_DATA("{}: Combination: {}", nameId(), comb.description(this));
568
2/2
✓ Branch 1 taken 581328 times.
✓ Branch 2 taken 290664 times.
871992 for (size_t t = 0; t < comb.terms.size(); ++t)
569 {
570
1/2
✓ Branch 1 taken 581328 times.
✗ Branch 2 not taken.
581328 auto& term = comb.terms.at(t);
571
2/2
✓ Branch 0 taken 290664 times.
✓ Branch 1 taken 290664 times.
581334 if (term.pinIndex != pinIdx) { continue; }
572
3/6
✓ Branch 3 taken 290664 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 290664 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 290664 times.
581328 if (std::holds_alternative<size_t>(term.dataSelection) && nodeData->staticDescriptorCount() <= std::get<size_t>(term.dataSelection)
573
2/8
✓ Branch 0 taken 290664 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 290664 times.
581328 && std::get<size_t>(term.dataSelection) < dataDescriptors.size())
574 {
575 term.dataSelection = dataDescriptors.at(std::get<size_t>(term.dataSelection));
576 flow::ApplyChanges();
577 }
578
579
3/6
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 290664 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 290664 times.
✗ Branch 9 not taken.
290664 auto value = std::holds_alternative<size_t>(term.dataSelection) ? nodeData->getValueAt(std::get<size_t>(term.dataSelection))
580 : nodeData->getDynamicDataAt(std::get<std::string>(term.dataSelection));
581
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 290664 times.
290664 if (!value) { continue; }
582
583 LOG_DATA("{}: Term '{}': {:.3g}", nameId(), term.description(this, getDataDescriptors(term.pinIndex)), *value);
584
1/2
✓ Branch 3 taken 290664 times.
✗ Branch 4 not taken.
290664 term.polyReg.push_back(std::make_pair(nodeDataTimeIntoRun, *value));
585
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 term.rawData.push_back(nodeData);
586 LOG_DATA("{}: Adding NodeData to the end of term.rawData. It now includes:", nameId());
587
7/12
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 290664 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 581316 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 581316 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 871980 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 581316 times.
✓ Branch 16 taken 290664 times.
871980 for ([[maybe_unused]] const auto& data : term.rawData)
588 {
589 LOG_DATA("{}: {}", nameId(), data->insTime.toYMDHMS(GPST));
590 }
591
592 // Check for all combinations with new info:
593
594
3/4
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
✓ Branch 4 taken 290658 times.
290664 if (std::ranges::any_of(comb.terms, [&](const auto& t) {
595
1/2
✓ Branch 1 taken 581328 times.
✗ Branch 2 not taken.
581328 bool termHasNoData = t.polyReg.empty();
596 581328 if (termHasNoData)
597 {
598 LOG_DATA("{}: Skipping, because no data on other term '{}' yet", nameId(), t.description(this, getDataDescriptors(t.pinIndex)));
599 }
600 581328 return termHasNoData;
601 }))
602 {
603 6 continue;
604 }
605
606 290658 if (!_sendRequests.empty()) { LOG_DATA("{}: Checking if term is in a send request", nameId()); }
607
2/2
✓ Branch 7 taken 290664 times.
✓ Branch 8 taken 290658 times.
581322 for (auto& [sendRequestTime, sendRequests] : _sendRequests)
608 {
609 290664 std::set<size_t> combsToRemove;
610
2/2
✓ Branch 5 taken 1743984 times.
✓ Branch 6 taken 290664 times.
2034648 for (auto& sendRequest : sendRequests)
611 {
612
2/2
✓ Branch 0 taken 1453320 times.
✓ Branch 1 taken 290664 times.
1743984 if (sendRequest.combIndex != c) { continue; }
613
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 const auto& srComb = _combinations.at(sendRequest.combIndex);
614
615
2/2
✓ Branch 5 taken 581328 times.
✓ Branch 6 taken 290664 times.
871992 for (const auto& srTerm : srComb.terms)
616 {
617
2/4
✓ Branch 1 taken 581328 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 581328 times.
1162656 if (combsToRemove.contains(sendRequest.combIndex)) { continue; }
618
6/8
✓ Branch 0 taken 290664 times.
✓ Branch 1 taken 290664 times.
✓ Branch 3 taken 290664 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 290664 times.
✓ Branch 7 taken 290664 times.
✓ Branch 8 taken 290664 times.
581328 if (srTerm.pinIndex != term.pinIndex || srTerm.dataSelection != term.dataSelection) { continue; }
619 LOG_DATA("{}: [{:.3f}s] Term found in combination and term is {}", nameId(), math::round(calcTimeIntoRun(sendRequestTime), 8),
620 sendRequest.termIndices.contains(t) ? "already calculated." : "still missing");
621
622
2/4
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 290664 times.
290664 if (sendRequest.termIndices.contains(t)) { continue; } // The term was already calculated
623
624
1/2
✓ Branch 2 taken 290664 times.
✗ Branch 3 not taken.
290664 if (auto dt = static_cast<double>((nodeData->insTime - sendRequestTime).count()); // Out of bounds (do not interpolate)
625
2/4
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 290664 times.
✗ Branch 4 not taken.
290664 (_noOutputIfTimeDiffLarge && dt > _maxTimeDiffMultiplierFrequency * _pinData.at(pinIdx).minTimeStep)
626
5/8
✓ Branch 0 taken 290664 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 290664 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 290664 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 290658 times.
✓ Branch 8 taken 6 times.
871986 || (_noOutputIfTimeStepLarge && srTerm.rawData.full()
627
4/8
✓ Branch 1 taken 290658 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 290658 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 290658 times.
✗ Branch 10 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 290664 times.
581322 && static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count())
628
2/4
✓ Branch 1 taken 290658 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 290658 times.
290658 > _maxTimeStepMultiplierFrequency * _pinData.at(pinIdx).minTimeStep))
629 {
630 if (_outputMissingAsNaN)
631 {
632 sendRequest.result = std::nan("");
633 LOG_DATA("{}: Setting combination {} to NaN (({} && dt = {} > dt_min = {}) || ({} && dt_interp = {} > {}))", nameId(),
634 _combinations.at(sendRequest.combIndex).description(this),
635 _noOutputIfTimeDiffLarge, dt, _maxTimeDiffMultiplierFrequency * _pinData.at(pinIdx).minTimeStep,
636 _noOutputIfTimeStepLarge,
637 static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count()),
638 _maxTimeStepMultiplierFrequency * _pinData.at(pinIdx).minTimeStep);
639 }
640 else
641 {
642 combsToRemove.emplace(sendRequest.combIndex);
643 LOG_DATA("{}: Removing combination {} (({} && dt = {} > dt_min = {}) || ({} && dt_interp = {} > {}))", nameId(),
644 _combinations.at(sendRequest.combIndex).description(this),
645 _noOutputIfTimeDiffLarge, dt, _maxTimeDiffMultiplierFrequency * _pinData.at(pinIdx).minTimeStep,
646 _noOutputIfTimeStepLarge,
647 static_cast<double>((srTerm.rawData.back()->insTime - srTerm.rawData.front()->insTime).count()),
648 _maxTimeStepMultiplierFrequency * _pinData.at(pinIdx).minTimeStep);
649 continue;
650 }
651 }
652
653
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 auto poly = term.polyReg.calcPolynomial();
654 LOG_DATA("{}: Updating send request: {} += {:.2f} * {:.3g} (by interpolating to time [{:.3f}s])", nameId(),
655 sendRequest.result, term.factor, poly.f(math::round(calcTimeIntoRun(sendRequestTime), 8)),
656 math::round(calcTimeIntoRun(sendRequestTime), 8));
657
1/2
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
290664 sendRequest.termIndices.insert(t);
658
3/6
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 290664 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 290664 times.
✗ Branch 8 not taken.
290664 sendRequest.result += term.factor * poly.f(math::round(calcTimeIntoRun(sendRequestTime), 8));
659
660 290664 bool exactTimeFound = false;
661
6/12
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 290664 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 581316 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 290652 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 581316 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 581316 times.
✗ Branch 16 not taken.
871968 for (const auto& rawData : term.rawData)
662 {
663
2/2
✓ Branch 2 taken 290664 times.
✓ Branch 3 taken 290652 times.
581316 if (rawData->insTime == sendRequestTime)
664 {
665 LOG_DATA("{}: Adding rawData [{}] {}", nameId(),
666 rawData->insTime.toYMDHMS(GPST),
667 term.description(this, getDataDescriptors(term.pinIndex)));
668
669
3/6
✓ Branch 1 taken 290664 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 290664 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 290664 times.
✗ Branch 8 not taken.
290664 sendRequest.rawData.emplace_back(term.description(this, getDataDescriptors(term.pinIndex)),
670 rawData);
671 290664 exactTimeFound = true;
672 290664 break;
673 }
674 }
675
1/2
✓ Branch 0 taken 290664 times.
✗ Branch 1 not taken.
290664 if (exactTimeFound) { continue; }
676 for (const auto& rawData : term.rawData)
677 {
678 LOG_DATA("{}: Adding rawData [{}] {}", nameId(),
679 rawData->insTime.toYMDHMS(GPST),
680 term.description(this, getDataDescriptors(term.pinIndex)));
681
682 sendRequest.rawData.emplace_back(term.description(this, getDataDescriptors(term.pinIndex)),
683 rawData);
684 }
685 290664 }
686 }
687
1/2
✗ Branch 5 not taken.
✓ Branch 6 taken 290664 times.
290664 for (const auto& comb : combsToRemove)
688 {
689 std::erase_if(sendRequests, [&](const SendRequest& sr) { return sr.combIndex == comb; });
690 }
691 290664 }
692 }
693 }
694
695 48444 std::vector<InsTime> emptySendRequests;
696
2/2
✓ Branch 7 taken 48445 times.
✓ Branch 8 taken 48444 times.
96889 for (const auto& [sendRequestTime, sendRequests] : _sendRequests)
697 {
698
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 48445 times.
48445 if (sendRequests.empty())
699 {
700 LOG_DATA("{}: Discarding send request at [{}]", nameId(), sendRequestTime.toYMDHMS(GPST));
701 emptySendRequests.push_back(sendRequestTime);
702 }
703 }
704
1/4
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 48444 times.
48444 for (const auto& sendRequestTime : emptySendRequests) { _sendRequests.erase(sendRequestTime); }
705
706 48444 if (!_sendRequests.empty()) { LOG_DATA("{}: Send requests ({})", nameId(), _sendRequests.size()); }
707
2/2
✓ Branch 7 taken 48445 times.
✓ Branch 8 taken 48444 times.
96889 for (const auto& [sendRequestTime, sendRequests] : _sendRequests)
708 {
709 LOG_DATA("{}: [{:.3f}s] [{}]", nameId(), math::round(calcTimeIntoRun(sendRequestTime), 8), sendRequestTime.toYMDHMS(GPST));
710
2/2
✓ Branch 5 taken 290670 times.
✓ Branch 6 taken 48445 times.
339115 for (const auto& sendRequest : sendRequests)
711 {
712
1/2
✓ Branch 1 taken 290670 times.
✗ Branch 2 not taken.
290670 const auto& comb = _combinations.at(sendRequest.combIndex);
713 LOG_DATA("{}: Combination: {}", nameId(), comb.description(this));
714
2/2
✓ Branch 1 taken 581340 times.
✓ Branch 2 taken 290670 times.
872010 for (size_t t = 0; t < comb.terms.size(); ++t)
715 {
716 LOG_DATA("{}: Term '{}' is {}", nameId(), comb.terms.at(t).description(this, getDataDescriptors(comb.terms.at(t).pinIndex)),
717 sendRequest.termIndices.contains(t) ? "added" : "missing");
718 }
719 }
720 }
721
722 LOG_DATA("{}: [{:.3f}s] Checking wether a send request can be sent ({} requests)", nameId(), nodeDataTimeIntoRun, _sendRequests.size());
723 48444 std::vector<InsTime> requestsToRemove;
724
2/2
✓ Branch 7 taken 48445 times.
✓ Branch 8 taken 24221 times.
72666 for (const auto& [sendRequestTime, sendRequests] : _sendRequests)
725 {
726 LOG_DATA("{}: [{:.3f}s] [{}]", nameId(), math::round(calcTimeIntoRun(sendRequestTime), 8), sendRequestTime.toYMDHMS(GPST));
727 LOG_DATA("{}: Combinations (all terms in all combinations must be calculated)", nameId());
728
3/4
✓ Branch 1 taken 48445 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 24222 times.
✓ Branch 4 taken 24223 times.
48445 if (std::ranges::all_of(sendRequests, [&](const auto& sendRequest) {
729 169555 const auto& comb = _combinations.at(sendRequest.combIndex);
730 LOG_DATA("{}: '{}' has {}/{} terms set", nameId(), comb.description(this), sendRequest.termIndices.size(), comb.terms.size());
731 169555 return comb.terms.size() == sendRequest.termIndices.size();
732 }))
733 {
734 // If all combinations for this send request epoch are calculated, send it out
735
1/2
✓ Branch 1 taken 24222 times.
✗ Branch 2 not taken.
24222 auto dynData = std::make_shared<DynamicData>();
736 24222 dynData->insTime = sendRequestTime;
737 LOG_DATA("{}: Sending out dynamic data at [{}] and deleting send request", nameId(), dynData->insTime.toYMDHMS(GPST));
738
739
2/2
✓ Branch 5 taken 145332 times.
✓ Branch 6 taken 24222 times.
169554 for (const auto& sendRequest : sendRequests)
740 {
741
1/2
✓ Branch 1 taken 145332 times.
✗ Branch 2 not taken.
145332 const auto& comb = _combinations.at(sendRequest.combIndex);
742 DynamicData::Data data{
743 .description = comb.description(this),
744 145332 .value = sendRequest.result,
745 145332 .rawData = sendRequest.rawData
746
2/6
✓ Branch 1 taken 145332 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 145332 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
145332 };
747 LOG_DATA("{}: {} includes raw data", nameId(), data.description);
748
2/2
✓ Branch 5 taken 290664 times.
✓ Branch 6 taken 145332 times.
435996 for ([[maybe_unused]] const auto& raw : data.rawData)
749 {
750 LOG_DATA("{}: [{}] from '{}'", nameId(), raw.second->insTime.toYMDHMS(GPST), raw.first);
751 }
752
1/2
✓ Branch 2 taken 145332 times.
✗ Branch 3 not taken.
145332 dynData->data.push_back(data);
753 145332 }
754
755
1/2
✓ Branch 2 taken 24222 times.
✗ Branch 3 not taken.
24222 invokeCallbacks(OUTPUT_PORT_INDEX_DYN_DATA, dynData);
756
757
1/2
✓ Branch 1 taken 24222 times.
✗ Branch 2 not taken.
24222 requestsToRemove.push_back(sendRequestTime);
758 24222 }
759 else
760 {
761 // If not, break, because we always have to send out the first request first
762 24223 break;
763 }
764 }
765
2/2
✓ Branch 5 taken 24222 times.
✓ Branch 6 taken 48444 times.
72666 for (const auto& insTime : requestsToRemove)
766 {
767
1/2
✓ Branch 1 taken 24222 times.
✗ Branch 2 not taken.
24222 _sendRequests.erase(insTime);
768 }
769 48444 }
770
771 } // namespace NAV
772