INSTINCT Code Coverage Report


Directory: src/
File: internal/Node/Node.hpp
Date: 2025-06-02 15:19:59
Exec Total Coverage
Lines: 18 29 62.1%
Functions: 6 11 54.5%
Branches: 3 18 16.7%

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 /// @file Node.hpp
10 /// @brief Node Class
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2020-12-14
13
14 #pragma once
15
16 // <boost/asio.hpp> needs to be included before <winsock.h> (even though not used in this file)
17 // https://stackoverflow.com/questions/9750344/boostasio-winsock-and-winsock-2-compatibility-issue
18 #ifdef _WIN32
19 // Set the proper SDK version before including boost/Asio
20 #include <SDKDDKVer.h>
21 // Note boost/ASIO includes Windows.h.
22 #include <boost/asio.hpp>
23 #endif //_WIN32
24
25 #include <imgui.h>
26 #include <imgui_node_editor.h>
27 #include <imgui_stdlib.h>
28
29 #include "internal/Node/Pin.hpp"
30 #include "Navigation/Time/InsTime.hpp"
31
32 #include <string>
33 #include <vector>
34 #include <thread>
35 #include <mutex>
36 #include <condition_variable>
37 #include <atomic>
38 #include <chrono>
39 #include <map>
40
41 #include <nlohmann/json.hpp>
42 using json = nlohmann::json; ///< json namespace
43
44 namespace NAV
45 {
46 class Node;
47 class NodeData;
48 class GroupBox;
49
50 namespace NodeRegistry
51 {
52
53 void RegisterNodeTypes(); // NOLINT(readability-redundant-declaration) - false warning. This is needed for the friend declaration below
54
55 } // namespace NodeRegistry
56
57 namespace FlowExecutor
58 {
59
60 /// @brief Main task of the FlowExecutor thread
61 void execute(); // NOLINT(readability-redundant-declaration) - false warning. This is needed for the friend declaration below
62
63 /// @brief Deinitialize all Nodes
64 void deinitialize(); // NOLINT(readability-redundant-declaration) - false warning. This is needed for the friend declaration below
65
66 } // namespace FlowExecutor
67
68 namespace gui
69 {
70 class NodeEditorApplication;
71
72 namespace menus
73 {
74
75 void ShowRunMenu();
76
77 } // namespace menus
78
79 } // namespace gui
80
81 /// @brief Converts the provided node into a json object
82 /// @param[out] j Json object which gets filled with the info
83 /// @param[in] node Node to convert into json
84 void to_json(json& j, const Node& node);
85 /// @brief Converts the provided json object into a node object
86 /// @param[in] j Json object with the needed values
87 /// @param[out] node Object to fill from the json
88 void from_json(const json& j, Node& node);
89
90 /// @brief Abstract parent class for all nodes
91 class Node
92 {
93 public:
94 /// Kind information class
95 struct Kind
96 {
97 /// Possible kinds of Nodes
98 enum Value : uint8_t
99 {
100 Blueprint, ///< Node with header
101 Simple, ///< Node without header, which displays its name in the center of the content
102 GroupBox, ///< Group box which can group other nodes and drag them together
103 };
104
105 /// @brief Default Constructor
106 Kind() = default;
107
108 /// @brief Implicit Constructor from Value type
109 /// @param[in] kind Value type to construct from
110 6612 constexpr Kind(Value kind) // NOLINT(hicpp-explicit-conversions, google-explicit-constructor)
111 6612 : value(kind)
112 6612 {}
113
114 /// @brief Constructor from std::string
115 /// @param[in] string String representation of the type
116 680 explicit Kind(const std::string& string)
117 680 {
118
2/2
✓ Branch 1 taken 580 times.
✓ Branch 2 taken 100 times.
680 if (string == "Blueprint")
119 {
120 580 value = Kind::Blueprint;
121 }
122
1/2
✓ Branch 1 taken 100 times.
✗ Branch 2 not taken.
100 else if (string == "Simple")
123 {
124 100 value = Kind::Simple;
125 }
126 else if (string == "GroupBox")
127 {
128 value = Kind::GroupBox;
129 }
130 680 }
131
132 /// @brief Allow switch(Node::Value(kind)) and comparisons
133 explicit operator Value() const { return value; }
134 /// @brief Prevent usage: if(node)
135 explicit operator bool() = delete;
136 /// @brief Assignment operator from Value type
137 /// @param[in] v Value type to construct from
138 /// @return The Kind type from the value type
139 387 Kind& operator=(Value v)
140 {
141 387 value = v;
142 387 return *this;
143 }
144
145 friend constexpr bool operator==(const Node::Kind& lhs, const Node::Kind& rhs);
146 friend constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind& rhs);
147
148 friend constexpr bool operator==(const Node::Kind& lhs, const Node::Kind::Value& rhs);
149 friend constexpr bool operator==(const Node::Kind::Value& lhs, const Node::Kind& rhs);
150 friend constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind::Value& rhs);
151 friend constexpr bool operator!=(const Node::Kind::Value& lhs, const Node::Kind& rhs);
152
153 /// @brief std::string conversion operator
154 /// @return A std::string representation of the node kind
155 explicit operator std::string() const
156 {
157 switch (value)
158 {
159 case Kind::Blueprint:
160 return "Blueprint";
161 case Kind::Simple:
162 return "Simple";
163 case Kind::GroupBox:
164 return "GroupBox";
165 }
166 return "";
167 }
168
169 private:
170 /// @brief Value of the node kind
171 Value value;
172 };
173
174 /// @brief Possible states of the node
175 enum class State : uint8_t
176 {
177 Disabled, ///< Node is disabled and won't be initialized
178 Deinitialized, ///< Node is deinitialized (red)
179 DoInitialize, ///< Node should be initialized
180 Initializing, ///< Node is currently initializing
181 Initialized, ///< Node is initialized (green)
182 DoDeinitialize, ///< Node should be deinitialized
183 Deinitializing, ///< Node is currently deinitializing
184 DoShutdown, ///< Node should shut down
185 Shutdown, ///< Node is shutting down
186 };
187
188 /// @brief Different Modes the Node can work in
189 enum class Mode : uint8_t
190 {
191 REAL_TIME, ///< Node running in real-time mode
192 POST_PROCESSING, ///< Node running in post-processing mode
193 };
194
195 /// @brief Constructor
196 /// @param[in] name Name of the node
197 explicit Node(std::string name);
198 /// @brief Destructor
199 virtual ~Node();
200 /// @brief Copy constructor
201 Node(const Node&) = delete;
202 /// @brief Move constructor
203 Node(Node&&) = delete;
204 /// @brief Copy assignment operator
205 Node& operator=(const Node&) = delete;
206 /// @brief Move assignment operator
207 Node& operator=(Node&&) = delete;
208
209 /* -------------------------------------------------------------------------------------------------------- */
210 /* Interface */
211 /* -------------------------------------------------------------------------------------------------------- */
212
213 /// @brief String representation of the Class Type
214 [[nodiscard]] virtual std::string type() const = 0;
215
216 /// @brief ImGui config window which is shown on double click
217 /// @attention Don't forget to set hasConfig to true
218 virtual void guiConfig();
219
220 /// @brief Saves the node into a json object
221 [[nodiscard]] virtual json save() const;
222
223 /// @brief Restores the node from a json object
224 /// @param[in] j Json object with the node state
225 virtual void restore(const json& j);
226
227 /// @brief Restores link related properties of the node from a json object
228 /// @param[in] j Json object with the node state
229 virtual void restoreAtferLink(const json& j);
230
231 /// @brief Initialize the Node
232 ///
233 /// Here a node can do things like
234 /// - connecting to a sensor
235 /// - read a static data file which needs to be read completely (e.g. RINEX nav files)
236 /// - reset variables in case the NAV::Node::resetNode() is not used
237 /// @attention This function is called in the default implementation of the NAV::Node::resetNode() function.
238 /// If the initialize function should not be called more than once, make sure to override the NAV::Node::resetNode() function.
239 virtual bool initialize();
240
241 /// @brief Deinitialize the Node
242 virtual void deinitialize();
243
244 /// @brief Resets the node. It is guaranteed that the node is initialized when this is called.
245 virtual bool resetNode();
246
247 /// @brief Called when a new link is to be established
248 /// @param[in] startPin Pin where the link starts
249 /// @param[in] endPin Pin where the link ends
250 /// @return True if link is allowed, false if link is rejected
251 virtual bool onCreateLink(OutputPin& startPin, InputPin& endPin);
252
253 /// @brief Called when a link is to be deleted
254 /// @param[in] startPin Pin where the link starts
255 /// @param[in] endPin Pin where the link ends
256 virtual void onDeleteLink(OutputPin& startPin, InputPin& endPin);
257
258 /// @brief Called when a new link was established
259 /// @param[in] startPin Pin where the link starts
260 /// @param[in] endPin Pin where the link ends
261 virtual void afterCreateLink(OutputPin& startPin, InputPin& endPin);
262
263 /// @brief Called when a link was deleted
264 /// @param[in] startPin Pin where the link starts
265 /// @param[in] endPin Pin where the link ends
266 virtual void afterDeleteLink(OutputPin& startPin, InputPin& endPin);
267
268 /// @brief Function called by the flow executer after finishing to flush out remaining data
269 virtual void flush();
270
271 /* -------------------------------------------------------------------------------------------------------- */
272 /* Member functions */
273 /* -------------------------------------------------------------------------------------------------------- */
274
275 /// @brief Notifies connected nodes about the change
276 /// @param[in] pinIdx Output Port index where to set the value
277 /// @param[in] insTime Time the value was generated
278 /// @param[in] guard Lock guard of the output data
279 void notifyOutputValueChanged(size_t pinIdx, const InsTime& insTime, const std::scoped_lock<std::mutex>&& guard);
280
281 /// @brief Blocks the thread till the output values was read by all connected nodes
282 /// @param[in] pinIdx Output Pin index where to request the lock
283 [[nodiscard]] std::scoped_lock<std::mutex> requestOutputValueLock(size_t pinIdx);
284
285 /// @brief Get Input Value connected on the pin. Only const data types.
286 /// @tparam T Type of the connected object
287 /// @param[in] portIndex Input port where to retrieve the data from
288 /// @return Pointer to the object
289 template<typename T>
290 637 [[nodiscard]] std::optional<InputPin::IncomingLink::ValueWrapper<T>> getInputValue(size_t portIndex) const
291 {
292 637 return inputPins.at(portIndex).link.getValue<T>();
293 }
294
295 /// @brief Unblocks the connected node. Has to be called when the input value should be released and getInputValue was not called.
296 /// @param[in] portIndex Input port where the data should be released
297 void releaseInputValue(size_t portIndex);
298
299 /// @brief Checks wether there is an input pin with the same time
300 /// @param[in] insTime Time to check
301 bool hasInputPinWithSameTime(const InsTime& insTime) const;
302
303 /// @brief Calls all registered callbacks on the specified output port
304 /// @param[in] portIndex Output port where to call the callbacks
305 /// @param[in] data The data to pass to the callback targets
306 void invokeCallbacks(size_t portIndex, const std::shared_ptr<const NodeData>& data);
307
308 /// @brief Returns the pin with the given id
309 /// @param[in] pinId Id of the Pin
310 /// @return The input pin
311 [[nodiscard]] InputPin& inputPinFromId(ax::NodeEditor::PinId pinId);
312
313 /// @brief Returns the pin with the given id
314 /// @param[in] pinId Id of the Pin
315 /// @return The output pin
316 [[nodiscard]] OutputPin& outputPinFromId(ax::NodeEditor::PinId pinId);
317
318 /// @brief Returns the index of the pin
319 /// @param[in] pinId Id of the Pin
320 /// @return The index of the pin
321 [[nodiscard]] size_t inputPinIndexFromId(ax::NodeEditor::PinId pinId) const;
322
323 /// @brief Returns the index of the pin
324 /// @param[in] pinId Id of the Pin
325 /// @return The index of the pin
326 [[nodiscard]] size_t outputPinIndexFromId(ax::NodeEditor::PinId pinId) const;
327
328 /// @brief Node name and id
329 [[nodiscard]] std::string nameId() const;
330
331 /// @brief Get the size of the node
332 [[nodiscard]] const ImVec2& getSize() const;
333
334 // ------------------------------------------ State handling ---------------------------------------------
335
336 /// @brief Converts the state into a printable text
337 /// @param[in] state State to convert
338 /// @return String representation of the state
339 static std::string toString(State state);
340
341 /// @brief Get the current state of the node
342 [[nodiscard]] State getState() const;
343
344 /// @brief Get the current mode of the node
345 [[nodiscard]] Mode getMode() const;
346
347 /// @brief Asks the node worker to initialize the node
348 /// @param[in] wait Wait for the worker to complete the request
349 /// @return True if not waiting and the worker accepted the request otherwise if waiting only true if the node initialized correctly
350 bool doInitialize(bool wait = false);
351
352 /// @brief Asks the node worker to reinitialize the node
353 /// @param[in] wait Wait for the worker to complete the request
354 /// @return True if not waiting and the worker accepted the request otherwise if waiting only true if the node initialized correctly
355 bool doReinitialize(bool wait = false);
356
357 /// @brief Asks the node worker to deinitialize the node
358 /// @param[in] wait Wait for the worker to complete the request
359 /// @return True if the worker accepted the request
360 bool doDeinitialize(bool wait = false);
361
362 /// @brief Asks the node worker to disable the node
363 /// @param[in] wait Wait for the worker to complete the request
364 /// @return True if the worker accepted the request
365 bool doDisable(bool wait = false);
366
367 /// @brief Enable the node
368 /// @return True if enabling was successful
369 bool doEnable();
370
371 /// Wakes the worker thread
372 void wakeWorker();
373
374 /// @brief Checks if the node is disabled
375 [[nodiscard]] bool isDisabled() const;
376
377 /// @brief Checks if the node is initialized
378 [[nodiscard]] bool isInitialized() const;
379
380 /// @brief Checks if the node is changing its state currently
381 [[nodiscard]] bool isTransient() const;
382
383 /// @brief Checks if the node is only working in real time (sensors, network interfaces, ...)
384 [[nodiscard]] bool isOnlyRealtime() const;
385
386 /* -------------------------------------------------------------------------------------------------------- */
387 /* Member variables */
388 /* -------------------------------------------------------------------------------------------------------- */
389
390 /// Unique Id of the Node
391 ax::NodeEditor::NodeId id = 0;
392 /// Kind of the Node
393 Kind kind = Kind::Blueprint;
394 /// Name of the Node
395 std::string name;
396 /// List of input pins
397 std::vector<InputPin> inputPins;
398 /// List of output pins
399 std::vector<OutputPin> outputPins;
400
401 /// Enables the callbacks
402 bool callbacksEnabled = false;
403
404 /// Map with callback events (sorted by time)
405 std::multimap<InsTime, std::pair<OutputPin*, size_t>> pollEvents;
406
407 protected:
408 /// The Default Window size for new config windows.
409 /// Only set the variable if the object/window has no persistently saved data (no entry in .ini file)
410 ImVec2 _guiConfigDefaultWindowSize{ 500.0F, 400.0F };
411
412 /// Flag if the config window should be shown
413 bool _hasConfig = false;
414
415 /// Lock the config when executing post-processing
416 bool _lockConfigDuringRun = true;
417
418 /// Whether the node can run in post-processing or only real-time
419 bool _onlyRealTime = false;
420
421 private:
422 State _state = State::Deinitialized; ///< Current state of the node
423 mutable std::mutex _stateMutex; ///< Mutex to interact with the worker state variable
424
425 /// Mode the node is currently running in
426 std::atomic<Mode> _mode = Mode::REAL_TIME;
427
428 /// Flag if the node should be reinitialize after deinitializing
429 bool _reinitialize = false;
430
431 /// Flag if the node should be disabled after deinitializing
432 bool _disable = false;
433
434 /// Flag if the config window is shown
435 bool _showConfig = false;
436
437 /// Mutex to show the config window (prevents initialization to modify values within the config window)
438 std::mutex _configWindowMutex;
439 /// Flag if the config window should be forced collapsed
440 bool _configWindowForceCollapse = false;
441 /// Flag if the config window is collapsed
442 bool _configWindowIsCollapsed = false;
443
444 /// Flag if the config window should be focused
445 bool _configWindowFocus = false;
446
447 /// Size of the node in pixels
448 ImVec2 _size{ 0, 0 };
449
450 /// Flag which prevents the worker to be autostarted if false
451 static inline bool _autostartWorker = true;
452
453 6612 std::chrono::duration<int64_t> _workerTimeout = std::chrono::minutes(1); ///< Periodic timeout of the worker to check if new data available
454 std::thread _worker; ///< Worker handling initialization and processing of data
455 std::mutex _workerMutex; ///< Mutex to interact with the worker condition variable
456 std::condition_variable _workerConditionVariable; ///< Condition variable to signal the worker thread to do something
457 bool _workerWakeup = false; ///< Variable to prevent the worker from sleeping
458
459 /// @brief Worker thread
460 /// @param[in, out] node The node where the thread belongs to
461 static void workerThread(Node* node);
462
463 /// Handler which gets triggered if the worker runs into a periodic timeout
464 virtual void workerTimeoutHandler();
465
466 /// @brief Called by the worker to initialize the node
467 /// @return True if the initialization was successful
468 bool workerInitializeNode();
469
470 /// @brief Called by the worker to deinitialize the node
471 /// @return True if the deinitialization was successful
472 bool workerDeinitializeNode();
473
474 friend class gui::NodeEditorApplication;
475 friend class NAV::GroupBox;
476
477 /// @brief Main task of the FlowExecutor thread
478 friend void NAV::FlowExecutor::execute();
479 /// @brief Deinitialize all Nodes
480 friend void NAV::FlowExecutor::deinitialize();
481 /// @brief Register all available Node types for the program
482 friend void NAV::NodeRegistry::RegisterNodeTypes();
483
484 /// @brief Converts the provided node into a json object
485 /// @param[out] j Json object which gets filled with the info
486 /// @param[in] node Node to convert into json
487 friend void NAV::to_json(json& j, const Node& node);
488 /// @brief Converts the provided json object into a node object
489 /// @param[in] j Json object with the needed values
490 /// @param[out] node Object to fill from the json
491 friend void NAV::from_json(const json& j, Node& node);
492
493 /// @brief Show the run menu dropdown
494 friend void gui::menus::ShowRunMenu();
495 };
496
497 /// @brief Equal compares Node::Kind values
498 /// @param[in] lhs Left-hand side of the operator
499 /// @param[in] rhs Right-hand side of the operator
500 /// @return Whether the comparison was successful
501 constexpr bool operator==(const Node::Kind& lhs, const Node::Kind& rhs) { return lhs.value == rhs.value; }
502 /// @brief Inequal compares Node::Kind values
503 /// @param[in] lhs Left-hand side of the operator
504 /// @param[in] rhs Right-hand side of the operator
505 /// @return Whether the comparison was successful
506 constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind& rhs) { return !(lhs == rhs); }
507
508 /// @brief Equal compares Node::Kind values
509 /// @param[in] lhs Left-hand side of the operator
510 /// @param[in] rhs Right-hand side of the operator
511 /// @return Whether the comparison was successful
512 3429 constexpr bool operator==(const Node::Kind& lhs, const Node::Kind::Value& rhs) { return lhs.value == rhs; }
513 /// @brief Equal compares Node::Kind values
514 /// @param[in] lhs Left-hand side of the operator
515 /// @param[in] rhs Right-hand side of the operator
516 /// @return Whether the comparison was successful
517 constexpr bool operator==(const Node::Kind::Value& lhs, const Node::Kind& rhs) { return lhs == rhs.value; }
518 /// @brief Inequal compares Node::Kind values
519 /// @param[in] lhs Left-hand side of the operator
520 /// @param[in] rhs Right-hand side of the operator
521 /// @return Whether the comparison was successful
522 2031 constexpr bool operator!=(const Node::Kind& lhs, const Node::Kind::Value& rhs) { return !(lhs == rhs); }
523 /// @brief Inequal compares Node::Kind values
524 /// @param[in] lhs Left-hand side of the operator
525 /// @param[in] rhs Right-hand side of the operator
526 /// @return Whether the comparison was successful
527 constexpr bool operator!=(const Node::Kind::Value& lhs, const Node::Kind& rhs) { return !(lhs == rhs); }
528
529 } // namespace NAV
530