VLink  2.0.0
A high-performance communication middleware
status.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file status.h
26  * @brief DDS-aligned status event hierarchy delivered to publisher and subscriber callbacks.
27  *
28  * @details
29  * The @c Status namespace exposes a polymorphic, shared-pointer based event model that mirrors
30  * the DDS status change notification system. When the underlying transport (DDS, intra, shm,
31  * zenoh, etc.) detects an interesting condition it constructs a concrete @c Base subclass and
32  * forwards it to the user-registered @c StatusCallback. Callers use @c Base::as<T>() to safely
33  * narrow the pointer to the concrete type carried by the event.
34  *
35  * @par Status codes
36  *
37  * | Value | Enumerator | Side | Triggered when |
38  * | ----- | ------------------------------ | ------- | ---------------------------------------------------- |
39  * | 0 | @c kUnknown | - | transport reports an unrecognised status type |
40  * | 1 | @c kPublicationMatched | writer | matching subscriber appeared or disappeared |
41  * | 2 | @c kOfferedDeadlineMissed | writer | offered publication deadline missed |
42  * | 3 | @c kOfferedIncompatibleQos | writer | discovered subscriber with incompatible QoS |
43  * | 4 | @c kLivelinessLost | writer | writer failed to assert liveliness within lease |
44  * | 5 | @c kSubscriptionMatched | reader | matching publisher appeared or disappeared |
45  * | 6 | @c kRequestedDeadlineMissed | reader | reader missed its requested deadline |
46  * | 7 | @c kLivelinessChanged | reader | matched publisher liveliness state changed |
47  * | 8 | @c kSampleRejected | reader | inbound sample dropped (resource limit) |
48  * | 9 | @c kRequestedIncompatibleQos | reader | discovered publisher with incompatible QoS |
49  * | 10 | @c kSampleLost | reader | sample lost before delivery |
50  *
51  * @par Severity matrix
52  *
53  * | Status | Severity | Action recommended |
54  * | ------------------------------- | ------------- | ------------------------------------------------------- |
55  * | @c kPublicationMatched | informational | log peer count change |
56  * | @c kSubscriptionMatched | informational | log peer count change |
57  * | @c kLivelinessChanged | informational | track @c alive_count delta |
58  * | @c kOfferedDeadlineMissed | warning | investigate slow publisher loop |
59  * | @c kRequestedDeadlineMissed | warning | investigate dropped traffic |
60  * | @c kOfferedIncompatibleQos | warning | reconcile QoS profile with peer |
61  * | @c kRequestedIncompatibleQos | warning | reconcile QoS profile with peer |
62  * | @c kSampleRejected | error | enlarge @c ResourceLimits or drop sources |
63  * | @c kSampleLost | error | enlarge @c History depth or upgrade reliability |
64  * | @c kLivelinessLost | error | peer considers writer dead; restore heartbeats |
65  *
66  * Concrete event structs and their counter / handle fields live in @c status_detail.h.
67  *
68  * @par Example
69  * @code
70  * auto sub = vlink::Subscriber<MyMsg>::create("dds://my/topic");
71  *
72  * sub->register_status_callback([](vlink::Status::BasePtr status) {
73  * switch (status->get_type()) {
74  * case vlink::Status::kSubscriptionMatched: {
75  * auto detail = status->as<vlink::Status::SubscriptionMatched>();
76  * VLOG_I("matched publishers: ", detail->current_count);
77  * break;
78  * }
79  * case vlink::Status::kSampleLost: {
80  * auto detail = status->as<vlink::Status::SampleLost>();
81  * VLOG_W("samples lost so far: ", detail->total_count);
82  * break;
83  * }
84  * default:
85  * break;
86  * }
87  * });
88  * @endcode
89  */
90 
91 #pragma once
92 
93 #include <cstdint>
94 #include <iostream>
95 #include <memory>
96 #include <string>
97 #include <type_traits>
98 
99 #include "../base/exception.h"
100 #include "../base/macros.h"
101 
102 namespace vlink {
103 
104 /**
105  * @namespace vlink::Status
106  * @brief DDS-aligned status enumeration, base event class, and helper predicates.
107  */
108 namespace Status { // NOLINT(readability-identifier-naming)
109 
110 /**
111  * @brief Discriminator that identifies the concrete @c Base subclass carried by an event.
112  *
113  * @details
114  * Values @c 1..4 are emitted on the writer side, values @c 5..10 on the reader side; use
115  * @c is_for_writer() / @c is_for_reader() to classify a value without inspecting the
116  * concrete subclass.
117  */
118 enum Type : uint8_t {
119  kUnknown = 0, ///< Placeholder for unrecognised status events.
120  // -- For writer
121  kPublicationMatched = 1, ///< Matching subscriber appeared or disappeared.
122  kOfferedDeadlineMissed = 2, ///< Writer missed its offered publication deadline.
123  kOfferedIncompatibleQos = 3, ///< Discovered subscriber with incompatible QoS.
124  kLivelinessLost = 4, ///< Writer liveliness assertion lapsed.
125  // -- For reader
126  kSubscriptionMatched = 5, ///< Matching publisher appeared or disappeared.
127  kRequestedDeadlineMissed = 6, ///< Reader missed its requested deadline.
128  kLivelinessChanged = 7, ///< Liveliness of a matched publisher changed.
129  kSampleRejected = 8, ///< Inbound sample dropped by resource limits.
130  kRequestedIncompatibleQos = 9, ///< Discovered publisher with incompatible QoS.
131  kSampleLost = 10, ///< Sample lost before delivery.
132 };
133 
134 /**
135  * @brief Reports whether @p type belongs to the writer-side group.
136  *
137  * @param type Status type to classify.
138  * @return @c true for values @c 1..4; @c false for @c kUnknown and reader-side values.
139  */
140 [[nodiscard]] [[maybe_unused]] static constexpr bool is_for_writer(Type type) noexcept {
141  return type != kUnknown && type < kSubscriptionMatched;
142 }
143 
144 /**
145  * @brief Reports whether @p type belongs to the reader-side group.
146  *
147  * @param type Status type to classify.
148  * @return @c true for values @c 5..10.
149  */
150 [[nodiscard]] [[maybe_unused]] static constexpr bool is_for_reader(Type type) noexcept {
151  return type >= kSubscriptionMatched;
152 }
153 
154 /**
155  * @brief Opaque transport-defined identifier for a matched publication or subscription.
156  *
157  * @details
158  * Treat as an opaque key; the bit pattern is transport-specific and is only meaningful when
159  * compared against handles obtained from the same transport.
160  */
161 using InstanceHandle = const void*;
162 
163 /**
164  * @struct Base
165  * @brief Polymorphic base for every concrete status event delivered to a callback.
166  *
167  * @details
168  * Concrete subclasses live in @c status_detail.h and carry the counter, handle, and reason
169  * fields specific to each event. Subscribers downcast through @c as<T>(), which throws
170  * @c Exception::RuntimeError for @c kUnknown events. @c std::enable_shared_from_this allows
171  * implementations to safely produce a @c shared_ptr to themselves from inside member calls.
172  */
173 struct VLINK_EXPORT Base : public std::enable_shared_from_this<Base> {
174  protected:
175  Base();
176 
177  virtual ~Base();
178 
179  public:
180  /**
181  * @brief Returns the concrete event type discriminator.
182  *
183  * @return One of the @c Status::Type enumerators.
184  */
185  [[nodiscard]] virtual Type get_type() const = 0;
186 
187  /**
188  * @brief Returns the event name without numeric fields.
189  *
190  * @return Short string such as @c "SubscriptionMatched". Use @c operator<< for full detail.
191  */
192  [[nodiscard]] virtual std::string get_string() const = 0;
193 
194  /**
195  * @brief Safely narrows this event to a concrete @c Status subclass.
196  *
197  * @tparam T Concrete event struct derived from @c Base; must not be @c Status::Unknown.
198  * @return @c shared_ptr<T> pointing at this event.
199  * @throws Exception::RuntimeError when @c get_type() is @c kUnknown.
200  */
201  template <typename T>
202  [[nodiscard]] std::shared_ptr<T> as() const;
203 
204  /**
205  * @brief Writes the human-readable description of @p status to @p ostream.
206  *
207  * @param ostream Output stream.
208  * @param status Event to print.
209  * @return Reference to @p ostream.
210  */
211  VLINK_EXPORT friend std::ostream& operator<<(std::ostream& ostream, const Base& status) noexcept;
212 };
213 
214 /**
215  * @struct Unknown
216  * @brief Placeholder event emitted when the transport reports a status the runtime cannot map.
217  *
218  * @details
219  * Callbacks normally short-circuit on @c get_type() == @c kUnknown to avoid attempting an
220  * invalid downcast.
221  */
222 struct VLINK_EXPORT Unknown final : public Base {
223  public:
224  /**
225  * @brief Returns @c kUnknown.
226  *
227  * @return Always @c kUnknown.
228  */
229  [[nodiscard]] Type get_type() const override;
230 
231  /**
232  * @brief Returns the literal "Unknown".
233  *
234  * @return Descriptive string.
235  */
236  [[nodiscard]] std::string get_string() const override;
237 
238  /**
239  * @brief Writes "Unknown" to @p ostream.
240  *
241  * @param ostream Output stream.
242  * @param status This @c Unknown event.
243  * @return Reference to @p ostream.
244  */
245  VLINK_EXPORT friend std::ostream& operator<<(std::ostream& ostream, const Unknown& status) noexcept;
246 };
247 
248 /**
249  * @brief Shared-pointer alias used as the parameter type of every status callback.
250  */
251 using BasePtr = std::shared_ptr<Status::Base>;
252 
253 /**
254  * @brief Writes the human-readable description of @p status to @p ostream.
255  *
256  * @param ostream Output stream.
257  * @param status Shared pointer to an event.
258  * @return Reference to @p ostream.
259  */
260 VLINK_EXPORT std::ostream& operator<<(std::ostream& ostream, const BasePtr& status) noexcept;
261 
262 ////////////////////////////////////////////////////////////////
263 /// Details
264 ////////////////////////////////////////////////////////////////
265 
266 template <typename T>
267 inline std::shared_ptr<T> Base::as() const {
268  static_assert(std::is_base_of_v<Base, T> && !std::is_same_v<struct Unknown, T>,
269  "Can not convert target status type.");
270 
271  if VUNLIKELY (get_type() == kUnknown) {
272  throw Exception::RuntimeError("Target status is unknown");
273  }
274 
275 #if defined(NDEBUG) || defined(__ANDROID__)
276  return std::static_pointer_cast<T>(const_cast<Base*>(this)->shared_from_this());
277 #else
278  return std::dynamic_pointer_cast<T>(const_cast<Base*>(this)->shared_from_this());
279 #endif
280 }
281 
282 } // namespace Status
283 
284 } // namespace vlink
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLINK_EXPORT
Definition: macros.h:81