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 |