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 |