PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_alarmListenerRta.cpp
Go to the documentation of this file.
1
19
20#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22
23#include <array>
24#include <cstdint>
25#include <memory>
26#include <span>
27#include <string>
28#include <utility>
29#include <vector>
30
34#include "profinet/alarms.h"
35#include "profinet/protocol.h"
36
37namespace profinet
38{
39
40namespace
41{
42
43using ::testing::_;
44using ::testing::StrictMock;
45
46constexpr std::array<std::uint8_t, 6> kControllerMac{0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
47
48constexpr std::array<std::uint8_t, 6> kDeviceMac{0x02, 0x00, 0x00, 0x00, 0x00, 0x01};
49
50constexpr std::uint16_t kVlanTpid = 0x8100;
51constexpr std::uint16_t kVlanPcp6Vid0 = 0xC000;
52constexpr std::uint16_t kVlanPcp5Vid0 = 0xA000;
53constexpr std::uint16_t kProfinetEtherType = 0x8892;
54
55constexpr std::uint16_t kControllerReference = 1;
56constexpr std::uint16_t kDeviceReference = 42;
57
58constexpr std::uint8_t kRtaVersion = 1;
59
60constexpr std::uint8_t kRtaTypeData = 0x01;
61constexpr std::uint8_t kRtaTypeAck = 0x03;
62constexpr std::uint8_t kRtaTypeNack = 0x02;
63constexpr std::uint8_t kRtaTypeErr = 0x0F; // niet 0x04?
64
65constexpr std::uint8_t kAddFlagsWindow1 = 0x80;
66constexpr std::uint8_t kAddFlagsTack = 0x10;
67
71Bytes MakeChannelDiagnosisItem()
72{
73 Bytes item;
74
75 wire::PutU16(item, 0x8000);
76 wire::PutU16(item, 1);
77 wire::PutU16(item, 0x0800);
78 wire::PutU16(item, 9);
79
80 return item;
81}
89[[nodiscard]] Bytes BuildAlarmBlock(bool highPriority = false, std::uint16_t specifier = 0x0401, std::span<const std::uint8_t> items = {})
90{
91 Bytes body;
92
93 wire::PutU16(body, 0x0001); // AlarmType
94 wire::PutU32(body, 0x00000000); // API
95 wire::PutU16(body, 0x0001); // Slot
96 wire::PutU16(body, 0x0001); // Subslot
97 wire::PutU32(body, 0x00000001); // ModuleIdent
98 wire::PutU32(body, 0x00000001); // SubmoduleIdent
99 wire::PutU16(body, specifier); // AlarmSpecifier
100
101 EXPECT_EQ(body.size(), 20U);
102
103 Bytes rest{0x01, 0x00};
104
105 rest.insert(rest.end(), body.begin(), body.end());
106 rest.insert(rest.end(), items.begin(), items.end());
107
108 const std::uint16_t blockType = highPriority ? 0x0001 : 0x0002;
109
110 Bytes block;
111
112 wire::PutU16(block, blockType);
113 wire::PutU16(block, static_cast<std::uint16_t>(rest.size()));
114 block.insert(block.end(), rest.begin(), rest.end());
115
116 return block;
117}
118
133[[nodiscard]] Bytes BuildRtaFrame(
134 std::uint8_t pduType,
135 std::uint8_t addFlags,
136 std::uint16_t sendSeq,
137 std::uint16_t ackSeq,
138 std::span<const std::uint8_t> varPart = {},
139 std::uint16_t dstRef = kControllerReference,
140 std::uint16_t srcRef = kDeviceReference,
141 std::uint8_t version = kRtaVersion,
142 bool vlan = false,
143 std::uint16_t frameId = FRAME_ID_ALARM_LOW)
144{
145 Bytes rta;
146
147 wire::PutU16(rta, dstRef);
148 wire::PutU16(rta, srcRef);
149 rta.push_back(static_cast<std::uint8_t>((version << 4U) | pduType));
150 rta.push_back(addFlags);
151 wire::PutU16(rta, sendSeq);
152 wire::PutU16(rta, ackSeq);
153 wire::PutU16(rta, static_cast<std::uint16_t>(varPart.size()));
154 rta.insert(rta.end(), varPart.begin(), varPart.end());
155
156 Bytes frame;
157
158 // Device -> controller.
159 frame.insert(frame.end(), kControllerMac.begin(), kControllerMac.end());
160 frame.insert(frame.end(), kDeviceMac.begin(), kDeviceMac.end());
161
162 if (vlan)
163 {
164 wire::PutU16(frame, kVlanTpid);
165 wire::PutU16(frame, kVlanPcp6Vid0);
166 }
167
168 wire::PutU16(frame, kProfinetEtherType);
170 frame.insert(frame.end(), rta.begin(), rta.end());
171
172 return frame;
173}
174
181struct SentRtaFrame
182{
184 std::uint16_t frameId = 0;
185
187 std::uint16_t dstRef = 0;
188
190 std::uint16_t srcRef = 0;
191
193 std::uint8_t pduType = 0;
194
196 std::uint8_t addFlags = 0;
197
199 std::uint16_t sendSeq = 0;
200
202 std::uint16_t ackSeq = 0;
203
206};
207
212[[nodiscard]] SentRtaFrame ParseSentRta(std::span<const std::uint8_t> frame)
213{
214 EXPECT_GE(frame.size(), 32U);
215 if (frame.size() < 32U)
216 {
217 return {}; // Return empty frame on size failure
218 }
219
220 wire::Reader r(frame);
221
222 // EXPECT_EQ((MacAddress{frame[0], frame[1], frame[2], frame[3], frame[4], frame[5]}), kDeviceMac);
223 // EXPECT_EQ((MacAddress{frame[6], frame[7], frame[8], frame[9], frame[10], frame[11]}), kControllerMac);
224 EXPECT_EQ(r.Fixed<macAddressLength>(), kDeviceMac);
225 EXPECT_EQ(r.Fixed<macAddressLength>(), kControllerMac);
226 EXPECT_EQ(r.U16(), kVlanTpid);
227
228 const std::uint16_t tci = r.U16();
229 EXPECT_TRUE(tci == kVlanPcp6Vid0 || tci == kVlanPcp5Vid0);
230
231 EXPECT_EQ(r.U16(), kProfinetEtherType);
232
233 SentRtaFrame result;
234 result.frameId = r.U16();
235 result.dstRef = r.U16();
236 result.srcRef = r.U16();
237 result.pduType = r.U8();
238 result.addFlags = r.U8();
239 result.sendSeq = r.U16();
240 result.ackSeq = r.U16();
241
242 const std::uint16_t variablePartLength = r.U16();
243 EXPECT_EQ(frame.size(), 32U + variablePartLength);
244
245 if (frame.size() >= 32U + variablePartLength)
246 {
247 result.varPart.assign(frame.begin() + 32, frame.begin() + 32 + variablePartLength);
248 }
249
250 return result;
251}
252
253} // namespace
254
262class AlarmListenerRtaTest : public ::testing::Test
263{
264 protected:
266 void SetUp() override
267 {
268 socket = std::make_unique<StrictMock<MockRawEthernetSocket>>();
269
270 mockSocket = socket.get();
271
272 AlarmEndpoint endpoint;
273 endpoint.interface = "eth0";
274 endpoint.controllerRef = kControllerReference;
275 endpoint.deviceRef = kDeviceReference;
276 endpoint.deviceMac = kDeviceMac;
277 endpoint.transport = 0;
278 ioContext = std::make_unique<asio::io_context>();
279 listener = std::make_unique<AlarmListener>(*ioContext, endpoint, kControllerMac);
281 }
282
287 {
288 EXPECT_CALL(*mockSocket, Recv()).WillOnce(::testing::Return(std::move(frame)));
289
291 }
292
299 [[nodiscard]] static SentRtaFrame ParseSentFrame(std::span<const std::uint8_t> frame)
300 {
301 return ParseSentRta(frame);
302 }
304 std::unique_ptr<testing::StrictMock<MockRawEthernetSocket>> socket;
305
307 StrictMock<MockRawEthernetSocket>* mockSocket = nullptr;
308
310 std::unique_ptr<AlarmListener> listener;
311
313 std::unique_ptr<asio::io_context> ioContext;
314};
315
323class AlarmListenerRtaIntegrationTest : public ::testing::Test
324{
325 protected:
327 void SetUp() override
328 {
329 socket = std::make_unique<StrictMock<MockRawEthernetSocket>>();
330
331 mockSocket = socket.get();
332
333 AlarmEndpoint endpoint;
334 endpoint.interface = "eth0";
335 endpoint.controllerRef = kControllerReference;
336 endpoint.deviceRef = kDeviceReference;
337 endpoint.deviceMac = kDeviceMac;
338 endpoint.transport = 0;
339 ioContext = std::make_unique<asio::io_context>();
340
341 listener = std::make_unique<AlarmListener>(*ioContext, endpoint, kControllerMac);
343 }
344
349 {
350 EXPECT_CALL(*mockSocket, Recv()).WillOnce(::testing::Return(std::move(frame)));
352 }
353
355 std::unique_ptr<testing::StrictMock<MockRawEthernetSocket>> socket;
356
358 StrictMock<MockRawEthernetSocket>* mockSocket = nullptr;
359
361 std::unique_ptr<AlarmListener> listener;
362
364 std::unique_ptr<asio::io_context> ioContext;
365};
366
372class AlarmListenerCallbackTest : public ::testing::Test
373{
374 protected:
376 void SetUp() override
377 {
378 socket = std::make_unique<StrictMock<MockRawEthernetSocket>>();
379 mockSocket = socket.get();
380
381 AlarmEndpoint endpoint;
382 endpoint.interface = "eth0";
383 endpoint.controllerRef = kControllerReference;
384 endpoint.deviceRef = kDeviceReference;
385 endpoint.deviceMac = kDeviceMac;
386 endpoint.transport = 0;
387 ioContext = std::make_unique<asio::io_context>();
388 listener = std::make_unique<AlarmListener>(*ioContext, endpoint, kControllerMac);
390 }
391
396 {
397 EXPECT_CALL(*mockSocket, Recv()).WillOnce(::testing::Return(std::move(frame)));
399 }
400
402 std::unique_ptr<testing::StrictMock<MockRawEthernetSocket>> socket;
403
405 StrictMock<MockRawEthernetSocket>* mockSocket = nullptr;
406
408 std::unique_ptr<AlarmListener> listener;
409
411 std::unique_ptr<asio::io_context> ioContext;
412};
413
415TEST_F(AlarmListenerRtaTest, UntaggedAlarmFrameIsProcessed)
416{
417 const Bytes alarm = BuildAlarmBlock();
418
419 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
420 {
421 const SentRtaFrame sent = ParseSentFrame(frame);
422
423 EXPECT_EQ(sent.frameId, FRAME_ID_ALARM_LOW);
424 EXPECT_EQ(sent.pduType, 0x11);
425 EXPECT_EQ(sent.dstRef, kDeviceReference);
426 EXPECT_EQ(sent.srcRef, kControllerReference);
427 EXPECT_EQ(sent.varPart.size(), alarm.size() - 4U); //+ 4U ff beter naar kijken
428 });
429
430 Feed(BuildRtaFrame(kRtaTypeData,
431 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
432 0xFFFF,
433 0xFFFE,
434 alarm,
435 kControllerReference,
436 kDeviceReference,
437 kRtaVersion,
438 false,
440}
441
443TEST_F(AlarmListenerRtaTest, VlanTaggedAlarmFrameIsProcessed)
444{
445 const Bytes alarm = BuildAlarmBlock();
446 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
447 {
448 const SentRtaFrame sent = ParseSentFrame(frame);
449 EXPECT_EQ(sent.frameId, FRAME_ID_ALARM_LOW);
450 EXPECT_EQ(sent.pduType, 0x11);
451 });
452 Feed(BuildRtaFrame(kRtaTypeData,
453 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
454 0xFFFF,
455 0xFFFE,
456 alarm,
457 kControllerReference,
458 kDeviceReference,
459 kRtaVersion,
460 true,
462}
463
465TEST_F(AlarmListenerRtaTest, HighPriorityAlarmUsesHighFrameId)
466{
467 const Bytes alarm = BuildAlarmBlock(true);
468
469 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
470 {
471 const SentRtaFrame sent = ParseSentFrame(frame);
472
473 EXPECT_EQ(sent.frameId, FRAME_ID_ALARM_HIGH);
474 EXPECT_EQ(sent.pduType, 0x11);
475 });
476
477 Feed(BuildRtaFrame(kRtaTypeData,
478 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
479 0xFFFF,
480 0xFFFE,
481 alarm,
482 kControllerReference,
483 kDeviceReference,
484 kRtaVersion,
485 true,
487}
488
490TEST_F(AlarmListenerRtaTest, NonProfinetEtherTypeIsIgnored)
491{
492 Bytes frame = BuildRtaFrame(kRtaTypeData,
493 0,
494 0xFFFF,
495 0xFFFE,
496 BuildAlarmBlock());
497
498 // EtherType starts at byte 12 for this untagged frame.
499 frame[12] = 0x08;
500 frame[13] = 0x00;
501
502 EXPECT_CALL(*mockSocket, Send(_)).Times(0);
503
504 Feed(std::move(frame));
505}
506
508TEST_F(AlarmListenerRtaTest, WrongSourceMacIsIgnored)
509{
510 Bytes frame = BuildRtaFrame(kRtaTypeData,
511 0,
512 0xFFFF,
513 0xFFFE,
514 BuildAlarmBlock());
515
516 // Replace source MAC with a different device.
517 frame[6] = 0xAA;
518 frame[7] = 0xBB;
519 frame[8] = 0xCC;
520 frame[9] = 0xDD;
521 frame[10] = 0xEE;
522 frame[11] = 0xFF;
523
524 EXPECT_CALL(*mockSocket, Send(_)).Times(0);
525
526 Feed(std::move(frame));
527}
528
530TEST_F(AlarmListenerRtaTest, WrongDestinationReferenceIsIgnored)
531{
532 EXPECT_CALL(*mockSocket, Send(_)).Times(0);
533
534 Feed(BuildRtaFrame(kRtaTypeData,
535 static_cast<std::uint8_t>(
536 kAddFlagsWindow1 |
537 kAddFlagsTack),
538 0xFFFF,
539 0xFFFE,
540 BuildAlarmBlock(),
541 99,
542 kDeviceReference));
543}
544
546TEST_F(AlarmListenerRtaTest, ShortEthernetFrameIsIgnored)
547{
548 EXPECT_CALL(*mockSocket, Send(_)).Times(0);
549
550 Feed(Bytes(15U, 0x00));
551}
552
554TEST_F(AlarmListenerRtaTest, AlarmAckIsPriorityVlanTagged)
555{
556 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
557 {
558 ASSERT_GE(frame.size(), 20U);
560 const MacAddress devMac = r.Fixed<macAddressLength>();
561 const MacAddress controllerMac = r.Fixed<macAddressLength>();
562
563 EXPECT_EQ(r.U16(), kVlanTpid);
564
565 const std::uint16_t tci = r.U16();
566
567 EXPECT_TRUE(tci == kVlanPcp6Vid0 || tci == kVlanPcp5Vid0);
568
569 EXPECT_EQ(r.U16(), kProfinetEtherType);
570 });
571
572 Feed(BuildRtaFrame(kRtaTypeData,
573 static_cast<std::uint8_t>(
574 kAddFlagsWindow1 |
575 kAddFlagsTack),
576 0xFFFF,
577 0xFFFE,
578 BuildAlarmBlock()));
579}
580
582TEST_F(AlarmListenerRtaTest, AlarmAckUsesDataPdu)
583{
584 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
585 {
586 const SentRtaFrame sent = ParseSentFrame(frame);
587
588 EXPECT_EQ(sent.pduType, 0x11);
589 });
590
591 Feed(BuildRtaFrame(kRtaTypeData,
592 static_cast<std::uint8_t>(
593 kAddFlagsWindow1 |
594 kAddFlagsTack),
595 0xFFFF,
596 0xFFFE,
597 BuildAlarmBlock()));
598}
599
601TEST_F(AlarmListenerRtaTest, AlarmAckUsesDeviceEndpointAsDestination)
602{
603 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
604 {
605 const SentRtaFrame sent = ParseSentFrame(frame);
606
607 EXPECT_EQ(sent.dstRef, kDeviceReference);
608
609 EXPECT_EQ(sent.srcRef, kControllerReference);
610 });
611
612 Feed(BuildRtaFrame(kRtaTypeData,
613 static_cast<std::uint8_t>(
614 kAddFlagsWindow1 |
615 kAddFlagsTack),
616 0xFFFF,
617 0xFFFE,
618 BuildAlarmBlock()));
619}
620
622TEST_F(AlarmListenerRtaTest, AlarmAckEchoesReceivedSequenceNumber)
623{
624 constexpr std::uint16_t kReceivedSequence = 0x1234;
625
626 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
627 {
628 const SentRtaFrame sent = ParseSentFrame(frame);
629
630 EXPECT_EQ(sent.ackSeq, kReceivedSequence);
631 });
632
633 Feed(BuildRtaFrame(kRtaTypeData,
634 static_cast<std::uint8_t>(
635 kAddFlagsWindow1 |
636 kAddFlagsTack),
637 kReceivedSequence,
638 0xFFFE,
639 BuildAlarmBlock()));
640}
641
643TEST_F(AlarmListenerCallbackTest, ReceivedAlarmInvokesCallback)
644{
645 std::vector<AlarmNotification> received;
646
647 listener->AddAlarmNotificationCallback([&received](const AlarmNotification& alarm)
648 {
649 received.push_back(alarm);
650 });
651
652 EXPECT_CALL(*mockSocket, Send(_)).Times(1);
653
654 Feed(BuildRtaFrame(kRtaTypeData,
655 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
656 0xFFFF,
657 0xFFFE,
658 BuildAlarmBlock()));
659
660 ASSERT_EQ(received.size(), 1U);
661 EXPECT_EQ(received[0].slotNumber, 1U);
662}
663
665TEST_F(AlarmListenerCallbackTest, MultipleCallbacksAreInvoked)
666{
667 std::uint32_t callback1Count = 0;
668 std::uint32_t callback2Count = 0;
669
670 listener->AddAlarmNotificationCallback([&callback1Count](const AlarmNotification&)
671 { ++callback1Count; });
672 listener->AddAlarmNotificationCallback([&callback2Count](const AlarmNotification&)
673 { ++callback2Count; });
674
675 EXPECT_CALL(*mockSocket, Send(_)).Times(1);
676
677 Feed(BuildRtaFrame(kRtaTypeData,
678 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
679 0xFFFF,
680 0xFFFE,
681 BuildAlarmBlock()));
682
683 EXPECT_EQ(callback1Count, 1U);
684 EXPECT_EQ(callback2Count, 1U);
685}
686
688TEST_F(AlarmListenerCallbackTest, EmptyCallbackIsIgnored)
689{
690 listener->AddAlarmNotificationCallback({});
691
692 EXPECT_CALL(*mockSocket, Send(_)).Times(1);
693
694 Feed(BuildRtaFrame(kRtaTypeData,
695 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
696 0xFFFF,
697 0xFFFE,
698 BuildAlarmBlock()));
699}
700
702TEST_F(AlarmListenerCallbackTest, CallbackExceptionDoesNotPreventOtherCallbacks)
703{
704 std::uint32_t callbackCount = 0;
705
706 listener->AddAlarmNotificationCallback([](const AlarmNotification&)
707 {
708 throw std::runtime_error("test callback failure");
709 });
710
711 listener->AddAlarmNotificationCallback([&callbackCount](const AlarmNotification&)
712 { ++callbackCount; });
713
714 EXPECT_CALL(*mockSocket, Send(_)).Times(1);
715
716 EXPECT_NO_THROW(Feed(BuildRtaFrame(kRtaTypeData,
717 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
718 0xFFFF,
719 0xFFFE,
720 BuildAlarmBlock())));
721
722 EXPECT_EQ(callbackCount, 1U);
723}
724
726TEST_F(AlarmListenerRtaIntegrationTest, TwoAlarmNotificationsAreDispatched)
727{
728 std::uint32_t alarmCount = 0;
729
730 listener->AddAlarmNotificationCallback([&alarmCount](const AlarmNotification&)
731 { ++alarmCount; });
732
733 EXPECT_CALL(*mockSocket, Send(_)).Times(2);
734
735 Feed(BuildRtaFrame(kRtaTypeData,
736 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
737 0xFFFF,
738 0xFFFE,
739 BuildAlarmBlock()));
740
741 Feed(BuildRtaFrame(kRtaTypeData,
742 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
743 0x0000,
744 0x0000,
745 BuildAlarmBlock()));
746
747 EXPECT_EQ(alarmCount, 2U);
748}
749
751TEST_F(AlarmListenerRtaTest, InvalidAlarmPayloadIsNotAcknowledged)
752{
753 EXPECT_CALL(*mockSocket, Send(_)).Times(0);
754
755 const Bytes invalidAlarm{0x00, 0x01, 0x00, 0x02, 0x00, 0x00};
756
757 Feed(BuildRtaFrame(kRtaTypeData,
758 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
759 0xFFFF,
760 0xFFFE,
761 invalidAlarm));
762}
763
769TEST_F(AlarmListenerRtaTest, WrongRtaVersionIsIgnored)
770{
771 EXPECT_CALL(*mockSocket, Send(_)).Times(0);
772 const Bytes alarm = BuildAlarmBlock(
773 false,
774 0x0401,
775 MakeChannelDiagnosisItem());
776
777 Bytes frame = BuildRtaFrame(
778 kRtaTypeData,
779 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
780 1,
781 0,
782 alarm,
783 kControllerReference,
784 kDeviceReference,
785 24,
786 false,
788
789 Feed(std::move(frame));
790}
791
797TEST_F(AlarmListenerRtaTest, DataWithoutTackIsIgnored)
798{
799 EXPECT_CALL(*mockSocket, Send(_)).Times(0);
800
801 Feed(BuildRtaFrame(kRtaTypeData, kAddFlagsWindow1, 0xFFFF, 0xFFFE, BuildAlarmBlock()));
802}
803
812TEST_F(AlarmListenerRtaIntegrationTest, DISABLED_DuplicateNotificationIsNotDispatchedTwice)
813{
814 std::uint32_t alarmCount = 0;
815
816 listener->AddAlarmNotificationCallback([&alarmCount](const AlarmNotification&)
817 { ++alarmCount; });
818
819 const Bytes frame = BuildRtaFrame(kRtaTypeData,
820 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
821 0xFFFF,
822 0xFFFE,
823 BuildAlarmBlock());
824
825 EXPECT_CALL(*mockSocket, Send(_)).Times(2);
826
827 Feed(frame);
828 Feed(frame);
829
830 EXPECT_EQ(alarmCount, 1U);
831}
832
840TEST_F(AlarmListenerRtaTest, DISABLED_OutOfSequenceNotificationIsRejected)
841{
842 std::uint32_t alarmCount = 0;
843
844 listener->AddAlarmNotificationCallback([&alarmCount](const AlarmNotification&)
845 { ++alarmCount; });
846
847 EXPECT_CALL(*mockSocket, Send(_)).Times(1);
848
849 Feed(BuildRtaFrame(kRtaTypeData,
850 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
851 5,
852 0xFFFE,
853 BuildAlarmBlock()));
854
855 EXPECT_EQ(alarmCount, 0U);
856}
857
866TEST_F(AlarmListenerRtaTest, FirstAlarmAckUsesInitialSequenceNumber)
867{
868 EXPECT_CALL(*mockSocket, Send(_)).Times(1).WillOnce([&](const Bytes& frame)
869 {
870 const SentRtaFrame sent = ParseSentFrame(frame);
871 // EXPECT_EQ(sent.sendSeq, 0xFFFF);
872 EXPECT_EQ(sent.sendSeq, 0x01);
873 });
874
875 Feed(BuildRtaFrame(kRtaTypeData,
876 static_cast<std::uint8_t>(kAddFlagsWindow1 | kAddFlagsTack),
877 0xFFFF,
878 0xFFFE,
879 BuildAlarmBlock()));
880}
881
884class RtaErrorTests : public ::testing::Test
885{
886 protected:
892 [[nodiscard]] static Bytes BuildRtaErrorPdu(std::array<std::uint8_t, 4> status, const Bytes& padding = {})
893 {
894 PNRTAHeader header;
895 header.alarmDstEndpoint = 0x8001U;
896 header.alarmSrcEndpoint = 0x0000U;
897
898 // Version 1 + ERR-RTA (0x04).
899 header.pduType =
900 static_cast<std::uint8_t>((PNRTAHeader::VERSION_1 << 4U) |
902
903 header.addFlags = 0x01U;
904 header.sendSeqNum = 0xFFFFU;
905 header.ackSeqNum = 0xFFFEU;
906 header.variablePartLenght = 4U;
907 header.payload.assign(status.begin(), status.end());
908 header.payload.insert(header.payload.end(), padding.begin(), padding.end());
909
910 return header.ToBytes();
911 }
912};
913
915TEST_F(RtaErrorTests, ParsesAumaReleaseIndReceivedError)
916{
917 const Bytes data = BuildRtaErrorPdu({0xCFU, 0x81U, 0xFDU, 0x0FU},
918 {0x00U, 0x00U, 0x00U, 0x00U});
919
920 const RtaError error = ParseRtaError(data);
921
922 EXPECT_EQ(error.header.alarmDstEndpoint, 0x8001U);
923 EXPECT_EQ(error.header.alarmSrcEndpoint, 0x0000U);
924 EXPECT_EQ(error.header.pduType, 0x14U);
925 EXPECT_EQ(error.header.addFlags, 0x01U);
926 EXPECT_EQ(error.header.sendSeqNum, 0xFFFFU);
927 EXPECT_EQ(error.header.ackSeqNum, 0xFFFEU);
928 EXPECT_EQ(error.header.variablePartLenght, 4U);
929
930 EXPECT_EQ(error.statusPnio, 0xCF81FD0FU);
931 EXPECT_EQ(error.errorCode, 0xCFU);
932 EXPECT_EQ(error.errorDecode, 0x81U);
933 EXPECT_EQ(error.errorCode1, 0xFDU);
934 EXPECT_EQ(error.errorCode2, 0x0FU);
935
936 EXPECT_TRUE(error.IsRtaProtocolError());
937 // EXPECT_TRUE(error.IsRtaAbort());
938 EXPECT_EQ(error.ReasonName(), "AR release.ind received");
939}
940
942TEST_F(RtaErrorTests, IgnoresEthernetPaddingAfterStatus)
943{
944 const Bytes data = BuildRtaErrorPdu(
945 {0xCFU, 0x81U, 0xFDU, 0x0FU},
946 Bytes(32U, 0x00U));
947
948 const RtaError error = ParseRtaError(data);
949
950 EXPECT_EQ(error.statusPnio, 0xCF81FD0FU);
951 EXPECT_EQ(error.errorCode2, 0x0FU);
952}
953
955TEST_F(RtaErrorTests, RejectsNonErrRtaPdu)
956{
957 PNRTAHeader header;
958 header.pduType = static_cast<std::uint8_t>((PNRTAHeader::VERSION_1 << 4U) |
960 header.variablePartLenght = 4U;
961 header.payload = {0xCFU, 0x81U, 0xFDU, 0x0FU};
962
963 EXPECT_THROW(ParseRtaError(header), std::invalid_argument);
964}
965
967TEST_F(RtaErrorTests, RejectsWrongVariablePartLength)
968{
969 PNRTAHeader header;
970 header.pduType = static_cast<std::uint8_t>((PNRTAHeader::VERSION_1 << 4U) |
972 header.variablePartLenght = 3U;
973 header.payload = {0xCFU, 0x81U, 0xFDU};
974
975 EXPECT_THROW(ParseRtaError(header), std::invalid_argument);
976}
977
979TEST_F(RtaErrorTests, RejectsTruncatedStatus)
980{
981 PNRTAHeader header;
982 header.pduType = static_cast<std::uint8_t>((PNRTAHeader::VERSION_1 << 4U) |
984 header.variablePartLenght = 4U;
985 header.payload = {0xCFU, 0x81U, 0xFDU};
986
987 EXPECT_THROW(ParseRtaError(header), std::invalid_argument);
988}
989
991TEST_F(RtaErrorTests, SequenceErrorIsNotAbort)
992{
993 const Bytes data = BuildRtaErrorPdu(
994 {0xCFU, 0x81U, 0xFDU, 0x01U});
995
996 const RtaError error = ParseRtaError(data);
997
998 EXPECT_TRUE(error.IsRtaProtocolError());
999 // EXPECT_FALSE(error.IsRtaAbort());
1000 EXPECT_EQ(error.ReasonName(), "Error within the coordination of sequence numbers");
1001}
1002
1004TEST_F(RtaErrorTests, NonRtaStatusIsNotProtocolError)
1005{
1006 const Bytes data = BuildRtaErrorPdu({0x81U, 0x81U, 0x05U, 0x00U});
1007
1008 const RtaError error = ParseRtaError(data);
1009
1010 EXPECT_FALSE(error.IsRtaProtocolError());
1011 // EXPECT_FALSE(error.IsRtaAbort());
1012 EXPECT_EQ(error.ReasonName(), "Reserved");
1013}
1014
1030class RtaErrorsTests : public ::testing::TestWithParam<RtaErrorReasonTestCase>
1031{
1032};
1033
1035TEST_P(RtaErrorsTests, EveryDefinedReasonIsDecoded)
1036{
1037 const auto testCase = GetParam();
1038
1039 PNRTAHeader header;
1040 header.pduType =
1041 static_cast<std::uint8_t>(
1042 (PNRTAHeader::VERSION_1 << 4U) |
1044 header.variablePartLenght = 4U;
1045 header.payload = {0xCFU, 0x81U, 0xFDU, testCase.code};
1046
1047 const RtaError error = ParseRtaError(header);
1048
1049 EXPECT_EQ(error.errorCode, 0xCFU);
1050 EXPECT_EQ(error.errorDecode, 0x81U);
1051 EXPECT_EQ(error.errorCode1, 0xFDU);
1052 EXPECT_EQ(error.errorCode2, testCase.code);
1053 EXPECT_EQ(error.ErrorClass(), testCase.errorClass);
1054 // EXPECT_STREQ(error.ReasonName().c_str(), testCase.description);
1055}
1057 AllDefinedReasons,
1059 ::testing::Values(
1062 RtaErrorReasonTestCase{0x02, RtaErrorReason::InstanceClosed, RtaErrorClass::Abort},
1063 RtaErrorReasonTestCase{0x03, RtaErrorReason::ArOutOfMemory, RtaErrorClass::Abort},
1066 RtaErrorReasonTestCase{0x06, RtaErrorReason::ArCmiTimeout, RtaErrorClass::Abort},
1067 RtaErrorReasonTestCase{0x07, RtaErrorReason::ArAlarmOpenFailed, RtaErrorClass::Abort},
1070 RtaErrorReasonTestCase{0x0A, RtaErrorReason::ArAlarmDataTooLong, RtaErrorClass::Abort},
1071 RtaErrorReasonTestCase{0x0B, RtaErrorReason::ArAlarmIndError, RtaErrorClass::Abort},
1073 RtaErrorReasonTestCase{0x0D, RtaErrorReason::ArAbortRequest, RtaErrorClass::Abort},
1074 RtaErrorReasonTestCase{0x0E, RtaErrorReason::ArRerunAbortsExisting, RtaErrorClass::Abort},
1075 RtaErrorReasonTestCase{0x0F, RtaErrorReason::ArReleaseIndReceived, RtaErrorClass::Abort},
1076 RtaErrorReasonTestCase{0x10, RtaErrorReason::ArDeviceDeactivated, RtaErrorClass::Abort},
1077 RtaErrorReasonTestCase{0x11, RtaErrorReason::ArRemoved, RtaErrorClass::Abort},
1078 RtaErrorReasonTestCase{0x12, RtaErrorReason::ArProtocolViolation, RtaErrorClass::Abort},
1079 RtaErrorReasonTestCase{0x13, RtaErrorReason::ArNameResolutionError, RtaErrorClass::Abort},
1080 RtaErrorReasonTestCase{0x14, RtaErrorReason::ArRpcBindError, RtaErrorClass::Abort},
1081 RtaErrorReasonTestCase{0x15, RtaErrorReason::ArRpcConnectError, RtaErrorClass::Abort},
1082 RtaErrorReasonTestCase{0x16, RtaErrorReason::ArRpcReadError, RtaErrorClass::Abort},
1083 RtaErrorReasonTestCase{0x17, RtaErrorReason::ArRpcWriteError, RtaErrorClass::Abort},
1084 RtaErrorReasonTestCase{0x18, RtaErrorReason::ArRpcControlError, RtaErrorClass::Abort},
1085 RtaErrorReasonTestCase{0x19, RtaErrorReason::ArForbiddenPullPlug, RtaErrorClass::Abort},
1086 RtaErrorReasonTestCase{0x1A, RtaErrorReason::ArApRemoved, RtaErrorClass::Abort},
1087 RtaErrorReasonTestCase{0x1B, RtaErrorReason::ArLinkDown, RtaErrorClass::Abort},
1089 RtaErrorReasonTestCase{0x1D, RtaErrorReason::NotSynchronized, RtaErrorClass::Abort},
1090 RtaErrorReasonTestCase{0x1E, RtaErrorReason::WrongTopology, RtaErrorClass::Abort},
1091 RtaErrorReasonTestCase{0x1F, RtaErrorReason::DcpStationNameChanged, RtaErrorClass::Abort},
1094 RtaErrorReasonTestCase{0x22, RtaErrorReason::NoIrdataRecordYet, RtaErrorClass::Abort},
1095 RtaErrorReasonTestCase{0x23, RtaErrorReason::Pdev, RtaErrorClass::Abort},
1097 RtaErrorReasonTestCase{0x25, RtaErrorReason::IpSuiteChanged, RtaErrorClass::Abort},
1098 RtaErrorReasonTestCase{0x26, RtaErrorReason::IocarSrRdhtExpired, RtaErrorClass::Extended},
1102 RtaErrorReasonTestCase{0x2A, RtaErrorReason::IocarSrMtotExpired, RtaErrorClass::Extended},
1106 RtaErrorReasonTestCase{0xFF, RtaErrorReason::UserAbort, RtaErrorClass::Abort}),
1107
1108 [](const ::testing::TestParamInfo<RtaErrorReasonTestCase>& info)
1109{
1110 return std::format("{:#04x}", info.param.code);
1111});
1112
1114TEST(RtaErrorsTests, UndefinedReasonIsUnknown)
1115{
1116 constexpr std::uint8_t kUnknownErrorCode2 = 0x30;
1117
1118 EXPECT_EQ(RtaError::ClassifyRtaErrorReason(kUnknownErrorCode2), RtaErrorReason::Reserved);
1119
1120 EXPECT_EQ(RtaError::ClassifyRtaError(kUnknownErrorCode2), RtaErrorClass::Reserved);
1121}
1122} // namespace profinet
Private-access adapter used by AlarmListener tests.
Declares the PROFINET RTA alarm listener and AlarmAck transmission support.
Declares PROFINET alarm, diagnosis, maintenance, and USI payload types and parsers.
Test fixture for AlarmListener callback behavior.
std::unique_ptr< AlarmListener > listener
Listener under test.
std::unique_ptr< testing::StrictMock< MockRawEthernetSocket > > socket
Mock Ethernet transport pointer owned by the listener.
StrictMock< MockRawEthernetSocket > * mockSocket
Mock Ethernet transport owned by the listener.
void Feed(Bytes frame)
Inject one Ethernet frame into the listener.
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
void SetUp() override
Construct the listener with a mocked Layer 2 transport.
Test fixture for multi-frame RTA alarm exchanges.
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
std::unique_ptr< testing::StrictMock< MockRawEthernetSocket > > socket
Mock Ethernet transport pointer owned by the listener.
StrictMock< MockRawEthernetSocket > * mockSocket
Mock Ethernet transport owned by the listener.
void Feed(Bytes frame)
Inject one Ethernet frame into the listener.
std::unique_ptr< AlarmListener > listener
Listener under test.
void SetUp() override
Construct the listener with a mocked Layer 2 transport.
Test fixture for Layer 2 RTA alarm handling.
StrictMock< MockRawEthernetSocket > * mockSocket
Mock Ethernet transport owned by the listener.
void SetUp() override
Construct the test listener with a mocked raw Ethernet socket.
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
void Feed(Bytes frame)
Inject one Ethernet frame into the listener.
static SentRtaFrame ParseSentFrame(std::span< const std::uint8_t > frame)
Return all transmitted RTA frames.
std::unique_ptr< testing::StrictMock< MockRawEthernetSocket > > socket
Mock Ethernet transport pointer owned by the listener.
std::unique_ptr< AlarmListener > listener
Listener under test.
static void SetSock(AlarmListener &listener, std::unique_ptr< IRawEthernetSocket > sock)
Set the Layer 2 Ethernet socket used by the listener.
static void HandleLayer2Frame(AlarmListener &listener)
Receive and process one Layer 2 RTA frame.
Tests for PROFINET RTA ERR-PDU decoding.
static Bytes BuildRtaErrorPdu(std::array< std::uint8_t, 4 > status, const Bytes &padding={})
Build the RTA header used by the AUMA ERR-RTA example.
Lightweight cursor for parsing a big-endian byte buffer without copies.
Definition wire.h:192
std::array< std::uint8_t, N > Fixed()
Read and consume a fixed-size byte array.
Definition wire.h:316
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
Definition wire.h:258
GoogleMock implementation of IRawEthernetSocket.
constexpr MacAddress kControllerMac
void PutU16(std::vector< std::uint8_t > &out, std::uint16_t value)
Append a 16-bit value to a buffer in big-endian order.
Definition wire.h:97
void PutU32(std::vector< std::uint8_t > &out, std::uint32_t value)
Append a 32-bit value to a buffer in big-endian order.
Definition wire.h:105
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
TEST(AlarmEndpointTests, DefaultTransportIsLayer2)
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
RtaErrorReason
Exact current PROFINET RTA ErrorCode2 values.
Definition protocol.h:2507
RtaError ParseRtaError(const PNRTAHeader &header)
Parse an RTA ERR-PDU from an already parsed RTA header.
Definition protocol.h:2680
constexpr int macAddressLength
Constant lenght of a mac address.
Definition util.h:37
TEST_P(RtaErrorsTests, EveryDefinedReasonIsDecoded)
RtaErrorClass
Classification of an RTA protocol error reason.
Definition protocol.h:2485
@ SequenceNumber
Error coordinating RTA sequence numbers.
@ Abort
RTA error that aborts the application relation.
@ Reserved
No error / reserved reason.
@ Obsoleted
Obsoleted reason code.
@ Extended
Additional AR/IOCARSR/PDEV RTA reason defined by PROFINET.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
INSTANTIATE_TEST_SUITE_P(AllDefinedReasons, RtaErrorsTests, ::testing::Values(RtaErrorReasonTestCase{0x00, RtaErrorReason::Reserved, RtaErrorClass::Reserved}, RtaErrorReasonTestCase{0x01, RtaErrorReason::SequenceNumber, RtaErrorClass::SequenceNumber}, RtaErrorReasonTestCase{0x02, RtaErrorReason::InstanceClosed, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x03, RtaErrorReason::ArOutOfMemory, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x04, RtaErrorReason::ArAddProviderConsumerFailed, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x05, RtaErrorReason::ArConsumerDhtWdtExpired, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x06, RtaErrorReason::ArCmiTimeout, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x07, RtaErrorReason::ArAlarmOpenFailed, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x08, RtaErrorReason::ArAlarmSendConfirmationNegative, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x09, RtaErrorReason::ArAlarmAckSendConfirmationNegative, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x0A, RtaErrorReason::ArAlarmDataTooLong, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x0B, RtaErrorReason::ArAlarmIndError, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x0C, RtaErrorReason::ArRpcClientCallConfirmationNegative, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x0D, RtaErrorReason::ArAbortRequest, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x0E, RtaErrorReason::ArRerunAbortsExisting, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x0F, RtaErrorReason::ArReleaseIndReceived, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x10, RtaErrorReason::ArDeviceDeactivated, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x11, RtaErrorReason::ArRemoved, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x12, RtaErrorReason::ArProtocolViolation, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x13, RtaErrorReason::ArNameResolutionError, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x14, RtaErrorReason::ArRpcBindError, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x15, RtaErrorReason::ArRpcConnectError, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x16, RtaErrorReason::ArRpcReadError, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x17, RtaErrorReason::ArRpcWriteError, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x18, RtaErrorReason::ArRpcControlError, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x19, RtaErrorReason::ArForbiddenPullPlug, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x1A, RtaErrorReason::ArApRemoved, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x1B, RtaErrorReason::ArLinkDown, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x1C, RtaErrorReason::ArMulticastMacRegistrationFailed, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x1D, RtaErrorReason::NotSynchronized, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x1E, RtaErrorReason::WrongTopology, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x1F, RtaErrorReason::DcpStationNameChanged, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x20, RtaErrorReason::DcpResetToFactorySettings, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x21, RtaErrorReason::CompanionAr8ippSubmodule, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x22, RtaErrorReason::NoIrdataRecordYet, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x23, RtaErrorReason::Pdev, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x24, RtaErrorReason::PdevNoRequiredSpeedDuplexity, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x25, RtaErrorReason::IpSuiteChanged, RtaErrorClass::Abort}, RtaErrorReasonTestCase{0x26, RtaErrorReason::IocarSrRdhtExpired, RtaErrorClass::Extended}, RtaErrorReasonTestCase{0x27, RtaErrorReason::IocarSrPdevParameterizationImpossible, RtaErrorClass::Extended}, RtaErrorReasonTestCase{0x28, RtaErrorReason::RemoteApplicationReadyTimeout, RtaErrorClass::Extended}, RtaErrorReasonTestCase{0x29, RtaErrorReason::IocarSrRedundantInterfaceLost, RtaErrorClass::Extended}, RtaErrorReasonTestCase{0x2A, RtaErrorReason::IocarSrMtotExpired, RtaErrorClass::Extended}, RtaErrorReasonTestCase{0x2B, RtaErrorReason::IocarSrArProtocolViolation, RtaErrorClass::Extended}, RtaErrorReasonTestCase{0x2C, RtaErrorReason::PdevPlugPortWithoutCombinedObjectContainer, RtaErrorClass::Extended}, RtaErrorReasonTestCase{0x2D, RtaErrorReason::ObsoletedNmeNoConfiguration, RtaErrorClass::Obsoleted}, RtaErrorReasonTestCase{0xFF, RtaErrorReason::UserAbort, RtaErrorClass::Abort}), [](const ::testing::TestParamInfo< RtaErrorReasonTestCase > &info) { return std::format("{:#04x}", info.param.code);})
constexpr std::uint16_t FRAME_ID_ALARM_HIGH
Frame ID used for high-priority alarms.
constexpr std::uint16_t FRAME_ID_ALARM_LOW
Frame ID used for low-priority alarms.
PROFINET protocol wire-format structures and serialization helpers.
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
RTA-PDU Header - Real-Time Acyclic PDU for Layer 2 alarm transport.
Definition protocol.h:2390
std::uint16_t sendSeqNum
Sender's sequence number for this PDU.
Definition protocol.h:2431
static constexpr std::uint8_t RTA_TYPE_ERR
PDU type: ERR.
Definition protocol.h:2402
std::uint16_t alarmSrcEndpoint
Source alarm endpoint (AlarmCR reference on the sender).
Definition protocol.h:2422
Bytes payload
RTA variable part (e.g. an AlarmNotification-PDU or AlarmAck-PDU).
Definition protocol.h:2440
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
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 constexpr std::uint8_t RTA_TYPE_DATA
PDU type: DATA.
Definition protocol.h:2393
static constexpr std::uint8_t VERSION_1
RTA protocol version 1.
Definition protocol.h:2405
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
One complete RTA ErrorCode2 test case.
RtaErrorClass errorClass
Expected semantic class.
RtaErrorReason errorReason
Expected error reason.
Decoded PROFINET RTA ERR-PDU.
Definition protocol.h:2571
static RtaErrorReason ClassifyRtaErrorReason(std::uint8_t errorCode2)
Return the decoded RTA reason.
Definition alarms.cpp:347
static RtaErrorClass ClassifyRtaError(std::uint8_t errorCode2)
Return the decoded RTA reason.
Definition alarms.cpp:496
std::uint8_t pduType
RTA PDU type including version nibble.
std::uint16_t srcRef
RTA source alarm endpoint.
std::uint8_t addFlags
RTA AdditionalFlags field.
std::uint16_t dstRef
RTA destination alarm endpoint.
std::uint16_t sendSeq
RTA SendSeqNum.
std::uint16_t frameId
Ethernet FrameID.
Bytes varPart
RTA variable part.
std::uint16_t ackSeq
RTA AckSeqNum.
Bytes frame