INSTINCT Code Coverage Report


Directory: src/
File: internal/Node/Node.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 20 29 69.0%
Functions: 6 11 54.5%
Branches: 5 18 27.8%

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