Branch data Line data Source code
1 : : /*
2 : : * Copyright 2025 Jinwoo Sung
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #pragma once
18 : :
19 : : #include <functional>
20 : : #include <future>
21 : : #include <memory>
22 : : #include <string>
23 : : #include <string_view>
24 : : #include <vector>
25 : :
26 : : #include "wirestead/base/visibility.hpp"
27 : : #include "wirestead/diagnostics/logger.hpp"
28 : : #include "wirestead/framer/iframer.hpp"
29 : : #include "wirestead/wrapper/context.hpp"
30 : : #include "wirestead/wrapper/runtime_stats.hpp"
31 : :
32 : : namespace wirestead {
33 : : namespace wrapper {
34 : :
35 : : /**
36 : : * @brief Common interface for 1:1 point-to-point communication (e.g., TcpClient, Serial, Udp)
37 : : */
38 : : class WIRESTEAD_API ChannelInterface {
39 : : public:
40 : : using MessageHandler = std::function<void(const MessageContext&)>;
41 : : using BatchMessageHandler = std::function<void(const std::vector<MessageContext>&)>;
42 : : using ConnectionHandler = std::function<void(const ConnectionContext&)>;
43 : : using ErrorHandler = std::function<void(const ErrorContext&)>;
44 : :
45 : 266 : virtual ~ChannelInterface() = default;
46 : :
47 : : // Lifecycle
48 : :
49 : : /**
50 : : * @brief Start the channel asynchronously.
51 : : *
52 : : * @return A future that resolves to true when the channel is connected/listening,
53 : : * or false if startup failed (e.g. connection refused, max retries exhausted).
54 : : * On false, the registered on_error() callback will have been invoked with
55 : : * the specific failure reason. This future always resolves - including on a
56 : : * restart after stop() (#444) - never blocks indefinitely.
57 : : */
58 : : [[nodiscard]] virtual std::future<bool> start() = 0;
59 : :
60 : : /**
61 : : * @brief Synchronously start the channel and wait for the result.
62 : : *
63 : : * @return true if connected/listening, false on failure. On false, the registered
64 : : * on_error() callback will have been invoked with the specific failure reason.
65 : : */
66 : 8 : [[nodiscard]] virtual bool start_sync() { return start().get(); }
67 : :
68 : : /**
69 : : * @brief Stop the channel and block until all pending async operations are cancelled.
70 : : *
71 : : * Safe to call from any thread. After stop() returns, no further callbacks will fire
72 : : * and it is safe to destroy the object. Calling stop() more than once is a no-op.
73 : : *
74 : : * Restart contract (#444): stop() fully tears down the underlying transport
75 : : * (socket/serial port, queues, timers) rather than leaving it in a reusable
76 : : * half-alive state. Every on_*() callback and every config setter ever
77 : : * called on this wrapper remains in force across any number of stop()/
78 : : * start() cycles - a subsequent start() reconstructs the transport from
79 : : * this wrapper's retained configuration and re-registers all callbacks
80 : : * automatically, including config changes made while stopped. Session/
81 : : * client-id state and stats() reset on restart. Callers never need to
82 : : * re-register callbacks or reconfigure after a stop()/start() cycle.
83 : : */
84 : : virtual void stop() = 0;
85 : : virtual bool connected() const = 0;
86 : : virtual RuntimeStats stats() const = 0;
87 : : virtual void reset_stats() = 0;
88 : :
89 : : // Transmission
90 : : //
91 : : // Strategy-aware API (recommended):
92 : : //
93 : : // send() / send_line()
94 : : // Behaviour depends on the configured backpressure strategy:
95 : : // BestEffort — non-blocking; drops data when the send queue is full.
96 : : // Reliable — blocks the calling thread until queue pressure is relieved,
97 : : // then enqueues. Never drops due to backpressure alone.
98 : : //
99 : : // Explicit API (escape hatch):
100 : : //
101 : : // try_send() / try_send_line()
102 : : // Always non-blocking and always rejects a full/backpressured queue,
103 : : // regardless of strategy. Reliable channels do not enqueue into pending_
104 : : // for try_send().
105 : : // Rejects payloads that would exceed the current non-blocking queue
106 : : // threshold; payloads below MAX_BUFFER_SIZE can still be rejected when
107 : : // they exceed the current backpressure high-water budget.
108 : : // Use when you need BestEffort behaviour on a Reliable channel for a specific
109 : : // call (e.g. fire-and-forget heartbeat).
110 : : //
111 : : // send_blocking() / send_line_blocking()
112 : : // Always blocks until queue pressure is relieved, regardless of strategy.
113 : : // This call may block indefinitely while the channel remains active and
114 : : // backpressure does not clear. stop() from another thread is expected to
115 : : // unblock waiting senders.
116 : :
117 : : /**
118 : : * @brief Enqueue data for transmission, honouring the backpressure strategy.
119 : : *
120 : : * BestEffort: non-blocking, drops if the send queue is full.
121 : : * Reliable: blocks until queue pressure is relieved, then enqueues.
122 : : *
123 : : * @return true Data was accepted into the send queue.
124 : : * @return false Data was dropped (not connected, or BestEffort queue full).
125 : : */
126 : : virtual bool send(std::string_view data) = 0;
127 : :
128 : : /**
129 : : * @brief Enqueue a line (data + "\n") for transmission, honouring the backpressure strategy.
130 : : * @return true Data was accepted. @return false Data was dropped.
131 : : */
132 : : virtual bool send_line(std::string_view line) = 0;
133 : :
134 : : /**
135 : : * @brief Block the calling thread until queue pressure is relieved, then enqueue.
136 : : *
137 : : * Ignores the configured strategy — always blocks. Prefer send() unless you need
138 : : * to override BestEffort behaviour for a specific call.
139 : : * This call may block indefinitely while the channel remains active and
140 : : * backpressure does not clear. stop() from another thread is expected to unblock
141 : : * waiting senders.
142 : : *
143 : : * @return true Data was accepted. @return false Channel stopped while waiting.
144 : : */
145 : : virtual bool send_blocking(std::string_view data) = 0;
146 : :
147 : : /**
148 : : * @brief Blocking variant of send_line(). Always blocks regardless of strategy.
149 : : *
150 : : * This call may block indefinitely while the channel remains active and
151 : : * backpressure does not clear. stop() from another thread is expected to unblock
152 : : * waiting senders.
153 : : *
154 : : * @return true Data was accepted. @return false Channel stopped while waiting.
155 : : */
156 : : virtual bool send_line_blocking(std::string_view line) = 0;
157 : :
158 : : /**
159 : : * @brief Non-blocking send that always drops on a full queue, ignoring strategy.
160 : : *
161 : : * Use as an escape hatch when you need drop-on-full behaviour on a Reliable channel.
162 : : * Rejects payloads that would exceed the current non-blocking queue threshold.
163 : : * A payload may be rejected even if it is below MAX_BUFFER_SIZE when it is larger
164 : : * than the current backpressure high-water budget. Use send() or send_blocking()
165 : : * when Reliable enqueue semantics are required for large payloads.
166 : : *
167 : : * @return true Data was accepted. @return false Dropped (not connected or queue full).
168 : : */
169 : : virtual bool try_send(std::string_view data) = 0;
170 : :
171 : : /**
172 : : * @brief Non-blocking send_line that always drops on a full queue, ignoring strategy.
173 : : *
174 : : * Uses the same non-blocking queue threshold policy as try_send().
175 : : *
176 : : * @return true Data was accepted. @return false Dropped.
177 : : */
178 : : virtual bool try_send_line(std::string_view line) = 0;
179 : :
180 : : /**
181 : : * @brief Enqueue a vector payload by transferring ownership, honouring the backpressure strategy.
182 : : *
183 : : * After this call, the caller must treat the moved-from vector as consumed regardless
184 : : * of the return value. Existing string_view send APIs remain available for borrowed data.
185 : : *
186 : : * @return true Data was accepted. @return false Data was dropped or rejected.
187 : : */
188 : : virtual bool send_move(std::vector<uint8_t>&& data) = 0;
189 : :
190 : : /**
191 : : * @brief Non-blocking ownership-transfer send.
192 : : *
193 : : * Always returns without waiting for backpressure. The moved-from vector is consumed
194 : : * regardless of the return value.
195 : : * Rejects payloads that would exceed the current non-blocking queue threshold,
196 : : * even below MAX_BUFFER_SIZE when they exceed the current high-water budget.
197 : : *
198 : : * @return true Data was accepted. @return false Data was dropped or rejected.
199 : : */
200 : : virtual bool try_send_move(std::vector<uint8_t>&& data) = 0;
201 : :
202 : : /**
203 : : * @brief Enqueue an immutable shared vector payload, honouring the backpressure strategy.
204 : : *
205 : : * The shared buffer must be non-null and non-empty.
206 : : *
207 : : * @return true Data was accepted. @return false Data was dropped or rejected.
208 : : */
209 : : virtual bool send_shared(std::shared_ptr<const std::vector<uint8_t>> data) = 0;
210 : :
211 : : /**
212 : : * @brief Non-blocking shared-buffer send.
213 : : *
214 : : * The shared buffer must be non-null and non-empty.
215 : : * Rejects payloads that would exceed the current non-blocking queue threshold,
216 : : * even below MAX_BUFFER_SIZE when they exceed the current high-water budget.
217 : : *
218 : : * @return true Data was accepted. @return false Data was dropped or rejected.
219 : : */
220 : : virtual bool try_send_shared(std::shared_ptr<const std::vector<uint8_t>> data) = 0;
221 : :
222 : : // Event handlers
223 : : virtual ChannelInterface& on_data(MessageHandler handler) = 0;
224 : :
225 : : /** @brief Register a callback for batched data reception */
226 : : virtual ChannelInterface& on_data_batch(BatchMessageHandler handler) = 0;
227 : :
228 : : virtual ChannelInterface& on_connect(ConnectionHandler handler) = 0;
229 : : virtual ChannelInterface& on_disconnect(ConnectionHandler handler) = 0;
230 : : virtual ChannelInterface& on_error(ErrorHandler handler) = 0;
231 : :
232 : : /**
233 : : * @brief Register a callback to be notified when send queue congestion changes.
234 : : * @param handler Callback receiving the current number of queued bytes.
235 : : *
236 : : * Default implementation is a no-op. Concrete implementations that support
237 : : * backpressure reporting must override this method; otherwise the handler
238 : : * is silently discarded.
239 : : */
240 : 0 : virtual ChannelInterface& on_backpressure(std::function<void(size_t)> handler) {
241 [ # # ]: 0 : if (handler) {
242 : 0 : WIRESTEAD_LOG_WARNING("channel_interface", "on_backpressure",
243 : : "Backpressure reporting is not supported by this channel; handler is discarded.");
244 : : }
245 : 0 : return *this;
246 : : }
247 : :
248 : : /**
249 : : * @brief Set a message framer for this channel.
250 : : * @param framer The framer instance to use.
251 : : */
252 : : virtual ChannelInterface& framer(std::unique_ptr<framer::IFramer> framer) = 0;
253 : :
254 : : /**
255 : : * @brief Set a handler for complete messages extracted by the framer.
256 : : * @param handler The callback for framed messages.
257 : : */
258 : : virtual ChannelInterface& on_message(MessageHandler handler) = 0;
259 : :
260 : : /** @brief Register a callback for batched framed message reception */
261 : : virtual ChannelInterface& on_message_batch(BatchMessageHandler handler) = 0;
262 : :
263 : : // Management
264 : : virtual ChannelInterface& auto_start(bool manage = true) = 0;
265 : : };
266 : :
267 : : } // namespace wrapper
268 : : } // namespace wirestead
|