INSTINCT Code Coverage Report


Directory: src/
File: Nodes/Utility/Combiner.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 166 387 42.9%
Functions: 15 22 68.2%
Branches: 208 896 23.2%

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