PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
cyclic_raw_ethernet_socket_stub.h
Go to the documentation of this file.
1
3#pragma once
4
5#include <chrono>
6#include <condition_variable>
7#include <cstddef>
8#include <deque>
9#include <functional>
10#include <memory>
11#include <mutex>
12#include <stdexcept>
13#include <string>
14#include <utility>
15#include <vector>
16
18#include "profinet/protocol.h"
19#include "profinet/util.h"
20
22{
23
32{
33 public:
36 {
38 bool throwException{false};
39
42 };
43
46 void SetSendThrows(bool enabled)
47 {
48 sendThrows = enabled;
49 }
50
53 void SetSendExceptionMessage(std::string message)
54 {
55 sendExceptionMessage = std::move(message);
56 }
57
60 void SetRecvExceptionMessage(std::string message)
61 {
62 recvExceptionMessage = std::move(message);
63 }
64
67 {
68 receiveActions.push_back({false, {}});
69 }
70
74 {
75 receiveActions.push_back({false, std::move(data)});
76 }
77
80 {
81 receiveActions.push_back({true, {}});
82 }
83
86 [[nodiscard]] std::size_t SentFrameCount() const
87 {
88 std::lock_guard<std::mutex> lock(mutex);
89 return sentFrames.size();
90 }
91
95 [[nodiscard]] const Bytes& SentFrame(std::size_t index) const
96 {
97 return sentFrames.at(index);
98 }
99
102 [[nodiscard]] std::chrono::milliseconds Timeout() const
103 {
104 return timeout;
105 }
106
109 void OnSend(std::function<void()> callback)
110 {
111 onSend = std::move(callback);
112 }
113
116 void Send(const Bytes& data) override
117 {
118 if (sendThrows)
119 {
120 throw std::runtime_error(sendExceptionMessage);
121 }
122 {
123 std::lock_guard<std::mutex> lock(mutex);
124 sentFrames.push_back(data);
125 }
126 if (onSend)
127 {
128 onSend();
129 }
130 }
131
134 [[nodiscard]] Bytes Recv() override
135 {
136 if (receiveActions.empty())
137 {
138 return {};
139 }
140
141 ReceiveAction action = std::move(receiveActions.front());
142 receiveActions.pop_front();
143
144 if (action.throwException)
145 {
146 throw std::runtime_error(recvExceptionMessage);
147 }
148
149 return std::move(action.data);
150 }
151
154 void SetTimeout(std::chrono::milliseconds value) override
155 {
156 timeout = value;
157 }
158
159 private:
161 std::deque<ReceiveAction> receiveActions;
162
164 std::vector<Bytes> sentFrames;
165
167 bool sendThrows{false};
168
170 std::chrono::milliseconds timeout{0};
171
173 std::string sendExceptionMessage{"send failure"};
174
176 std::function<void()> onSend;
177
179 std::string recvExceptionMessage{"receive failure"};
180
181 mutable std::mutex mutex;
182};
183
185{
186 public:
187 void Send(const Bytes& frame) override
188 {
189 }
190
191 Bytes Recv() override
192 {
193 BlockReceive();
194
195 return {};
196 }
197
198 void SetTimeout(std::chrono::milliseconds timeout) override
199 {
200 }
201
203 {
204 std::unique_lock lock(mutex);
205 receiveEntered = true;
206 receiveEnteredCv.notify_all();
207
208 receiveCv.wait(lock, [this]
209 {
210 return receiveReleased;
211 });
212 }
213
215 {
216 {
217 std::lock_guard lock(mutex);
218 receiveReleased = true;
219 }
220
221 receiveCv.notify_all();
222 }
223
225 {
226 std::unique_lock lock(mutex);
227
228 receiveEnteredCv.wait(lock, [this]
229 {
230 return receiveEntered;
231 });
232 }
233
234 // Receive() should call BlockReceive().
235 // Other IRrawEthernetSocket methods implement the minimum
236 // behavior required by CyclicController::Start()/TxCycle().
237
238 private:
239 std::mutex mutex;
240 std::condition_variable receiveCv;
241 std::condition_variable receiveEnteredCv;
242
243 bool receiveEntered{false};
244 bool receiveReleased{false};
245};
246
248{
249 std::mutex mutex;
250 std::condition_variable receiveCv;
251 std::condition_variable receiveEnteredCv;
252 std::condition_variable receiveExitedCv;
253
254 bool receiveEntered{false};
255 bool receiveExited{false};
256 bool releaseReceive{false};
257
258 std::atomic<bool> destroyed{false};
259 std::atomic<bool> destroyedWhileReceiving{false};
260 std::atomic<bool> receiveActive{false};
261};
262
264{
265 public:
267 std::shared_ptr<LifetimeTrackingSocketState> state)
268 : state(std::move(state))
269 {
270 }
271
273 {
274 if (state->receiveActive.load(std::memory_order_acquire))
275 {
276 state->destroyedWhileReceiving.store(
277 true, std::memory_order_release);
278 }
279
280 state->destroyed.store(true, std::memory_order_release);
281 }
282
283 void Send(const Bytes&) override
284 {
285 }
286
287 Bytes Recv() override
288 {
289 state->receiveActive.store(true, std::memory_order_release);
290
291 {
292 std::lock_guard lock(state->mutex);
293 state->receiveEntered = true;
294 }
295
296 state->receiveEnteredCv.notify_one();
297
298 {
299 std::unique_lock lock(state->mutex);
300 state->receiveCv.wait(lock, [this]
301 {
302 return state->releaseReceive;
303 });
304 }
305
306 state->receiveActive.store(false, std::memory_order_release);
307
308 {
309 std::lock_guard lock(state->mutex);
310 state->receiveExited = true;
311 }
312
313 state->receiveExitedCv.notify_one();
314
315 return {};
316 }
317
318 void SetTimeout(std::chrono::milliseconds) override
319 {
320 }
321
323 {
324 std::unique_lock lock(state->mutex);
325
326 state->receiveEnteredCv.wait(lock, [this]
327 {
328 return state->receiveEntered;
329 });
330 }
331
333 {
334 std::unique_lock lock(state->mutex);
335
336 state->receiveExitedCv.wait(lock, [this]
337 {
338 return state->receiveExited;
339 });
340 }
341
343 {
344 {
345 std::lock_guard lock(state->mutex);
346 state->releaseReceive = true;
347 }
348
349 state->receiveCv.notify_one();
350 }
351
352 private:
353 std::shared_ptr<LifetimeTrackingSocketState> state;
354};
356{
357 std::mutex mutex;
358 std::condition_variable receiveEnteredCv;
359 std::condition_variable releaseReceiveCv;
360
361 bool receiveEntered{false};
362 bool releaseReceive{false};
363};
364} // namespace profinet::cyclic::test
Abstraction for sending and receiving raw Ethernet frames.
Abstract interface for raw Ethernet frame transport.
Bytes Recv() override
Receive one raw Ethernet frame.
void Send(const Bytes &frame) override
Send one complete raw Ethernet frame.
void SetTimeout(std::chrono::milliseconds timeout) override
Set the receive timeout.
Deterministic socket stub used by cyclic controller unit tests.
void QueueTimeout()
Queue a receive timeout result represented by an empty frame.
void SetRecvExceptionMessage(std::string message)
Configure the exception message used by Recv().
void SetSendThrows(bool enabled)
Configure whether Send() throws.
void SetTimeout(std::chrono::milliseconds value) override
Configure the receive timeout.
void OnSend(std::function< void()> callback)
Register a callback invoked after every successful Send().
void SetSendExceptionMessage(std::string message)
Configure the exception message used by Send().
const Bytes & SentFrame(std::size_t index) const
Return a transmitted frame.
std::chrono::milliseconds Timeout() const
Return the most recently configured socket timeout.
Bytes Recv() override
Receive the next queued frame or timeout.
std::deque< ReceiveAction > receiveActions
Queued receive actions consumed by Recv().
std::vector< Bytes > sentFrames
Frames successfully sent through the stub.
void QueueReceive(Bytes data)
Queue a received Ethernet frame.
std::size_t SentFrameCount() const
Return the number of frames sent through the stub.
void Send(const Bytes &data) override
Send an Ethernet frame through the stub.
std::chrono::milliseconds timeout
Timeout configured through SetTimeout().
std::function< void()> onSend
Callback invoked after a successful send.
void SetTimeout(std::chrono::milliseconds) override
Set the receive timeout.
LifetimeTrackingRawEthernetSocket(std::shared_ptr< LifetimeTrackingSocketState > state)
void Send(const Bytes &) override
Send one complete raw Ethernet frame.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
PROFINET protocol wire-format structures and serialization helpers.
Bytes data
Bytes returned by Recv() when no exception is requested.
Bytes frame
Declares Linux Ethernet, addressing, timing, and utility helpers used by the PROFINET stack.