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 "Demo.hpp" | ||
10 | |||
11 | #include "util/Logger.hpp" | ||
12 | |||
13 | #include "internal/NodeManager.hpp" | ||
14 | namespace nm = NAV::NodeManager; | ||
15 | #include "internal/FlowManager.hpp" | ||
16 | |||
17 | #include "internal/gui/widgets/HelpMarker.hpp" | ||
18 | #include "internal/gui/widgets/Matrix.hpp" | ||
19 | #include "internal/gui/widgets/imgui_ex.hpp" | ||
20 | |||
21 | #include "NodeData/IMU/ImuObs.hpp" | ||
22 | |||
23 | #include <chrono> | ||
24 | #include <thread> | ||
25 | #include <random> | ||
26 | |||
27 | namespace NAV | ||
28 | { | ||
29 | namespace | ||
30 | { | ||
31 | ✗ | InsTime getCurrentInsTime() | |
32 | { | ||
33 | ✗ | std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); | |
34 | ✗ | auto* t = std::localtime(&now); // NOLINT(concurrency-mt-unsafe) | |
35 | |||
36 | ✗ | return { static_cast<uint16_t>(t->tm_year + 1900), | |
37 | ✗ | static_cast<uint16_t>(t->tm_mon), | |
38 | ✗ | static_cast<uint16_t>(t->tm_mday), | |
39 | ✗ | static_cast<uint16_t>(t->tm_hour), | |
40 | ✗ | static_cast<uint16_t>(t->tm_min), | |
41 | ✗ | static_cast<long double>(t->tm_sec) }; | |
42 | } | ||
43 | } // namespace | ||
44 | |||
45 | /// @brief Write info to a json object | ||
46 | /// @param[out] j Json output | ||
47 | /// @param[in] data Object to read info from | ||
48 | ✗ | static void to_json(json& j, const Demo::DemoData& data) // NOLINT(misc-use-anonymous-namespace) | |
49 | { | ||
50 | ✗ | j = json{ | |
51 | ✗ | { "boolean", data.boolean }, | |
52 | ✗ | { "integer", data.integer }, | |
53 | ✗ | }; | |
54 | ✗ | } | |
55 | /// @brief Read info from a json object | ||
56 | /// @param[in] j Json variable to read info from | ||
57 | /// @param[out] data Output object | ||
58 | ✗ | static void from_json(const json& j, Demo::DemoData& data) // NOLINT(misc-use-anonymous-namespace) | |
59 | { | ||
60 | ✗ | if (j.contains("boolean")) | |
61 | { | ||
62 | ✗ | j.at("boolean").get_to(data.boolean); | |
63 | } | ||
64 | ✗ | if (j.contains("integer")) | |
65 | { | ||
66 | ✗ | j.at("integer").get_to(data.integer); | |
67 | } | ||
68 | ✗ | } | |
69 | |||
70 | } // namespace NAV | ||
71 | |||
72 | 112 | NAV::Demo::Demo() | |
73 |
5/10✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
✓ Branch 9 taken 112 times.
✗ Branch 10 not taken.
✓ Branch 13 taken 112 times.
✗ Branch 14 not taken.
✓ Branch 16 taken 112 times.
✗ Branch 17 not taken.
|
336 | : Node(typeStatic()) |
74 | { | ||
75 | LOG_TRACE("{}: called", name); | ||
76 | |||
77 | 112 | _onlyRealTime = false; // Set this to true if you have a sensor, network stream, ... | |
78 | 112 | _hasConfig = true; | |
79 | 112 | _lockConfigDuringRun = false; | |
80 | 112 | _guiConfigDefaultWindowSize = { 630, 410 }; | |
81 | |||
82 |
4/8✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 112 times.
✓ Branch 10 taken 112 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
448 | nm::CreateOutputPin(this, "", Pin::Type::Delegate, { typeStatic() }, this); |
83 |
4/8✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 112 times.
✓ Branch 10 taken 112 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
448 | nm::CreateOutputPin(this, "Sensor\nData", Pin::Type::Flow, { NAV::ImuObs::type() }); |
84 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateOutputPin(this, "Bool", Pin::Type::Bool, { "" }, &_valueBool); |
85 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateOutputPin(this, "Int", Pin::Type::Int, { "" }, &_valueInt); |
86 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateOutputPin(this, "Float", Pin::Type::Float, { "" }, &_valueFloat); |
87 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateOutputPin(this, "Double", Pin::Type::Float, { "" }, &_valueDouble); |
88 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateOutputPin(this, "String", Pin::Type::String, { "" }, &_valueString); |
89 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateOutputPin(this, "Object", Pin::Type::Object, { "Demo::DemoData" }, &_valueObject); |
90 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateOutputPin(this, "Matrix", Pin::Type::Matrix, { "Eigen::MatrixXd" }, &_valueMatrix); |
91 | |||
92 |
4/8✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 112 times.
✓ Branch 10 taken 112 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
|
448 | nm::CreateInputPin(this, "Demo Node", Pin::Type::Delegate, { typeStatic() }); |
93 |
4/8✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 112 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 112 times.
✓ Branch 9 taken 112 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
|
336 | nm::CreateInputPin(this, "Flow", Pin::Type::Flow, { NAV::NodeData::type() }, &Demo::receiveData); |
94 |
1/2✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
|
112 | nm::CreateInputPin(this, "Bool", Pin::Type::Bool); |
95 |
1/2✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
|
112 | nm::CreateInputPin(this, "Int", Pin::Type::Int); |
96 |
1/2✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
|
112 | nm::CreateInputPin(this, "Float", Pin::Type::Float); |
97 |
1/2✓ Branch 4 taken 112 times.
✗ Branch 5 not taken.
|
112 | nm::CreateInputPin(this, "Double", Pin::Type::Float); |
98 |
1/2✓ Branch 3 taken 112 times.
✗ Branch 4 not taken.
|
112 | nm::CreateInputPin(this, "String", Pin::Type::String, {}, &Demo::stringUpdatedNotifyFunction); |
99 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateInputPin(this, "Object", Pin::Type::Object, { "Demo::DemoData" }); |
100 |
2/4✓ Branch 2 taken 112 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 112 times.
✗ Branch 7 not taken.
|
224 | nm::CreateInputPin(this, "Matrix", Pin::Type::Matrix, { "Eigen::MatrixXd" }); |
101 | 560 | } | |
102 | |||
103 | 224 | NAV::Demo::~Demo() | |
104 | { | ||
105 | LOG_TRACE("{}: called", nameId()); | ||
106 | 224 | } | |
107 | |||
108 | 448 | std::string NAV::Demo::typeStatic() | |
109 | { | ||
110 |
1/2✓ Branch 1 taken 448 times.
✗ Branch 2 not taken.
|
896 | return "Demo"; |
111 | } | ||
112 | |||
113 | ✗ | std::string NAV::Demo::type() const | |
114 | { | ||
115 | ✗ | return typeStatic(); | |
116 | } | ||
117 | |||
118 | 112 | std::string NAV::Demo::category() | |
119 | { | ||
120 |
1/2✓ Branch 1 taken 112 times.
✗ Branch 2 not taken.
|
224 | return "Utility"; |
121 | } | ||
122 | |||
123 | ✗ | void NAV::Demo::guiConfig() | |
124 | { | ||
125 | ✗ | if (ImGui::BeginTable("##DemoValues", 2, ImGuiTableFlags_Borders)) | |
126 | { | ||
127 | ✗ | ImGui::TableSetupColumn("Input"); | |
128 | ✗ | ImGui::TableSetupColumn("Output"); | |
129 | ✗ | ImGui::TableHeadersRow(); | |
130 | |||
131 | /* ----------------------------------------------- Delegate ----------------------------------------------- */ | ||
132 | ✗ | ImGui::TableNextColumn(); | |
133 | { | ||
134 | // The returned type automatically blocks editing on the other side of the link. Like a scoped_lock for mutexes | ||
135 | ✗ | auto connectedNode = getInputValue<Demo>(INPUT_PORT_INDEX_DEMO_NODE); | |
136 | ✗ | ImGui::Text("Delegate: %s", connectedNode ? connectedNode->v->nameId().c_str() : "N/A"); | |
137 | ✗ | } | |
138 | ✗ | ImGui::TableNextColumn(); | |
139 | /* ------------------------------------------------ Flow ------------------------------------------------ */ | ||
140 | ✗ | ImGui::TableNextColumn(); | |
141 | ✗ | ImGui::Text("Flow Data Count: %d", _receivedDataCnt); | |
142 | ✗ | ImGui::TableNextColumn(); | |
143 | ✗ | if (ImGui::Checkbox("Simulate File Reader", &_fileReaderInsteadSensor)) | |
144 | { | ||
145 | ✗ | if (_fileReaderInsteadSensor) | |
146 | { | ||
147 | ✗ | if (_timer.is_running()) { _timer.stop(); } | |
148 | } | ||
149 | else | ||
150 | { | ||
151 | ✗ | if (isInitialized() && !_timer.is_running()) | |
152 | { | ||
153 | ✗ | int outputInterval = static_cast<int>(1.0 / static_cast<double>(_outputFrequency) * 1000.0); | |
154 | ✗ | _timer.start(outputInterval, readSensorDataThread, this); | |
155 | } | ||
156 | } | ||
157 | ✗ | updateOutputFlowPin(); | |
158 | ✗ | flow::ApplyChanges(); | |
159 | } | ||
160 | ✗ | if (_fileReaderInsteadSensor) | |
161 | { | ||
162 | ✗ | ImGui::SetNextItemWidth(100.0F); | |
163 | ✗ | if (ImGui::InputInt("FileReader Obs Count", &_nPollData)) | |
164 | { | ||
165 | ✗ | flow::ApplyChanges(); | |
166 | } | ||
167 | } | ||
168 | else | ||
169 | { | ||
170 | ✗ | if (ImGui::SliderInt("Frequency", &_outputFrequency, 1, 10)) | |
171 | { | ||
172 | ✗ | int outputInterval = static_cast<int>(1.0 / static_cast<double>(_outputFrequency) * 1000.0); | |
173 | ✗ | _timer.setInterval(outputInterval); | |
174 | ✗ | flow::ApplyChanges(); | |
175 | } | ||
176 | } | ||
177 | /* ------------------------------------------------- Bool ------------------------------------------------- */ | ||
178 | ✗ | ImGui::TableNextColumn(); | |
179 | { | ||
180 | ✗ | auto connectedBool = getInputValue<bool>(INPUT_PORT_INDEX_BOOL); | |
181 | ✗ | ImGui::Text("Bool: %s", connectedBool ? (connectedBool->v ? "true" : "false") : "N/A"); | |
182 | ✗ | } | |
183 | ✗ | ImGui::TableNextColumn(); | |
184 | { | ||
185 | ✗ | auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_BOOL); | |
186 | ✗ | if (ImGui::Checkbox("Bool", &_valueBool)) | |
187 | { | ||
188 | ✗ | flow::ApplyChanges(); | |
189 | } | ||
190 | ✗ | } | |
191 | |||
192 | /* -------------------------------------------------- Int ------------------------------------------------- */ | ||
193 | ✗ | ImGui::TableNextColumn(); | |
194 | ✗ | if (auto connectedInt = getInputValue<int>(INPUT_PORT_INDEX_INT)) | |
195 | { | ||
196 | ✗ | ImGui::Text("Int: %d", *connectedInt->v); | |
197 | } | ||
198 | else | ||
199 | { | ||
200 | ✗ | ImGui::TextUnformatted("Int: N/A"); | |
201 | ✗ | } | |
202 | ✗ | ImGui::TableNextColumn(); | |
203 | { | ||
204 | ✗ | auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_INT); | |
205 | ✗ | if (ImGui::InputInt("Int", &_valueInt)) // Returns true if a change was made | |
206 | { | ||
207 | // Limit the values to [-2,5] | ||
208 | ✗ | _valueInt = std::max(_valueInt, -2); | |
209 | ✗ | _valueInt = std::min(_valueInt, 5); | |
210 | |||
211 | ✗ | flow::ApplyChanges(); | |
212 | } | ||
213 | ✗ | } | |
214 | /* ------------------------------------------------- Float ------------------------------------------------ */ | ||
215 | ✗ | ImGui::TableNextColumn(); | |
216 | ✗ | if (auto connectedFloat = getInputValue<float>(INPUT_PORT_INDEX_FLOAT)) | |
217 | { | ||
218 | ✗ | ImGui::Text("Float: %.3f", *connectedFloat->v); | |
219 | } | ||
220 | else | ||
221 | { | ||
222 | ✗ | ImGui::TextUnformatted("Float: N/A"); | |
223 | ✗ | } | |
224 | ✗ | ImGui::TableNextColumn(); | |
225 | { | ||
226 | ✗ | auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_FLOAT); | |
227 | ✗ | if (ImGui::DragFloat("Float", &_valueFloat)) | |
228 | { | ||
229 | ✗ | flow::ApplyChanges(); | |
230 | } | ||
231 | ✗ | } | |
232 | /* ------------------------------------------------ Double ------------------------------------------------ */ | ||
233 | ✗ | ImGui::TableNextColumn(); | |
234 | ✗ | if (auto connectedDouble = getInputValue<double>(INPUT_PORT_INDEX_DOUBLE)) | |
235 | { | ||
236 | ✗ | ImGui::Text("Double : %.3f", *connectedDouble->v); | |
237 | } | ||
238 | else | ||
239 | { | ||
240 | ✗ | ImGui::TextUnformatted("Double: N/A"); | |
241 | ✗ | } | |
242 | ✗ | ImGui::TableNextColumn(); | |
243 | { | ||
244 | ✗ | auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_DOUBLE); | |
245 | ✗ | if (ImGui::DragDouble("Double", &_valueDouble)) | |
246 | { | ||
247 | ✗ | flow::ApplyChanges(); | |
248 | } | ||
249 | ✗ | } | |
250 | /* ------------------------------------------------ String ------------------------------------------------ */ | ||
251 | ✗ | ImGui::TableNextColumn(); | |
252 | ✗ | if (auto connectedString = getInputValue<std::string>(INPUT_PORT_INDEX_STRING)) | |
253 | { | ||
254 | ✗ | ImGui::Text("String: %s", connectedString->v->c_str()); | |
255 | } | ||
256 | else | ||
257 | { | ||
258 | ✗ | ImGui::TextUnformatted("String: N/A"); | |
259 | ✗ | } | |
260 | ✗ | ImGui::Text("The String was updated %lu time%s", _stringUpdateCounter, _stringUpdateCounter > 1 || _stringUpdateCounter == 0 ? "s" : ""); | |
261 | ✗ | ImGui::TableNextColumn(); | |
262 | { | ||
263 | // Before accessing and changing the value. A lock has to be requested to ensure it is not changed before all linked nodes received the value. | ||
264 | ✗ | auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_STRING); | |
265 | ✗ | if (ImGui::InputText("String", &_valueString)) | |
266 | { | ||
267 | ✗ | flow::ApplyChanges(); | |
268 | ✗ | const auto& outputPin = outputPins[OUTPUT_PORT_INDEX_STRING]; | |
269 | ✗ | if (outputPin.isPinLinked()) | |
270 | { | ||
271 | ✗ | if (!isInitialized()) { LOG_WARN("{}: Notifying connected nodes requires this node to be initialized.", nameId()); } | |
272 | ✗ | else if (!callbacksEnabled) { LOG_WARN("{}: Notifying connected nodes requires enabled callbacks on this node.", nameId()); } | |
273 | ✗ | else if (std::ranges::none_of(outputPin.links, [](const OutputPin::OutgoingLink& link) { return link.connectedNode->isInitialized(); })) | |
274 | { | ||
275 | ✗ | LOG_WARN("{}: Notifying connected nodes requires at least one connected node to be initialized.", nameId()); | |
276 | } | ||
277 | } | ||
278 | ✗ | notifyOutputValueChanged(OUTPUT_PORT_INDEX_STRING, getCurrentInsTime(), guard); | |
279 | } | ||
280 | ✗ | } | |
281 | ✗ | ImGui::SameLine(); | |
282 | ✗ | gui::widgets::HelpMarker("The string notifies about changes.\nInitialize both nodes for this to work."); | |
283 | /* ------------------------------------------------ Object ------------------------------------------------ */ | ||
284 | ✗ | ImGui::TableNextColumn(); | |
285 | ✗ | if (auto connectedObject = getInputValue<DemoData>(INPUT_PORT_INDEX_DEMO_DATA)) | |
286 | { | ||
287 | ✗ | ImGui::Text("Object: [%d, %d, %d], %s", connectedObject->v->integer.at(0), connectedObject->v->integer.at(1), connectedObject->v->integer.at(2), | |
288 | ✗ | connectedObject->v->boolean ? "true" : "false"); | |
289 | } | ||
290 | else | ||
291 | { | ||
292 | ✗ | ImGui::TextUnformatted("Object: N/A"); | |
293 | ✗ | } | |
294 | ✗ | ImGui::TableNextColumn(); | |
295 | { | ||
296 | ✗ | auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_DEMO_DATA); | |
297 | ✗ | if (ImGui::InputInt3("", _valueObject.integer.data())) | |
298 | { | ||
299 | ✗ | flow::ApplyChanges(); | |
300 | } | ||
301 | ✗ | ImGui::SameLine(); | |
302 | ✗ | if (ImGui::Checkbox("Object", &_valueObject.boolean)) | |
303 | { | ||
304 | ✗ | flow::ApplyChanges(); | |
305 | } | ||
306 | ✗ | } | |
307 | /* ------------------------------------------------ Matrix ------------------------------------------------ */ | ||
308 | ✗ | ImGui::TableNextColumn(); | |
309 | ✗ | if (auto connectedMatrix = getInputValue<Eigen::MatrixXd>(INPUT_PORT_INDEX_MATRIX)) | |
310 | { | ||
311 | ✗ | gui::widgets::MatrixView("Current Matrix", connectedMatrix->v, GuiMatrixViewFlags_Header, ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit, "%.1f"); | |
312 | } | ||
313 | else | ||
314 | { | ||
315 | ✗ | ImGui::TextUnformatted("Matrix: N/A"); | |
316 | ✗ | } | |
317 | ✗ | ImGui::TableNextColumn(); | |
318 | { | ||
319 | ✗ | auto guard = requestOutputValueLock(OUTPUT_PORT_INDEX_MATRIX); | |
320 | ✗ | if (gui::widgets::InputMatrix("Init Matrix", &_valueMatrix, GuiMatrixViewFlags_Header, ImGuiTableFlags_Borders | ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_SizingFixedFit, 30.0F, 0.0, 0.0, "%.1f")) | |
321 | { | ||
322 | ✗ | flow::ApplyChanges(); | |
323 | } | ||
324 | ✗ | } | |
325 | |||
326 | ✗ | ImGui::EndTable(); | |
327 | } | ||
328 | ✗ | } | |
329 | |||
330 | ✗ | [[nodiscard]] json NAV::Demo::save() const | |
331 | { | ||
332 | LOG_TRACE("{}: called", nameId()); | ||
333 | |||
334 | ✗ | json j; | |
335 | |||
336 | return { | ||
337 | ✗ | { "outputFrequency", _outputFrequency }, | |
338 | ✗ | { "nPollData", _nPollData }, | |
339 | ✗ | { "valueBool", _valueBool }, | |
340 | ✗ | { "valueInt", _valueInt }, | |
341 | ✗ | { "valueFloat", _valueFloat }, | |
342 | ✗ | { "valueDouble", _valueDouble }, | |
343 | ✗ | { "valueString", _valueString }, | |
344 | ✗ | { "valueObject", _valueObject }, | |
345 | ✗ | { "valueMatrix", _valueMatrix }, | |
346 | ✗ | { "fileReaderInsteadSensor", _fileReaderInsteadSensor }, | |
347 | ✗ | }; | |
348 | ✗ | } | |
349 | |||
350 | ✗ | void NAV::Demo::restore(json const& j) | |
351 | { | ||
352 | LOG_TRACE("{}: called", nameId()); | ||
353 | |||
354 | ✗ | if (j.contains("outputFrequency")) { j.at("outputFrequency").get_to(_outputFrequency); } | |
355 | ✗ | if (j.contains("nPollData")) { j.at("nPollData").get_to(_nPollData); } | |
356 | ✗ | if (j.contains("valueBool")) { j.at("valueBool").get_to(_valueBool); } | |
357 | ✗ | if (j.contains("valueInt")) { j.at("valueInt").get_to(_valueInt); } | |
358 | ✗ | if (j.contains("valueFloat")) { j.at("valueFloat").get_to(_valueFloat); } | |
359 | ✗ | if (j.contains("valueDouble")) { j.at("valueDouble").get_to(_valueDouble); } | |
360 | ✗ | if (j.contains("valueString")) { j.at("valueString").get_to(_valueString); } | |
361 | ✗ | if (j.contains("valueObject")) { j.at("valueObject").get_to(_valueObject); } | |
362 | ✗ | if (j.contains("valueMatrix")) { j.at("valueMatrix").get_to(_valueMatrix); } | |
363 | ✗ | if (j.contains("fileReaderInsteadSensor")) | |
364 | { | ||
365 | ✗ | j.at("fileReaderInsteadSensor").get_to(_fileReaderInsteadSensor); | |
366 | ✗ | updateOutputFlowPin(); | |
367 | } | ||
368 | ✗ | } | |
369 | |||
370 | ✗ | bool NAV::Demo::initialize() | |
371 | { | ||
372 | LOG_TRACE("{}: called", nameId()); | ||
373 | |||
374 | // To Show the Initialization in the GUI | ||
375 | ✗ | std::this_thread::sleep_for(std::chrono::milliseconds(2000)); | |
376 | |||
377 | ✗ | _receivedDataCnt = 0; | |
378 | |||
379 | ✗ | _stringUpdateCounter = 0; | |
380 | |||
381 | ✗ | if (!_fileReaderInsteadSensor) | |
382 | { | ||
383 | ✗ | int outputInterval = static_cast<int>(1.0 / static_cast<double>(_outputFrequency) * 1000.0); | |
384 | ✗ | _timer.start(outputInterval, readSensorDataThread, this); | |
385 | } | ||
386 | |||
387 | ✗ | return true; | |
388 | } | ||
389 | |||
390 | ✗ | void NAV::Demo::deinitialize() | |
391 | { | ||
392 | LOG_TRACE("{}: called", nameId()); | ||
393 | |||
394 | ✗ | if (_timer.is_running()) | |
395 | { | ||
396 | ✗ | _timer.stop(); | |
397 | } | ||
398 | |||
399 | // To Show the Deinitialization in the GUI | ||
400 | ✗ | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |
401 | ✗ | } | |
402 | |||
403 | ✗ | bool NAV::Demo::resetNode() | |
404 | { | ||
405 | LOG_TRACE("{}: called", nameId()); | ||
406 | // Here you could reset a FileReader | ||
407 | ✗ | _iPollData = 0; | |
408 | ✗ | _receivedDataCnt = 0; | |
409 | |||
410 | ✗ | return true; | |
411 | } | ||
412 | |||
413 | ✗ | void NAV::Demo::updateOutputFlowPin() | |
414 | { | ||
415 | ✗ | std::vector<ax::NodeEditor::PinId> connectedPins; | |
416 | ✗ | for (const auto& link : outputPins.at(OUTPUT_PORT_INDEX_FLOW).links) | |
417 | { | ||
418 | ✗ | connectedPins.push_back(link.connectedPinId); | |
419 | } | ||
420 | ✗ | nm::DeleteOutputPin(outputPins.at(OUTPUT_PORT_INDEX_FLOW)); | |
421 | ✗ | if (_fileReaderInsteadSensor) | |
422 | { | ||
423 | ✗ | nm::CreateOutputPin(this, "FileReader\n Data", Pin::Type::Flow, { NAV::NodeData::type() }, &Demo::pollData, OUTPUT_PORT_INDEX_FLOW); | |
424 | } | ||
425 | else | ||
426 | { | ||
427 | ✗ | nm::CreateOutputPin(this, "Sensor\nData", Pin::Type::Flow, { NAV::ImuObs::type() }, static_cast<void*>(nullptr), OUTPUT_PORT_INDEX_FLOW); | |
428 | } | ||
429 | ✗ | for (const auto& pinId : connectedPins) | |
430 | { | ||
431 | ✗ | if (auto* targetPin = nm::FindInputPin(pinId)) | |
432 | { | ||
433 | ✗ | if (outputPins.at(OUTPUT_PORT_INDEX_FLOW).canCreateLink(*targetPin)) | |
434 | { | ||
435 | ✗ | outputPins.at(OUTPUT_PORT_INDEX_FLOW).createLink(*targetPin); | |
436 | } | ||
437 | } | ||
438 | } | ||
439 | ✗ | } | |
440 | |||
441 | ✗ | bool NAV::Demo::onCreateLink(OutputPin& startPin, [[maybe_unused]] InputPin& endPin) | |
442 | { | ||
443 | LOG_TRACE("{}: called for {} ==> {}", nameId(), size_t(startPin.id), size_t(endPin.id)); | ||
444 | |||
445 | ✗ | if (startPin.id == outputPins.at(OUTPUT_PORT_INDEX_FLOW).id) | |
446 | { | ||
447 | ✗ | _onlyRealTime = !_fileReaderInsteadSensor; | |
448 | } | ||
449 | |||
450 | ✗ | return true; | |
451 | } | ||
452 | |||
453 | ✗ | void NAV::Demo::onDeleteLink(OutputPin& startPin, [[maybe_unused]] InputPin& endPin) | |
454 | { | ||
455 | LOG_TRACE("{}: called for {} ==> {}", nameId(), size_t(startPin.id), size_t(endPin.id)); | ||
456 | |||
457 | ✗ | if (startPin.id == outputPins.at(OUTPUT_PORT_INDEX_FLOW).id) | |
458 | { | ||
459 | ✗ | _onlyRealTime = false; | |
460 | } | ||
461 | ✗ | } | |
462 | |||
463 | ✗ | void NAV::Demo::receiveData(NAV::InputPin::NodeDataQueue& queue, size_t /* pinIdx */) | |
464 | { | ||
465 | ✗ | std::shared_ptr<const NAV::NodeData> obs = queue.extract_front(); // Either 'extract_front()' or 'pop_front()' needs to be called | |
466 | ✗ | _receivedDataCnt++; | |
467 | |||
468 | ✗ | LOG_DEBUG("{}: received {} data at [{} GPST]", nameId(), _receivedDataCnt, obs->insTime.toYMDHMS(GPST)); | |
469 | ✗ | } | |
470 | |||
471 | ✗ | void NAV::Demo::readSensorDataThread(void* userData) | |
472 | { | ||
473 | ✗ | auto* node = static_cast<Demo*>(userData); | |
474 | |||
475 | ✗ | if (!node->outputPins.at(OUTPUT_PORT_INDEX_FLOW).isPinLinked() || !node->callbacksEnabled) | |
476 | { | ||
477 | ✗ | return; | |
478 | } | ||
479 | |||
480 | ✗ | if (node->getMode() == Mode::POST_PROCESSING) | |
481 | { | ||
482 | ✗ | LOG_WARN("{}: Flow contains nodes which can only do post-processing. Sensor output is suppressed."); | |
483 | |||
484 | ✗ | return; | |
485 | } | ||
486 | |||
487 | ✗ | auto imuPos = ImuPos(); | |
488 | ✗ | auto obs = std::make_shared<ImuObs>(imuPos); | |
489 | |||
490 | ✗ | std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); | |
491 | ✗ | auto* t = std::localtime(&now); // NOLINT(concurrency-mt-unsafe) | |
492 | |||
493 | ✗ | obs->insTime = InsTime(static_cast<uint16_t>(t->tm_year + 1900), | |
494 | ✗ | static_cast<uint16_t>(t->tm_mon) + 1, | |
495 | ✗ | static_cast<uint16_t>(t->tm_mday), | |
496 | ✗ | static_cast<uint16_t>(t->tm_hour), | |
497 | ✗ | static_cast<uint16_t>(t->tm_min), | |
498 | ✗ | static_cast<long double>(t->tm_sec)); | |
499 | |||
500 | ✗ | std::random_device rd; | |
501 | ✗ | std::default_random_engine generator(rd()); | |
502 | |||
503 | ✗ | std::uniform_real_distribution<double> distribution(-9.0, 9.0); | |
504 | ✗ | obs->p_acceleration = Eigen::Vector3d(distribution(generator), distribution(generator), distribution(generator)); | |
505 | |||
506 | ✗ | distribution = std::uniform_real_distribution<double>(-3.0, 3.0); | |
507 | ✗ | obs->p_angularRate = Eigen::Vector3d(distribution(generator), distribution(generator), distribution(generator)); | |
508 | |||
509 | ✗ | distribution = std::uniform_real_distribution<double>(-1.0, 1.0); | |
510 | ✗ | obs->p_magneticField = Eigen::Vector3d(distribution(generator), distribution(generator), distribution(generator)); | |
511 | |||
512 | ✗ | distribution = std::uniform_real_distribution<double>(15.0, 25.0); | |
513 | ✗ | obs->temperature = distribution(generator); | |
514 | |||
515 | ✗ | LOG_INFO("{}: Sending Sensor data with time [{} GPST]", node->nameId(), obs->insTime.toYMDHMS(GPST)); | |
516 | |||
517 | ✗ | node->invokeCallbacks(OUTPUT_PORT_INDEX_FLOW, obs); | |
518 | ✗ | } | |
519 | |||
520 | ✗ | std::shared_ptr<const NAV::NodeData> NAV::Demo::peekPollData(bool peek) | |
521 | { | ||
522 | // This function is only an example of how to implement peek/poll logic. It is not used in this node. | ||
523 | ✗ | if (_iPollData >= _nPollData) | |
524 | { | ||
525 | ✗ | return nullptr; | |
526 | } | ||
527 | |||
528 | ✗ | if (peek) // Early return with time to let the Node sort the observations | |
529 | { | ||
530 | ✗ | auto obs = std::make_shared<NodeData>(); // Construct the real observation (here in example also from type NodeData) | |
531 | ✗ | obs->insTime = InsTime(2000, 1, 1, 0, 0, _iPollData); | |
532 | ✗ | return obs; | |
533 | ✗ | } | |
534 | |||
535 | ✗ | auto obs = std::make_shared<NodeData>(); // Construct the real observation (here in example also from type NodeData) | |
536 | ✗ | obs->insTime = InsTime(2000, 1, 1, 0, 0, _iPollData); | |
537 | |||
538 | ✗ | _iPollData++; | |
539 | // Calls all the callbacks | ||
540 | ✗ | invokeCallbacks(OUTPUT_PORT_INDEX_FLOW, obs); | |
541 | |||
542 | ✗ | return obs; | |
543 | ✗ | } | |
544 | |||
545 | ✗ | std::shared_ptr<const NAV::NodeData> NAV::Demo::pollData() | |
546 | { | ||
547 | ✗ | if (_iPollData >= _nPollData) | |
548 | { | ||
549 | ✗ | return nullptr; // Tells the node that the last message was read | |
550 | } | ||
551 | |||
552 | ✗ | auto obs = std::make_shared<NodeData>(); // Construct the real observation (here in example also from type NodeData) | |
553 | ✗ | obs->insTime = InsTime(2000, 1, 1, 0, 0, _iPollData); | |
554 | |||
555 | ✗ | _iPollData++; | |
556 | |||
557 | ✗ | invokeCallbacks(OUTPUT_PORT_INDEX_FLOW, obs); // Calls all the callbacks | |
558 | ✗ | return obs; | |
559 | ✗ | } | |
560 | |||
561 | ✗ | void NAV::Demo::stringUpdatedNotifyFunction([[maybe_unused]] const InsTime& insTime, size_t pinIdx) // TODO: This does not work | |
562 | { | ||
563 | ✗ | _stringUpdateCounter++; | |
564 | |||
565 | ✗ | if (auto value = getInputValue<std::string>(pinIdx)) | |
566 | { | ||
567 | ✗ | LOG_DEBUG("String value updated to '{}' at time {}", *value->v, insTime); | |
568 | ✗ | } | |
569 | ✗ | } | |
570 |