INSTINCT Code Coverage Report


Directory: src/
File: Nodes/Utility/Demo.cpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 31 300 10.3%
Functions: 4 23 17.4%
Branches: 46 504 9.1%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 #include "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