INSTINCT Code Coverage Report


Directory: src/
File: internal/Node/Pin.hpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 82 134 61.2%
Functions: 29 89 32.6%
Branches: 22 80 27.5%

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 Pin.hpp
10 /// @brief Pin class
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2020-12-14
13
14 #pragma once
15
16 #include <imgui_node_editor.h>
17
18 #include <nlohmann/json.hpp>
19 using json = nlohmann::json; ///< json namespace
20
21 #include <string>
22 #include <variant>
23 #include <vector>
24 #include <memory>
25 #include <tuple>
26 #include <mutex>
27 #include <atomic>
28 #include <condition_variable>
29
30 #include "util/Logger.hpp"
31 #include "util/Container/TsDeque.hpp"
32 #include "Navigation/Time/InsTime.hpp"
33
34 namespace NAV
35 {
36 class Node;
37 class NodeData;
38 class InputPin;
39 class OutputPin;
40
41 /// @brief Pins in the GUI for information exchange
42 class Pin
43 {
44 public:
45 /// @brief Type of the data on the Pin
46 struct Type
47 {
48 /// @brief Type of the data on the Pin
49 enum Value : uint8_t
50 {
51 None, ///< Not initialized
52 Flow, ///< NodeData Trigger
53 Bool, ///< Boolean
54 Int, ///< Integer Number
55 Float, ///< Floating Point Number
56 String, ///< std::string
57 Object, ///< Generic Object
58 Matrix, ///< Matrix Object
59 Delegate, ///< Reference to the Node object
60 };
61
62 /// @brief Default Constructor
63 constexpr Type() = default;
64
65 /// @brief Implicit Constructor from Value type
66 /// @param[in] type Value type to construct from
67 13716 constexpr Type(Value type) // NOLINT(hicpp-explicit-conversions, google-explicit-constructor)
68 13716 : value(type)
69 13716 {}
70
71 /// @brief Constructor from std::string
72 /// @param[in] typeString String representation of the type
73 explicit Type(const std::string& typeString)
74 {
75 if (typeString == "Flow")
76 {
77 value = Type::Flow;
78 }
79 else if (typeString == "Bool")
80 {
81 value = Type::Bool;
82 }
83 else if (typeString == "Int")
84 {
85 value = Type::Int;
86 }
87 else if (typeString == "Float")
88 {
89 value = Type::Float;
90 }
91 else if (typeString == "String")
92 {
93 value = Type::String;
94 }
95 else if (typeString == "Object")
96 {
97 value = Type::Object;
98 }
99 else if (typeString == "Matrix")
100 {
101 value = Type::Matrix;
102 }
103 else if (typeString == "Delegate")
104 {
105 value = Type::Delegate;
106 }
107 }
108
109 /// @brief Allow switch(Node::Value(type)) and comparisons
110 explicit operator Value() const { return value; }
111 /// @brief Prevent usage: if(pin)
112 explicit operator bool() = delete;
113 /// @brief Assignment operator from Value type
114 /// @param[in] v Value type to construct from
115 /// @return The Type type from the value type
116 219 Type& operator=(Value v)
117 {
118 219 value = v;
119 219 return *this;
120 }
121
122 friend constexpr bool operator==(const Pin::Type& lhs, const Pin::Type& rhs);
123 friend constexpr bool operator!=(const Pin::Type& lhs, const Pin::Type& rhs);
124
125 friend constexpr bool operator==(const Pin::Type& lhs, const Pin::Type::Value& rhs);
126 friend constexpr bool operator==(const Pin::Type::Value& lhs, const Pin::Type& rhs);
127 friend constexpr bool operator!=(const Pin::Type& lhs, const Pin::Type::Value& rhs);
128 friend constexpr bool operator!=(const Pin::Type::Value& lhs, const Pin::Type& rhs);
129
130 /// @brief std::string conversion operator
131 /// @return A std::string representation of the pin type
132 explicit operator std::string() const
133 {
134 switch (value)
135 {
136 case Type::None:
137 return "None";
138 case Type::Flow:
139 return "Flow";
140 case Type::Bool:
141 return "Bool";
142 case Type::Int:
143 return "Int";
144 case Type::Float:
145 return "Float";
146 case Type::String:
147 return "String";
148 case Type::Object:
149 return "Object";
150 case Type::Matrix:
151 return "Matrix";
152 case Type::Delegate:
153 return "Delegate";
154 }
155 return "";
156 }
157
158 private:
159 /// @brief Value of the pin type
160 Value value = Value::None;
161 };
162
163 /// Kind of the Pin (Input/Output)
164 struct Kind
165 {
166 /// @brief Kind of the Pin (Input/Output)
167 enum Value : uint8_t
168 {
169 None, ///< None
170 Output, ///< Output Pin
171 Input, ///< Input Pin
172 };
173
174 /// @brief Default Constructor
175 Kind() = default;
176
177 /// @brief Implicit Constructor from Value type
178 /// @param[in] kind Value type to construct from
179 13716 constexpr Kind(Value kind) // NOLINT(hicpp-explicit-conversions, google-explicit-constructor)
180 13716 : value(kind)
181 13716 {}
182
183 /// @brief Constructor from std::string
184 /// @param[in] kindString String representation of the type
185 explicit Kind(const std::string& kindString)
186 {
187 if (kindString == "Input")
188 {
189 value = Kind::Input;
190 }
191 else if (kindString == "Output")
192 {
193 value = Kind::Output;
194 }
195 }
196
197 /// @brief Allow switch(Node::Value(kind)) and comparisons
198 explicit operator Value() const { return value; }
199 /// @brief Prevent usage: if(pin)
200 explicit operator bool() = delete;
201 /// @brief Assignment operator from Value type
202 /// @param[in] v Value type to construct from
203 /// @return The Kind type from the value type
204 Kind& operator=(Value v)
205 {
206 value = v;
207 return *this;
208 }
209
210 friend constexpr bool operator==(const Pin::Kind& lhs, const Pin::Kind& rhs);
211 friend constexpr bool operator!=(const Pin::Kind& lhs, const Pin::Kind& rhs);
212
213 friend constexpr bool operator==(const Pin::Kind& lhs, const Pin::Kind::Value& rhs);
214 friend constexpr bool operator==(const Pin::Kind::Value& lhs, const Pin::Kind& rhs);
215 friend constexpr bool operator!=(const Pin::Kind& lhs, const Pin::Kind::Value& rhs);
216 friend constexpr bool operator!=(const Pin::Kind::Value& lhs, const Pin::Kind& rhs);
217
218 /// @brief std::string conversion operator
219 /// @return A std::string representation of the pin kind
220 explicit operator std::string() const
221 {
222 switch (value)
223 {
224 case Kind::None:
225 return "None";
226 case Kind::Input:
227 return "Input";
228 case Kind::Output:
229 return "Output";
230 }
231 }
232
233 private:
234 /// @brief Value of the pin kind
235 Value value = Value::None;
236 };
237
238 /// Link between two pins
239 struct Link
240 {
241 ax::NodeEditor::LinkId linkId = 0; ///< Unique id of the link
242 Node* connectedNode = nullptr; ///< Pointer to the node, which is connected to this pin
243 ax::NodeEditor::PinId connectedPinId = 0; ///< Id of the pin, which is connected to this pin
244
245 protected:
246 /// @brief Default Constructor
247 5856 Link() = default;
248
249 /// @brief Constructor
250 /// @param[in] linkId Unique id of the link
251 /// @param[in] connectedNode Node connected on the other end of the link
252 /// @param[in] connectedPinId Id of the pin, which is connected on the other end of the link
253 270 Link(ax::NodeEditor::LinkId linkId,
254 Node* connectedNode,
255 ax::NodeEditor::PinId connectedPinId)
256 270 : linkId(linkId), connectedNode(connectedNode), connectedPinId(connectedPinId) {}
257 };
258
259 // /// Callback function type to call when firable
260 // using OldFlowCallback = void (Node::*)(const std::shared_ptr<const NodeData>&, ax::NodeEditor::LinkId);
261 // /// Notify function type to call when the connected value changed
262 // using OldNotifyFunc = void (Node::*)(ax::NodeEditor::LinkId);
263 // /// FileReader pollData function type
264 // using OldPollDataFunc = std::shared_ptr<const NAV::NodeData> (Node::*)(bool);
265
266 /// @brief Constructor
267 /// @param[in] id Unique Id of the Pin
268 /// @param[in] name Name of the Pin
269 /// @param[in] type Type of the Pin
270 /// @param[in] kind Kind of the Pin (Input/Output)
271 /// @param[in] parentNode Reference to the parent node
272 12636 Pin(ax::NodeEditor::PinId id, const char* name, Type type, Kind kind, Node* parentNode)
273
1/2
✓ Branch 1 taken 12636 times.
✗ Branch 2 not taken.
37908 : id(id), name(name), type(type), kind(kind), parentNode(parentNode) {}
274
275 /// @brief Checks if pins can connect
276 /// @param[in] startPin The start pin to create a link to
277 /// @param[in] endPin The end pin to create a link to
278 /// @return True if the pins can create a link
279 [[nodiscard]] static bool canCreateLink(const OutputPin& startPin, const InputPin& endPin);
280
281 /// @brief Checks if the first list of data identifiers has a common entry with the second
282 /// @param[in] a First list of data identifiers
283 /// @param[in] b Second list of data identifiers
284 /// @return True if they have a common entry
285 [[nodiscard]] static bool dataIdentifierHaveCommon(const std::vector<std::string>& a, const std::vector<std::string>& b);
286
287 /// @brief Get the Icon Color object
288 /// @return Color struct
289 [[nodiscard]] ImColor getIconColor() const;
290
291 /// @brief Draw the Pin Icon
292 /// @param[in] connected Flag if the pin is connected
293 /// @param[in] alpha Alpha value of the pin
294 void drawPinIcon(bool connected, int alpha) const;
295
296 /// Unique Id of the Pin
297 ax::NodeEditor::PinId id;
298 /// Name of the Pin
299 std::string name;
300 /// Type of the Pin
301 Type type = Type::None;
302 /// Kind of the Pin (Input/Output)
303 Kind kind = Kind::None;
304 /// One or multiple Data Identifiers (Unique name which is used for data flows)
305 std::vector<std::string> dataIdentifier;
306 /// Reference to the parent node
307 Node* parentNode = nullptr;
308
309 protected:
310 /// @brief Default constructor
311 1080 Pin() = default;
312
313 /// @brief Create a Link between the two given pins
314 /// @param[in] startPin Start Pin of the link
315 /// @param[in] endPin End Pin of the link
316 /// @param[in] linkId Id of the link to create
317 /// @return True if the link could be created
318 static bool createLink(OutputPin& startPin, InputPin& endPin, ax::NodeEditor::LinkId linkId = 0);
319
320 /// @brief Destroys and recreates a link from this pin to another
321 /// @param[in] startPin Start Pin of the link
322 /// @param[in] endPin End Pin of the link
323 /// @return True if the link could be created
324 static bool recreateLink(OutputPin& startPin, InputPin& endPin);
325
326 /// @brief Disconnects the link
327 /// @param[in] startPin Start Pin of the link
328 /// @param[in] endPin End Pin of the link
329 static void deleteLink(OutputPin& startPin, InputPin& endPin);
330
331 private:
332 /// Size of the Pin Icons in [px]
333 static constexpr int m_PinIconSize = 24;
334 };
335
336 /// Output pins of nodes
337 class OutputPin : public Pin
338 {
339 public:
340 /// @brief Constructor
341 /// @param[in] id Unique Id of the Pin
342 /// @param[in] name Name of the Pin
343 /// @param[in] type Type of the Pin
344 /// @param[in] parentNode Reference to the parent node
345 7324 OutputPin(ax::NodeEditor::PinId id, const char* name, Type type, Node* parentNode)
346
1/2
✓ Branch 2 taken 7324 times.
✗ Branch 3 not taken.
7324 : Pin(id, name, type, Pin::Kind::Output, parentNode) {}
347
348 /// @brief Default constructor (for serialization)
349 536 OutputPin() = default;
350 /// @brief Destructor
351 12123 ~OutputPin() = default;
352 /// @brief Copy constructor
353 OutputPin(const OutputPin&) = delete;
354 /// @brief Move constructor
355 4264 OutputPin(OutputPin&& other) noexcept
356 4264 : Pin(std::move(other)),
357 4264 links(std::move(other.links)), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
358 4264 data(other.data), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
359 4264 noMoreDataAvailable(other.noMoreDataAvailable.load()), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
360 12792 blocksConnectedNodeFromFinishing(other.blocksConnectedNodeFromFinishing.load()) // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
361 4264 {}
362 /// @brief Copy assignment operator
363 OutputPin& operator=(const OutputPin&) = delete;
364 /// @brief Move assignment operator
365 OutputPin& operator=(OutputPin&& other) noexcept
366 {
367 if (this != &other)
368 {
369 links = std::move(other.links);
370 data = other.data;
371 noMoreDataAvailable = other.noMoreDataAvailable.load();
372 blocksConnectedNodeFromFinishing = other.blocksConnectedNodeFromFinishing.load();
373 Pin::operator=(std::move(other));
374 }
375 return *this;
376 }
377
378 /// @brief Checks if this pin can connect to the provided pin
379 /// @param[in] other The pin to create a link to
380 /// @return True if it can create a link
381 [[nodiscard]] bool canCreateLink(const InputPin& other) const;
382
383 /// @brief Checks if the pin is linked
384 /// @return True if a link exists on this pin
385 [[nodiscard]] bool isPinLinked() const;
386
387 /// @brief Checks if the pin is linked to the other pin
388 /// @param[in] endPin The pin to check if they are linked
389 /// @return True if a link exists on the pins
390 [[nodiscard]] bool isPinLinked(const InputPin& endPin) const;
391
392 /// @brief Creates a link from this pin to another, calling all node specific callbacks
393 /// @param[in] endPin Pin which should be linked to this pin
394 /// @param[in] linkId Id of the link to create
395 /// @return True if the link could be created
396 bool createLink(InputPin& endPin, ax::NodeEditor::LinkId linkId = 0);
397
398 /// @brief Destroys and recreates a link from this pin to another
399 /// @param[in] endPin Pin which should be linked to this pin
400 /// @return True if the link could be created
401 bool recreateLink(InputPin& endPin);
402
403 /// @brief Disconnects the link
404 /// @param[in] endPin Pin which should be linked to this pin
405 void deleteLink(InputPin& endPin);
406
407 /// @brief Disconnects all links
408 void deleteLinks();
409
410 /// Collection of information about the connected node and pin
411 struct OutgoingLink : public Link
412 {
413 /// @brief Default Constructor
414 OutgoingLink() = default;
415
416 /// @brief Constructor
417 /// @param[in] linkId Unique id of the link
418 /// @param[in] connectedNode Node connected on the other end of the link
419 /// @param[in] connectedPinId Id of the pin, which is connected on the other end of the link
420 270 OutgoingLink(ax::NodeEditor::LinkId linkId,
421 Node* connectedNode,
422 ax::NodeEditor::PinId connectedPinId)
423 270 : Link(linkId, connectedNode, connectedPinId) {}
424
425 /// @brief Returns a pointer to the pin which is connected to this one
426 [[nodiscard]] InputPin* getConnectedPin() const;
427
428 /// @brief Flag to signal the connected node, that the data was changed
429 bool dataChangeNotification = false;
430 };
431
432 /// Info to identify the linked pins
433 std::vector<OutgoingLink> links;
434
435 /// @brief FileReader/Simulator peekPollData function type for nodes with more than one polling pin
436 ///
437 /// - First parameter is the index of the pin in the outputPins vector
438 /// - Second parameter is a boolean 'peek'
439 /// This function gets called twice.
440 /// - First with 'peek = true': There an observation with a valid InsTime must be provided. `invokeCallbacks(...` should not be called.
441 /// - Second with 'peek = false': Here the message is read again and `invokeCallbacks(...)` should be called
442 using PeekPollDataFunc = std::shared_ptr<const NAV::NodeData> (Node::*)(size_t, bool);
443
444 /// FileReader/Simulator pollData function type for nodes with a single poll pin
445 using PollDataFunc = std::shared_ptr<const NAV::NodeData> (Node::*)();
446
447 /// @brief Possible Types represented by an output pin
448 using PinData = std::variant<const void*, // Object/Matrix/Delegate
449 const bool*, // Bool
450 const int*, // Int
451 const float*, // Float
452 const double*, // Float
453 const std::string*, // String
454 PeekPollDataFunc, // Flow (FileReader poll data function with peeking)
455 PollDataFunc>; // Flow (FileReader poll data function)
456
457 /// Pointer to data (owned by this node) which is transferred over this pin
458 PinData data = static_cast<void*>(nullptr);
459
460 /// Mutex to interact with the data object and also the dataAccessCounter variable
461 std::mutex dataAccessMutex;
462
463 /// @brief Counter for data accessing
464 size_t dataAccessCounter = 0;
465
466 /// Condition variable to signal that the data was read by connected nodes (used for non-flow pins)
467 std::condition_variable dataAccessConditionVariable;
468
469 /// Flag set, when no more data is available on this pin
470 std::atomic<bool> noMoreDataAvailable = true;
471
472 /// Flag, whether connected nodes can finish with this pin not being finished yet (needed to make a loop with nodes)
473 std::atomic<bool> blocksConnectedNodeFromFinishing = true;
474
475 friend class Pin;
476 friend class InputPin;
477
478 private:
479 /// @brief Connects this pin to another
480 /// @param[in] endPin Pin which should be linked to this pin
481 /// @param[in] linkId Id of the link to create
482 void connect(InputPin& endPin, ax::NodeEditor::LinkId linkId = 0);
483
484 /// @brief Disconnects the link
485 /// @param[in] endPin Pin which should be disconnected from this pin
486 void disconnect(InputPin& endPin);
487 };
488
489 /// Input pins of nodes
490 class InputPin : public Pin
491 {
492 public:
493 /// @brief Constructor
494 /// @param[in] id Unique Id of the Pin
495 /// @param[in] name Name of the Pin
496 /// @param[in] type Type of the Pin
497 /// @param[in] parentNode Reference to the parent node
498 5312 InputPin(ax::NodeEditor::PinId id, const char* name, Type type, Node* parentNode)
499
3/6
✓ Branch 2 taken 5312 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 5312 times.
✗ Branch 6 not taken.
✓ Branch 10 taken 5312 times.
✗ Branch 11 not taken.
5312 : Pin(id, name, type, Pin::Kind::Input, parentNode) {}
500
501 /// @brief Default constructor (for serialization)
502
2/4
✓ Branch 2 taken 544 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 544 times.
✗ Branch 8 not taken.
544 InputPin() = default;
503 /// @brief Destructor
504 8990 ~InputPin() = default;
505 /// @brief Copy constructor
506 InputPin(const InputPin&) = delete;
507 /// @brief Move constructor
508 3135 InputPin(InputPin&& other) noexcept
509 3135 : Pin(std::move(other)),
510 3135 link(other.link), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
511 3135 callback(other.callback), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
512 3135 firable(other.firable), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
513 3135 priority(other.priority), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
514 3135 neededForTemporalQueueCheck(other.neededForTemporalQueueCheck), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
515 3135 dropQueueIfNotFirable(other.dropQueueIfNotFirable), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
516 3135 queueBlocked(other.queueBlocked), // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
517 3135 queue(other.queue) // NOLINT(bugprone-use-after-move,hicpp-invalid-access-moved)
518 3135 {}
519 /// @brief Copy assignment operator
520 InputPin& operator=(const InputPin&) = delete;
521 /// @brief Move assignment operator
522 InputPin& operator=(InputPin&& other) noexcept
523 {
524 if (this != &other)
525 {
526 // copy if trivially-copyable, otherwise move
527 link = other.link;
528 callback = other.callback;
529 firable = other.firable;
530 priority = other.priority;
531 neededForTemporalQueueCheck = other.neededForTemporalQueueCheck;
532 dropQueueIfNotFirable = other.dropQueueIfNotFirable;
533 queueBlocked = other.queueBlocked;
534 queue = std::move(other.queue);
535 Pin::operator=(std::move(other));
536 }
537 return *this;
538 }
539
540 /// @brief Checks if this pin can connect to the provided pin
541 /// @param[in] other The pin to create a link to
542 /// @return True if it can create a link
543 [[nodiscard]] bool canCreateLink(const OutputPin& other) const;
544
545 /// @brief Checks if the pin is linked
546 /// @return True if a link exists on this pin
547 [[nodiscard]] bool isPinLinked() const;
548
549 /// @brief Creates a link from this pin to another, calling all node specific callbacks
550 /// @param[in] startPin Pin which should be linked to this pin
551 /// @param[in] linkId Id of the link to create
552 /// @return True if the link could be created
553 bool createLink(OutputPin& startPin, ax::NodeEditor::LinkId linkId = 0);
554
555 /// @brief Destroys and recreates a link from this pin to another
556 /// @param[in] startPin Pin which should be linked to this pin
557 /// @return True if the link could be created
558 bool recreateLink(OutputPin& startPin);
559
560 /// @brief Disconnects the link
561 void deleteLink();
562
563 /// Collection of information about the connected node and pin
564 struct IncomingLink : public Link
565 {
566 /// @brief Default Constructor
567 5856 IncomingLink() = default;
568
569 /// @brief Constructor
570 /// @param[in] linkId Unique id of the link
571 /// @param[in] connectedNode Node connected on the other end of the link
572 /// @param[in] connectedPinId Id of the pin, which is connected on the other end of the link
573 IncomingLink(ax::NodeEditor::LinkId linkId,
574 Node* connectedNode,
575 ax::NodeEditor::PinId connectedPinId)
576 : Link(linkId, connectedNode, connectedPinId) {}
577
578 /// @brief Returns a pointer to the pin which is connected to this one
579 [[nodiscard]] OutputPin* getConnectedPin() const;
580
581 /// @brief Value wrapper, automatically incrementing and decrementing the data access counter
582 template<typename T>
583 class ValueWrapper
584 {
585 public:
586 /// @brief Constructor
587 /// @param v Reference to the value to wrap
588 /// @param outputPin Output pin reference
589 /// @param dataChangeNotification Reference to the Data change notification of the link
590 701 ValueWrapper(const T* v, OutputPin* outputPin, bool& dataChangeNotification)
591 701 : v(v), outputPin(outputPin)
592 {
593
1/2
✓ Branch 1 taken 701 times.
✗ Branch 2 not taken.
701 std::scoped_lock guard(outputPin->dataAccessMutex);
594
1/2
✓ Branch 0 taken 701 times.
✗ Branch 1 not taken.
701 if (!dataChangeNotification)
595 {
596 701 outputPin->dataAccessCounter++;
597 }
598 701 dataChangeNotification = false; // We take 'ownership' of the incremented dataAccessCounter
599 701 }
600 /// @brief Desctructor
601 2250 ~ValueWrapper()
602 {
603
2/2
✓ Branch 0 taken 1402 times.
✓ Branch 1 taken 848 times.
2250 if (outputPin)
604 {
605 1402 std::scoped_lock guard(outputPin->dataAccessMutex);
606
1/2
✓ Branch 0 taken 1402 times.
✗ Branch 1 not taken.
1402 if (outputPin->dataAccessCounter > 0)
607 {
608 1402 outputPin->dataAccessCounter--;
609
2/2
✓ Branch 0 taken 701 times.
✓ Branch 1 taken 701 times.
1402 if (outputPin->dataAccessCounter == 0)
610 {
611 701 outputPin->dataAccessConditionVariable.notify_all();
612 }
613 }
614 1402 }
615 2250 }
616
617 /// @brief Copy constructor
618 701 ValueWrapper(const ValueWrapper& other)
619 701 : v(other.v), outputPin(other.outputPin)
620 {
621
1/2
✓ Branch 1 taken 701 times.
✗ Branch 2 not taken.
701 std::scoped_lock guard(outputPin->dataAccessMutex);
622 701 outputPin->dataAccessCounter++;
623 701 }
624 /// @brief Move constructor
625 848 ValueWrapper(ValueWrapper&& other) noexcept
626 848 : v(std::move(other.v)), outputPin(other.outputPin)
627 {
628 848 other.outputPin = nullptr;
629 848 }
630 /// @brief Copy assignment operator
631 ValueWrapper& operator=(const ValueWrapper& other)
632 {
633 if (this != &other)
634 {
635 v = other.v;
636 outputPin = other.outputPin;
637 std::scoped_lock guard(outputPin->dataAccessMutex);
638 outputPin->dataAccessCounter++;
639 }
640 return *this;
641 }
642 /// @brief Move assignment operator
643 ValueWrapper& operator=(ValueWrapper&& other) noexcept
644 {
645 if (this != &other)
646 {
647 v = std::move(other.v);
648 outputPin = other.outputPin;
649 other.outputPin = nullptr;
650 }
651 return *this;
652 }
653
654 /// Pointer to the value wrapped
655 const T* v;
656
657 private:
658 /// Pointer to the output pin representing the data
659 OutputPin* outputPin;
660 };
661
662 /// @brief Get the value connected on the pin if the pin is connected. Automatically increments the data access counter to prevent editing.
663 /// @tparam T Type of the connected object
664 /// @return ValueWrapper with a const pointer to the connected data
665 template<typename T>
666 701 [[nodiscard]] std::optional<ValueWrapper<T>> getValue() const
667 {
668
1/2
✓ Branch 1 taken 701 times.
✗ Branch 2 not taken.
701 if (auto* connectedPin = getConnectedPin())
669 {
670 // if (!connectedPin->parentNode->isInitialized()) { return nullptr; } // TODO: Check this here (Problem: member access into incomplete type 'NAV::Node')
671
672
1/2
✓ Branch 1 taken 701 times.
✗ Branch 2 not taken.
701 auto outgoingLink = std::ranges::find_if(connectedPin->links, [&](const OutputPin::OutgoingLink& link) {
673 701 return link.linkId == linkId;
674 });
675
676 // clang-format off
677 if constexpr (std::is_same_v<T, bool>
678 || std::is_same_v<T, int>
679 || std::is_same_v<T, float>
680 || std::is_same_v<T, double>
681 || std::is_same_v<T, std::string>) // clang-format on
682 {
683 if (const auto* pVal = std::get_if<const T*>(&(connectedPin->data));
684 pVal && *pVal)
685 {
686 return ValueWrapper<T>(*pVal, connectedPin, outgoingLink->dataChangeNotification);
687 }
688 }
689 else
690 {
691
1/2
✓ Branch 1 taken 701 times.
✗ Branch 2 not taken.
701 if (const auto* pVal = std::get_if<const void*>(&(connectedPin->data));
692
1/2
✓ Branch 0 taken 701 times.
✗ Branch 1 not taken.
701 pVal && *pVal)
693 {
694
1/2
✓ Branch 2 taken 701 times.
✗ Branch 3 not taken.
701 return ValueWrapper<T>(static_cast<const T*>(*pVal), connectedPin, outgoingLink->dataChangeNotification);
695 }
696 }
697 }
698
699 return std::nullopt;
700 }
701 };
702
703 /// Info to identify the linked pin
704 IncomingLink link;
705
706 /// Node data queue type
707 using NodeDataQueue = TsDeque<std::shared_ptr<const NAV::NodeData>>;
708
709 /// Flow data callback function type to call when firable.
710 /// - 1st Parameter: Queue with the received messages
711 /// - 2nd Parameter: Pin index of the pin the data is received on
712 using FlowFirableCallbackFunc = void (Node::*)(NodeDataQueue&, size_t);
713 /// Notify function type to call when the connected value changed
714 /// - 1st Parameter: Time when the message was received
715 /// - 2nd Parameter: Pin index of the pin the data is received on
716 using DataChangedNotifyFunc = void (Node::*)(const InsTime&, size_t);
717 /// Callback function types
718 using Callback = std::variant<FlowFirableCallbackFunc, // Flow: Callback function type to call when firable
719 DataChangedNotifyFunc>; // Other: Notify function type to call when the connected value changed
720
721 /// Callback to call when the node is firable or when it should be notified of data change
722 Callback callback;
723
724 /// Function type to call when checking if a pin is firable
725 using FlowFirableCheckFunc = bool (*)(const Node*, const InputPin&);
726
727 /// @brief Function to check if the callback is firable
728
2/4
✓ Branch 1 taken 389696 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 389703 times.
✗ Branch 4 not taken.
389689 FlowFirableCheckFunc firable = [](const Node*, const InputPin& inputPin) { return !inputPin.queue.empty() && !inputPin.queueBlocked; };
729
730 /// @brief Priority when checking firable condition related to other pins (higher priority gets triggered first)
731 int priority = 0;
732
733 /// @brief Whether it should be checked for temporal ordering
734 bool neededForTemporalQueueCheck = true;
735
736 /// @brief If true, drops elements from the queue if not firable, otherwise sleeps the worker
737 bool dropQueueIfNotFirable = true;
738
739 /// If true no more messages are accepted to the queue
740 bool queueBlocked = false;
741
742 /// Queue with received data
743 NodeDataQueue queue;
744
745 #ifdef TESTING
746 /// Flow data watcher callback function type to call when firable.
747 /// - 1st Parameter: Queue with the received messages
748 /// - 2nd Parameter: Pin index of the pin the data is received on
749 using FlowFirableWatcherCallbackFunc = std::function<void(const Node*, const NodeDataQueue&, size_t)>;
750 /// Notify watcher function type to call when the connected value changed
751 /// - 1st Parameter: Time when the message was received
752 /// - 2nd Parameter: Pin index of the pin the data is received on
753 using DataChangedWatcherNotifyFunc = std::function<void(const Node*, const InsTime&, size_t)>;
754
755 /// Watcher callback function types
756 using WatcherCallback = std::variant<FlowFirableWatcherCallbackFunc, // Flow: Callback function type to call when firable
757 DataChangedWatcherNotifyFunc>; // Other: Notify function type to call when the connected value changed
758
759 /// Watcher Callbacks are used in testing to check the transmitted data
760 std::vector<WatcherCallback> watcherCallbacks;
761 #endif
762
763 friend class Pin;
764 friend class OutputPin;
765
766 private:
767 /// @brief Connects this pin to another
768 /// @param[in] startPin Pin which should be linked to this pin
769 /// @param[in] linkId Id of the link to create
770 void connect(OutputPin& startPin, ax::NodeEditor::LinkId linkId = 0);
771
772 /// @brief Disconnects the link
773 void disconnect();
774 };
775
776 /// @brief Equal compares Pin::Kind values
777 /// @param[in] lhs Left-hand side of the operator
778 /// @param[in] rhs Right-hand side of the operator
779 /// @return Whether the comparison was successful
780 273 constexpr bool operator==(const Pin::Kind& lhs, const Pin::Kind& rhs) { return lhs.value == rhs.value; }
781 /// @brief Inequal compares Pin::Kind values
782 /// @param[in] lhs Left-hand side of the operator
783 /// @param[in] rhs Right-hand side of the operator
784 /// @return Whether the comparison was successful
785 273 constexpr bool operator!=(const Pin::Kind& lhs, const Pin::Kind& rhs) { return !(lhs == rhs); }
786
787 /// @brief Equal compares Pin::Kind values
788 /// @param[in] lhs Left-hand side of the operator
789 /// @param[in] rhs Right-hand side of the operator
790 /// @return Whether the comparison was successful
791 constexpr bool operator==(const Pin::Kind& lhs, const Pin::Kind::Value& rhs) { return lhs.value == rhs; }
792 /// @brief Equal compares Pin::Kind values
793 /// @param[in] lhs Left-hand side of the operator
794 /// @param[in] rhs Right-hand side of the operator
795 /// @return Whether the comparison was successful
796 constexpr bool operator==(const Pin::Kind::Value& lhs, const Pin::Kind& rhs) { return lhs == rhs.value; }
797 /// @brief Inequal compares Pin::Kind values
798 /// @param[in] lhs Left-hand side of the operator
799 /// @param[in] rhs Right-hand side of the operator
800 /// @return Whether the comparison was successful
801 constexpr bool operator!=(const Pin::Kind& lhs, const Pin::Kind::Value& rhs) { return !(lhs == rhs); }
802 /// @brief Inequal compares Pin::Kind values
803 /// @param[in] lhs Left-hand side of the operator
804 /// @param[in] rhs Right-hand side of the operator
805 /// @return Whether the comparison was successful
806 constexpr bool operator!=(const Pin::Kind::Value& lhs, const Pin::Kind& rhs) { return !(lhs == rhs); }
807
808 /// @brief Equal compares Pin::Type values
809 /// @param[in] lhs Left-hand side of the operator
810 /// @param[in] rhs Right-hand side of the operator
811 /// @return Whether the comparison was successful
812 273 constexpr bool operator==(const Pin::Type& lhs, const Pin::Type& rhs) { return lhs.value == rhs.value; }
813 /// @brief Inequal compares Pin::Type values
814 /// @param[in] lhs Left-hand side of the operator
815 /// @param[in] rhs Right-hand side of the operator
816 /// @return Whether the comparison was successful
817 constexpr bool operator!=(const Pin::Type& lhs, const Pin::Type& rhs) { return !(lhs == rhs); }
818
819 /// @brief Equal compares Pin::Type values
820 /// @param[in] lhs Left-hand side of the operator
821 /// @param[in] rhs Right-hand side of the operator
822 /// @return Whether the comparison was successful
823 5891128 constexpr bool operator==(const Pin::Type& lhs, const Pin::Type::Value& rhs) { return lhs.value == rhs; }
824 /// @brief Equal compares Pin::Type values
825 /// @param[in] lhs Left-hand side of the operator
826 /// @param[in] rhs Right-hand side of the operator
827 /// @return Whether the comparison was successful
828 constexpr bool operator==(const Pin::Type::Value& lhs, const Pin::Type& rhs) { return lhs == rhs.value; }
829 /// @brief Inequal compares Pin::Type values
830 /// @param[in] lhs Left-hand side of the operator
831 /// @param[in] rhs Right-hand side of the operator
832 /// @return Whether the comparison was successful
833 2486115 constexpr bool operator!=(const Pin::Type& lhs, const Pin::Type::Value& rhs) { return !(lhs == rhs); }
834 /// @brief Inequal compares Pin::Type values
835 /// @param[in] lhs Left-hand side of the operator
836 /// @param[in] rhs Right-hand side of the operator
837 /// @return Whether the comparison was successful
838 constexpr bool operator!=(const Pin::Type::Value& lhs, const Pin::Type& rhs) { return !(lhs == rhs); }
839
840 /// @brief Converts the provided pin into a json object
841 /// @param[out] j Json object which gets filled with the info
842 /// @param[in] pin Node to convert into json
843 void to_json(json& j, const OutputPin& pin);
844 /// @brief Converts the provided json object into a pin object
845 /// @param[in] j Json object with the needed values
846 /// @param[out] pin Object to fill from the json
847 void from_json(const json& j, OutputPin& pin);
848
849 /// @brief Converts the provided pin into a json object
850 /// @param[out] j Json object which gets filled with the info
851 /// @param[in] pin Node to convert into json
852 void to_json(json& j, const InputPin& pin);
853 /// @brief Converts the provided json object into a pin object
854 /// @param[in] j Json object with the needed values
855 /// @param[out] pin Object to fill from the json
856 void from_json(const json& j, InputPin& pin);
857
858 } // namespace NAV
859