PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
alarmListener.cpp
Go to the documentation of this file.
1
3
5
6#include <cstring>
7#include <iostream>
8#include <ranges>
9
11#include "profinet/exceptions.h"
12#include "profinet/udpSocket.h"
13
14namespace profinet
15{
16
17AlarmListener::AlarmListener(asio::io_context& ioContext, AlarmEndpoint endpoint, std::optional<MacAddress> controllerMac)
19 endpoint(std::move(endpoint)),
20 controllerMac(controllerMac.value_or(MacAddress{}))
21{
22}
23
28
29void AlarmListener::AddAlarmNotificationCallback(std::function<void(const AlarmNotification&)> callback)
30{
31 if (!callback)
32 {
33 return;
34 }
35 const std::lock_guard<std::mutex> lock(callbacksMutex);
36
37 alarmNotificationCallbacks.push_back(std::move(callback));
38}
39
40void AlarmListener::RemoveAlarmNotificationCallback(std::function<void(const AlarmNotification&)> callback)
41{
42 if (!callback)
43 {
44 return;
45 }
46 // Extract the raw function pointer from the passed callback
47 auto targetPtr = callback.target<void (*)(const AlarmNotification&)>();
48 if (!targetPtr || !*targetPtr)
49 {
50 return; // Passed callback is not a plain function pointer (e.g., a capturing lambda)
51 }
52
53 auto fnToMatch = *targetPtr;
54
55 // Erase matching callbacks
56 const std::lock_guard<std::mutex> lock(callbacksMutex);
57 std::erase_if(alarmNotificationCallbacks, [fnToMatch](const auto& cb)
58 {
59 auto target = cb.template target<void (*)(const AlarmNotification&)>();
60 return target && *target == fnToMatch;
61 });
62}
63
64void AlarmListener::AddRtaAlarmCallback(std::function<void(const RtaError&)> callback)
65{
66 if (!callback)
67 {
68 return;
69 }
70 const std::lock_guard<std::mutex> lock(callbacksMutex);
71 rtaAlarmCallbacks.push_back(std::move(callback));
72}
73
74void AlarmListener::RemoveRtaAlarmCallback(std::function<void(const RtaError&)> callback)
75{
76 if (!callback)
77 {
78 return;
79 }
80 // Extract the raw function pointer from the passed callback
81 auto targetPtr = callback.target<void (*)(const RtaError&)>();
82 if (!targetPtr || !*targetPtr)
83 {
84 return; // Passed callback is not a plain function pointer (e.g., a capturing lambda)
85 }
86
87 auto fnToMatch = *targetPtr;
88
89 // Erase matching callbacks
90 const std::lock_guard<std::mutex> lock(callbacksMutex);
91 std::erase_if(rtaAlarmCallbacks, [fnToMatch](const auto& cb)
92 {
93 auto target = cb.template target<void (*)(const RtaError&)>();
94 return target && *target == fnToMatch;
95 });
96}
97
98void AlarmListener::Start(std::unique_ptr<IRawEthernetSocket> eSocket, std::unique_ptr<IUdpSocket> uSocket)
99{
100 std::lock_guard lock(lifecycleMutex);
101
102 if (running)
103 {
104 return;
105 }
106
107 std::jthread worker;
108 try
109 {
110 if (endpoint.transport == 0)
111 {
112 if (eSocket == nullptr)
113 {
114 eSocket = std::make_unique<AsioEthernetSocket>(ioContext, endpoint.interface, ETHERTYPE_PROFINET);
115 }
116 static constexpr uint16_t timeoutMs = 1000;
117 eSocket->SetTimeout(std::chrono::milliseconds(timeoutMs));
118 }
119 else
120 {
121 if (uSocket == nullptr)
122 {
123 uSocket = std::make_unique<UdpSocket>(ALARM_UDP_PORT, std::chrono::seconds(1));
124 }
125 }
126 }
127 catch (...)
128 {
129 running = false;
130 eSocket.reset();
131 uSocket.reset();
132 throw;
133 }
134 l2Sock = std::move(eSocket);
135
136 udpSock = std::move(uSocket);
137
138 running = true;
139 worker = std::jthread([this]
140 { ListenLoop(); });
141 thread = std::move(worker);
142}
143
145{
146 std::scoped_lock lock(lifecycleMutex);
147
148 if (!running && !thread.joinable())
149 {
150 return;
151 }
152
153 running = false;
154
155 if (thread.joinable())
156 {
157 thread.join();
158 }
159
160 l2Sock.reset();
161 udpSock.reset();
162}
163
165{
166 while (running)
167 {
168 try
169 {
170 if (endpoint.transport == 0)
171 {
173 }
174 else
175 {
177 }
178 }
179 catch (const std::exception& e)
180 {
181 if (!running)
182 {
183 break;
184 }
185 // Keep the loop alive for non-fatal errors
186 // (parse failures, transient socket hiccups).
187 // euhm do i throw when udp or eth is failing...
188 std::cerr
189 << "Error: "
190 << e.what()
191 << "\n";
192 continue;
193 }
194 }
195}
196
198{
199 if (l2Sock == nullptr)
200 {
201 return;
202 }
203 Bytes data = l2Sock->Recv();
204 constexpr std::size_t macHeaderLength = 12U;
205 constexpr std::size_t etherTypeLength = 2U;
206 constexpr std::size_t frameIdLength = 2U;
207
208 if (data.size() < macHeaderLength + etherTypeLength + frameIdLength)
209 {
210 return;
211 }
212
213 MacAddress dstMac{};
214 MacAddress srcMac{};
215 std::size_t offset = 0;
216 std::ranges::copy(data.begin() + offset, data.begin() + offset + macAddressLength, dstMac.begin());
217 offset += macAddressLength;
218 std::ranges::copy(data.begin() + offset, data.begin() + offset + macAddressLength, srcMac.begin());
219 offset += macAddressLength;
220 if (srcMac != endpoint.deviceMac)
221 {
222 return;
223 }
224
225 // Returns the offset at which the EtherType starts.
226 const std::size_t etherTypeOffset = SkipVlanTags(data);
227
228 if (data.size() < etherTypeOffset + etherTypeLength + frameIdLength)
229 {
230 return;
231 }
232 wire::Reader r(data);
233 r.SkipTo(etherTypeOffset);
234 const auto ethertype = r.U16();
235 offset += sizeof(std::uint16_t);
236
237 if (ethertype != ETHERTYPE_PROFINET)
238 {
239 return;
240 }
241
242 const std::size_t frameIdOffset = etherTypeOffset + etherTypeLength;
243 r.SkipTo(frameIdOffset);
244 const auto frameId = r.U16();
245
246 const std::size_t rtaOffset = frameIdOffset + frameIdLength;
248 {
249 const auto alarmData = Bytes(data.begin() + rtaOffset, data.end());
250 ProcessAlarm(alarmData, true, srcMac, std::nullopt);
251 }
252 else if (frameId == FRAME_ID_ALARM_LOW)
253 {
254 const auto alarmData = Bytes(data.begin() + rtaOffset, data.end());
255 ProcessAlarm(alarmData, false, srcMac, std::nullopt);
256 }
257}
258
260{
261 if (!udpSock)
262 {
263 return;
264 }
265 const UdpDatagram datagram = udpSock->RecvFrom(RECEIVE_BUFFER_LENGTH);
266
267 if (datagram.data.empty() || datagram.data.size() < 28)
268 {
269 return;
270 }
271
272 ProcessAlarm(datagram.data, std::nullopt, std::nullopt, datagram.source);
273}
274
275void AlarmListener::ProcessAlarm(const Bytes& payload, std::optional<bool> highPriority,
276 std::optional<MacAddress> srcMac, std::optional<UdpEndpoint> udpSource)
277{
278 Bytes alarmData;
279
280 if (endpoint.transport == 0 && payload.size() >= 12)
281 {
282 // First check if it is a RTA ERR PDU (protocol error), then for a (error) message
283 PNRTAHeader rta;
284 try
285 {
286 rta = PNRTAHeader::Parse(Bytes(payload.begin(), payload.end()));
287 }
288 catch (const std::exception&)
289 {
290 return;
291 }
293 {
294 return;
295 }
297 alarmData = Bytes(payload.begin() + 12, payload.end());
298
299 auto version = (rta.pduType >> 4) & 0xFF;
300 if (version != PNRTAHeader::VERSION_1)
301 {
302 return;
303 }
304
306 {
307 return;
308 }
309 try
310 {
311 auto error = ParseRtaError(rta);
312 if (error.IsRtaProtocolError())
313 {
314 std::vector<std::function<void(const RtaError&)>> callbacksCopy;
315
316 {
317 std::scoped_lock lock(callbacksMutex);
318 callbacksCopy = rtaAlarmCallbacks;
319 }
320
321 for (const auto& cb : callbacksCopy)
322 {
323 try
324 {
325 cb(error);
326 }
327 catch (...)
328 {
329 // A misbehaving callback must not take down the listener thread.
330 }
331 }
332 SendTransportAck(srcMac, highPriority.has_value() ? *highPriority : false);
333 return;
334 }
335 }
336 catch (...)
337 {
338 // An Alarm notification is not a RTA alarm, continue.
339 }
340 // expected seqnr & SendNack maken
341 // ook test:DuplicateNotificationIsNotDispatchedTwice
342 // if rta_header.send_seq_num == self._exp_seq_num_o:
343 // # Retransmission of an already-accepted PDU: re-ack only
344 // self._send_transport_ack(src_mac, bool(high_priority))
345 // return
346 }
347 else
348 {
349 alarmData = payload;
350 }
351
352 AlarmNotification alarm;
353 try
354 {
355 alarm = ParseAlarmNotification(alarmData);
356 }
357 catch (const std::exception&)
358 {
359 return;
360 }
361 (void)highPriority; // frame-ID priority is informational only; block_type is authoritative (matches upstream).
362
363 SendAck(alarm, srcMac, udpSource);
364
365 std::vector<std::function<void(const AlarmNotification&)>> callbacksCopy;
366
367 {
368 std::scoped_lock lock(callbacksMutex);
369 callbacksCopy = alarmNotificationCallbacks;
370 }
371
372 for (const auto& cb : callbacksCopy)
373 {
374 try
375 {
376 cb(alarm);
377 }
378 catch (...)
379 {
380 // A misbehaving callback must not take down the listener thread.
381 }
382 }
383}
384
385void AlarmListener::SendTransportAck(std::optional<MacAddress> srcMac, bool highPriority)
386{
389 {}, srcMac.value_or(endpoint.deviceMac), highPriority);
390}
391
392void AlarmListener::SendNack(std::optional<MacAddress> srcMac, bool highPriority)
393{
396 {}, srcMac.value_or(endpoint.deviceMac), highPriority);
397}
398
399void AlarmListener::SendAck(const AlarmNotification& alarm, std::optional<MacAddress> srcMac, std::optional<UdpEndpoint> udpSource)
400{
402
403 PNBlockHeader header;
404 header.blockType = blockType;
405 header.blockLength = static_cast<std::uint16_t>(PNAlarmAckPDU::kSize - 4);
406 header.blockVersionHigh = 0x01;
407 header.blockVersionLow = 0x00;
408
409 auto alarmSpecifier =
410 static_cast<std::uint16_t>((alarm.alarmSequenceNumber & AlarmNotificationBitmask::AlarmSequenceNumber) |
415
416 PNAlarmAckPDU ack;
417 std::ranges::copy(header.ToBytes() | std::views::take(blockHeaderLength), ack.blockHeader.data());
418 ack.alarmType = alarm.alarmType;
419 ack.api = alarm.api;
420 ack.slotNumber = alarm.slotNumber;
421 ack.subslotNumber = alarm.subslotNumber;
422 ack.alarmSpecifier = alarmSpecifier;
423 ack.statusPNIO = 0;
424
425 const Bytes ackData = ack.ToBytes();
426
427 if (endpoint.transport == 0)
428 {
429 SendLayer2Ack(ackData, srcMac.value_or(endpoint.deviceMac), alarm.IsHighPriority());
430 }
431 else if (udpSource && udpSource.has_value())
432 {
433 SendUdpAck(ackData, *udpSource);
434 }
435}
436namespace
437{
438constexpr std::uint16_t kVlanTpid = 0x8100;
439constexpr std::uint16_t kVlanPcp6Vid0 = 0xC000;
440constexpr std::uint16_t kVlanPcp5Vid0 = 0xA000;
441} // namespace
442void AlarmListener::SendLayer2Ack(const Bytes& ackData, const MacAddress& dstMac, bool highPriority)
443{
446 ackData, dstMac, highPriority);
447}
448
449void AlarmListener::SendRtaFrame(const std::uint8_t rtaType,
450 const std::uint8_t addFlags,
451 const Bytes& ackData, const MacAddress& dstMac, bool highPriority)
452{
453 if (l2Sock == nullptr)
454 {
455 return;
456 }
457
458 sendSeqNum = static_cast<std::uint16_t>((sendSeqNum + 1) & 0xFFFF);
459
460 PNRTAHeader rta;
463 // rta.pduType = static_cast<std::uint8_t>((PNRTAHeader::RTA_TYPE_DATA << 4) | PNRTAHeader::VERSION_1);
464 rta.pduType = static_cast<std::uint8_t>((PNRTAHeader::VERSION_1 << 4) | rtaType);
465 rta.addFlags = addFlags;
467 rta.ackSeqNum = recvSeqNum;
468 rta.variablePartLenght = static_cast<std::uint16_t>(ackData.size());
469
470 Bytes frame;
471 frame.insert(frame.end(), dstMac.begin(), dstMac.end());
472 frame.insert(frame.end(), controllerMac.begin(), controllerMac.end());
473 // add VLAN tag?
474 const std::uint16_t vlanTci = highPriority ? kVlanPcp6Vid0 : kVlanPcp5Vid0;
475
476 wire::PutBigEndian(frame, kVlanTpid);
477 wire::PutBigEndian(frame, vlanTci);
479
480 const std::uint16_t frameId = highPriority ? FRAME_ID_ALARM_HIGH : FRAME_ID_ALARM_LOW;
482
483 Bytes rtaBytes = rta.ToBytes();
484 frame.insert(frame.end(), rtaBytes.begin(), rtaBytes.end());
485 frame.insert(frame.end(), ackData.begin(), ackData.end());
486
487 try
488 {
489 l2Sock->Send(frame);
490 }
491 catch (const std::exception&)
492 {
493 // log?
494 }
495}
496
497void AlarmListener::SendUdpAck(const Bytes& ackData, const UdpEndpoint& dstAddr)
498{
499 if (udpSock == nullptr)
500 {
501 return;
502 }
503 udpSock->SendTo(ackData, dstAddr);
504}
505
506} // namespace profinet
Declares the PROFINET RTA alarm listener and AlarmAck transmission support.
Asio-backed platform-independent raw Ethernet transport.
std::atomic< bool > running
Whether the listener thread should keep running.
AlarmListener(asio::io_context &ioContext, AlarmEndpoint endpoint, std::optional< MacAddress > controllerMac=std::nullopt)
Construct a listener for the given endpoint.
asio::io_context & ioContext
Asio execution context used by the Layer-2 socket.
AlarmEndpoint endpoint
Alarm endpoint configuration.
void RemoveAlarmNotificationCallback(std::function< void(const AlarmNotification &)> callback) override
Remove a registered (alarm notification) callback.
void SendNack(std::optional< MacAddress > srcMac, bool highPriority)
Send an RTA-NACK for an out-of-sequence DATA PDU.
void RemoveRtaAlarmCallback(std::function< void(const RtaError &)> callback) override
Remove a registered (rta alarm) callback.
void HandleUdpFrame()
Receive and process one UDP frame.
~AlarmListener() override
Stop (if running) and release resources.
void Stop() override
Stop the listener.
std::uint16_t recvSeqNum
Last received sequence number, for duplicate detection.
std::unique_ptr< IRawEthernetSocket > l2Sock
Raw Ethernet transport used for Layer 2 alarm reception.
MacAddress controllerMac
This host's MAC address.
std::uint16_t sendSeqNum
Sequence number for outgoing AlarmAck-PDUs.
void HandleLayer2Frame()
Receive and process one Layer 2 (RTA-PDU) frame.
void AddAlarmNotificationCallback(std::function< void(const AlarmNotification &)> callback) override
Register a callback for received alarms.
void SendAck(const AlarmNotification &alarm, std::optional< MacAddress > srcMac, std::optional< UdpEndpoint > udpSource)
Build and send an AlarmAck-PDU for a received notification.
void ListenLoop()
Listener thread body: dispatches to the Layer 2 or UDP handler.
std::mutex lifecycleMutex
Guards lifecycle steps of listener against concurrent access.
std::jthread thread
Background thread receiving and processing alarms.
std::vector< std::function< void(const RtaError &)> > rtaAlarmCallbacks
Registered callbacks invoked for successfully parsed rta alarms.
void AddRtaAlarmCallback(std::function< void(const RtaError &)> callback) override
Register a callback for received alarms.
void SendTransportAck(std::optional< MacAddress > srcMac, bool highPriority)
Send a pure RTA-ACK (TACK) with empty var part.
void SendLayer2Ack(const Bytes &ackData, const MacAddress &dstMac, bool highPriority)
Send an AlarmAck-PDU over the Layer 2 transport.
void SendRtaFrame(std::uint8_t pduType, std::uint8_t addFlags, const Bytes &ackData, const MacAddress &dstMac, bool highPriority)
Build and send an RTA PDU (Layer 2); returns the frame bytes.
void Start(std::unique_ptr< IRawEthernetSocket > eSocket=nullptr, std::unique_ptr< IUdpSocket > uSocket=nullptr) override
Start the background listener (socket + thread).
void ProcessAlarm(const Bytes &payload, std::optional< bool > highPriority, std::optional< MacAddress > srcMac, std::optional< UdpEndpoint > udpSource)
Parse an alarm payload and dispatch it to registered callbacks.
std::unique_ptr< IUdpSocket > udpSock
UDP transport used for UDP alarm reception and acknowledgement.
std::vector< std::function< void(const AlarmNotification &)> > alarmNotificationCallbacks
Registered callbacks invoked for successfully parsed alarmsnotifications.
void SendUdpAck(const Bytes &ackData, const UdpEndpoint &dstAddr)
Send an AlarmAck-PDU over the UDP transport.
std::mutex callbacksMutex
Protects concurrent access to callbacks.
Lightweight cursor for parsing a big-endian byte buffer without copies.
Definition wire.h:192
void SkipTo(std::size_t offset)
Move the read position to an absolute byte offset.
Definition wire.h:343
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
Definition wire.h:258
Declares exceptions and error types used by the PROFINET IO controller stack.
void PutBigEndian(std::vector< std::uint8_t > &out, T value)
Append an unsigned integral value to a buffer in big-endian order.
Definition wire.h:45
BlockType
PROFINET block types.
Definition indices.h:89
@ AlarmAcknowledgementLow
Alarm acknowledgement low block.
@ AlarmAcknowledgementHigh
Alarm acknowledgement high block.
constexpr int blockHeaderLength
Constant lenght of a blockHeader.
Definition protocol.h:58
@ ManufacturerSpecific
Bit 12: Indicates manufacturer-specific alarm details are present.
@ AlarmSequenceNumber
Bit mask extracting the alarm sequence number (bits 0–10, range 0–2047).
@ SubmoduleDiagnosisState
Bit 13: Indicates submodule diagnosis state is present.
@ ArDiagnosisState
Bit 14: Indicates Application Relation (AR) diagnosis state is present.
@ ChannelDiagnosis
Bit 15: Indicates channel diagnosis details are present.
AlarmNotification ParseAlarmNotification(const Bytes &data)
Parse a complete AlarmNotification PDU (BlockHeader + body + items).
Definition alarms.cpp:245
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
constexpr std::uint16_t ALARM_UDP_PORT
UDP port used for alarm transport when Transport == 1.
RtaError ParseRtaError(const PNRTAHeader &header)
Parse an RTA ERR-PDU from an already parsed RTA header.
Definition protocol.h:2680
std::size_t SkipVlanTags(std::span< const std::uint8_t > frame) noexcept
Return the byte offset of the real EtherType in a raw Ethernet frame.
Definition util.cpp:70
constexpr int macAddressLength
Constant lenght of a mac address.
Definition util.h:37
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
constexpr std::size_t RECEIVE_BUFFER_LENGTH
Receive buffer length.
Definition util.h:43
constexpr std::uint16_t FRAME_ID_ALARM_HIGH
Frame ID used for high-priority alarms.
constexpr std::uint16_t ETHERTYPE_PROFINET
EtherType used by PROFINET RTA frames (0x8892).
constexpr std::uint16_t FRAME_ID_ALARM_LOW
Frame ID used for low-priority alarms.
asio::error_code error
Alarm endpoint configuration.
int transport
Transport: 0 = Layer2 (RTA), 1 = UDP.
std::string interface
Network interface name to bind to, e.g. "eth0".
std::uint16_t controllerRef
Controller's local alarm reference (from AlarmCRBlockReq).
std::uint16_t deviceRef
Device's local alarm reference (from AlarmCRBlockRes).
MacAddress deviceMac
The device's MAC address.
Complete parsed AlarmNotification PDU: header fields + parsed items.
Definition alarms.h:356
bool manufacturerSpecific
Whether the alarm specifier indicates a manufacturer-specific diagnosis.
Definition alarms.h:388
std::uint16_t slotNumber
Slot number where the alarm originated.
Definition alarms.h:370
std::uint16_t alarmSequenceNumber
Sequence number extracted from the alarm specifier.
Definition alarms.h:382
std::uint32_t api
API number.
Definition alarms.h:367
AlarmType alarmType
Alarm type (Diagnosis/Process/Pull/Plug/...).
Definition alarms.h:364
std::uint16_t subslotNumber
Subslot number where the alarm originated.
Definition alarms.h:373
bool IsHighPriority() const
Whether this is a high-priority notification.
Definition alarms.h:404
bool channelDiagnosis
Whether the alarm specifier indicates channel diagnosis is present.
Definition alarms.h:385
bool arDiagnosisState
Whether the alarm specifier indicates AR diagnosis state.
Definition alarms.h:394
bool submoduleDiagnosisState
Whether the alarm specifier indicates submodule diagnosis state.
Definition alarms.h:391
AlarmAck-PDU: acknowledges receipt of an AlarmNotification-PDU.
Definition protocol.h:2347
std::uint16_t alarmSpecifier
Alarm specifier flags echoed from the notification.
Definition protocol.h:2364
AlarmType alarmType
Alarm type being acknowledged.
Definition protocol.h:2352
std::uint16_t slotNumber
Slot number being acknowledged.
Definition protocol.h:2358
std::array< std::uint8_t, blockHeaderLength > blockHeader
Nested 6-byte block header.
Definition protocol.h:2349
std::uint32_t statusPNIO
PNIO status code (0 = acknowledged OK).
Definition protocol.h:2367
std::uint16_t subslotNumber
Subslot number being acknowledged.
Definition protocol.h:2361
static constexpr std::size_t kSize
Size in bytes of this PDU.
Definition protocol.h:2370
std::uint32_t api
API number.
Definition protocol.h:2355
Bytes ToBytes() const
Serialize this PDU back to raw bytes.
Definition protocol.h:2374
Bytes ToBytes() const
Serialize this header back to raw bytes.
Definition protocol.h:1347
BlockType blockType
Block type identifier.
Definition protocol.h:1308
std::uint8_t blockVersionHigh
Block format major version.
Definition protocol.h:1317
std::uint8_t blockVersionLow
Block format minor version.
Definition protocol.h:1320
std::uint16_t blockLength
Length in bytes of the block body (excludes Type/Length fields).
Definition protocol.h:1311
RTA-PDU Header - Real-Time Acyclic PDU for Layer 2 alarm transport.
Definition protocol.h:2390
static constexpr std::uint8_t ADD_FLAGS_TACK
TACK(transport ack request) in bit 4(IEC 61158-6-10; cf. p-net pf_put_alarm_fixed)
Definition protocol.h:2414
std::uint16_t sendSeqNum
Sender's sequence number for this PDU.
Definition protocol.h:2431
std::uint16_t alarmSrcEndpoint
Source alarm endpoint (AlarmCR reference on the sender).
Definition protocol.h:2422
Bytes ToBytes() const
Serialize this header and variable part back to raw bytes.
Definition protocol.h:2469
std::uint16_t alarmDstEndpoint
Destination alarm endpoint (AlarmCR reference on the receiver).
Definition protocol.h:2419
static constexpr std::uint8_t RTA_TYPE_ACK
PDU type: ACK.
Definition protocol.h:2399
std::uint16_t variablePartLenght
Length in bytes of the variable part (Payload).
Definition protocol.h:2437
std::uint8_t addFlags
Additional flags (window size, TACK flag).
Definition protocol.h:2428
static PNRTAHeader Parse(const Bytes &data)
Parse an RTA header and variable part from raw bytes.
Definition protocol.h:2448
static constexpr std::uint8_t RTA_TYPE_DATA
PDU type: DATA.
Definition protocol.h:2393
static constexpr std::uint8_t ADD_FLAGS_WINDOW_1
RTA AddFlags bits: window size in bits 0-3.
Definition protocol.h:2411
static constexpr std::uint8_t VERSION_1
RTA protocol version 1.
Definition protocol.h:2405
static constexpr std::uint8_t RTA_TYPE_NACK
PDU type: NACK.
Definition protocol.h:2396
std::uint16_t ackSeqNum
Sequence number being acknowledged.
Definition protocol.h:2434
std::uint8_t pduType
PDU type and protocol version, packed as type(bits 4-7) + version(bits 0-3).
Definition protocol.h:2425
Decoded PROFINET RTA ERR-PDU.
Definition protocol.h:2571
A UDP datagram together with the IPv4 address of its sender.
Definition IUdpSocket.h:65
std::vector< std::uint8_t > data
Datagram payload.
Definition IUdpSocket.h:67
UdpEndpoint source
IPv4 address and UDP port of the datagram sender.
Definition IUdpSocket.h:70
Portable IPv4 UDP endpoint.
Definition IUdpSocket.h:42
std::uint8_t addFlags
RTA AdditionalFlags field.
std::uint16_t frameId
Ethernet FrameID.
Bytes frame
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
Bytes payload
Definition test_dcp.cpp:392
Declares the portable IPv4 UDP socket implementation.