PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_alarmsMore.cpp
Go to the documentation of this file.
1
21
22#include <gtest/gtest.h>
23
24#include <cstdint>
25#include <string>
26#include <utility>
27#include <vector>
28
29#include "profinet/alarms.h"
30#include "profinet/protocol.h"
31#include "profinet/wire.h"
32
33namespace profinet
34{
35namespace
36{
37
46[[nodiscard]] Bytes BuildBlockHeader(
47 std::uint16_t blockType,
48 std::uint16_t blockLength,
49 std::uint8_t versionHigh = 1U,
50 std::uint8_t versionLow = 2U)
51{
52 Bytes data;
53 wire::PutU16(data, blockType);
54 wire::PutU16(data, blockLength);
55 data.push_back(versionHigh);
56 data.push_back(versionLow);
57 return data;
58}
59
67[[nodiscard]] Bytes BuildMaintenanceItem(
68 std::uint16_t blockType,
69 std::uint16_t blockLength,
70 std::uint32_t maintenanceStatus)
71{
72 Bytes item;
73 wire::PutU16(item, static_cast<std::uint16_t>(Usi::Maintenance));
74
75 const Bytes header = BuildBlockHeader(blockType, blockLength);
76 item.insert(item.end(), header.begin(), header.end());
77
78 item.push_back(0x00);
79 item.push_back(0x00);
80
81 wire::PutU32(item, maintenanceStatus);
82
83 return item;
84}
85
95[[nodiscard]] Bytes BuildUploadRetrievalItem(
96 Usi usi,
97 std::uint16_t blockType,
98 std::uint16_t blockLength,
99 std::uint32_t recordIndex,
100 std::uint32_t recordLength)
101{
102 Bytes item;
103 wire::PutU16(item, static_cast<std::uint16_t>(usi));
104
105 const Bytes header = BuildBlockHeader(blockType, blockLength);
106 item.insert(item.end(), header.begin(), header.end());
107
108 item.push_back(0x00);
109 item.push_back(0x00);
110
111 wire::PutU32(item, recordIndex);
112 wire::PutU32(item, recordLength);
113
114 return item;
115}
116
126[[nodiscard]] Bytes BuildPeAlarmItem(
127 std::uint16_t blockType,
128 std::uint16_t blockLength,
129 std::uint8_t versionHigh,
130 std::uint8_t versionLow,
131 std::uint8_t operationalMode)
132{
133 Bytes item;
134 wire::PutU16(item, static_cast<std::uint16_t>(Usi::PeAlarm));
135
136 const Bytes header = BuildBlockHeader(blockType, blockLength, versionHigh, versionLow);
137 item.insert(item.end(), header.begin(), header.end());
138 item.push_back(operationalMode);
139
140 return item;
141}
142
149[[nodiscard]] Bytes BuildRsAlarmItem(Usi usi, std::uint16_t alarmInfo)
150{
151 Bytes item;
152 wire::PutU16(item, static_cast<std::uint16_t>(usi));
153 wire::PutU16(item, alarmInfo);
154 return item;
155}
156
166[[nodiscard]] Bytes BuildPralAlarmItem(
167 std::uint16_t channelNumber,
168 std::uint16_t channelProperties,
169 std::uint16_t reason,
170 std::uint16_t extendedReason,
171 const Bytes& additionalValue = {})
172{
173 Bytes item;
174 wire::PutU16(item, static_cast<std::uint16_t>(Usi::PralAlarm));
175 wire::PutU16(item, channelNumber);
176 wire::PutU16(item, channelProperties);
177 wire::PutU16(item, reason);
178 wire::PutU16(item, extendedReason);
179 item.insert(item.end(), additionalValue.begin(), additionalValue.end());
180 return item;
181}
182
191[[nodiscard]] Bytes BuildAlarmNotification(
192 std::uint16_t blockType,
193 std::uint16_t specifier,
194 const Bytes& items = {},
195 std::uint16_t blockLengthOverride = 0)
196{
197 Bytes body;
198
199 wire::PutU16(body, 0x0007); // AlarmType.
200 wire::PutU32(body, 0x12345678); // API.
201 wire::PutU16(body, 0x0021); // Slot.
202 wire::PutU16(body, 0x1002); // Subslot.
203 wire::PutU32(body, 0x11223344); // ModuleIdent.
204 wire::PutU32(body, 0x55667788); // SubmoduleIdent.
205 wire::PutU16(body, specifier); // AlarmSpecifier.
206
207 EXPECT_EQ(body.size(), 20U);
208
209 if (body.size() < 20U)
210 {
211 return {};
212 }
213
214 Bytes bodyWithItems{0x02U, 0x01U};
215 bodyWithItems.insert(bodyWithItems.end(), body.begin(), body.end());
216 bodyWithItems.insert(bodyWithItems.end(), items.begin(), items.end());
217
218 const auto actualBlockLength = static_cast<std::uint16_t>(bodyWithItems.size());
219 const auto blockLength =
220 blockLengthOverride == 0U ? actualBlockLength : blockLengthOverride;
221
222 Bytes result;
223 wire::PutU16(result, blockType);
224 wire::PutU16(result, blockLength);
225 result.insert(result.end(), bodyWithItems.begin(), bodyWithItems.end());
226
227 return result;
228}
229
237[[nodiscard]] Bytes BuildChannelDiagnosisItem(
238 std::uint16_t channelNumber,
239 std::uint16_t channelProperties,
240 std::uint16_t channelErrorType)
241{
242 Bytes item;
243 wire::PutU16(item, static_cast<std::uint16_t>(Usi::ChannelDiagnosis));
244 wire::PutU16(item, channelNumber);
245 wire::PutU16(item, channelProperties);
246 wire::PutU16(item, channelErrorType);
247 return item;
248}
249
260[[nodiscard]] Bytes BuildQualifiedDiagnosisItem(
261 std::uint16_t channelNumber,
262 std::uint16_t channelProperties,
263 std::uint16_t channelErrorType,
264 std::uint16_t extendedErrorType,
265 std::uint32_t extendedAdditionalValue,
266 std::uint32_t qualifier)
267{
268 Bytes item;
269 wire::PutU16(item, static_cast<std::uint16_t>(Usi::QualifiedChannelDiagnosis));
270 wire::PutU16(item, channelNumber);
271 wire::PutU16(item, channelProperties);
272 wire::PutU16(item, channelErrorType);
273 wire::PutU16(item, extendedErrorType);
274 wire::PutU32(item, extendedAdditionalValue);
275 wire::PutU32(item, qualifier);
276 return item;
277}
278
281class DiagnosisItemTests : public ::testing::Test
282{
283};
284
287class MaintenanceItemTests : public ::testing::Test
288{
289};
290
293class UploadRetrievalItemTests : public ::testing::Test
294{
295};
296
299class RsAlarmItemTests : public ::testing::Test
300{
301};
302
305class PralAlarmItemTests : public ::testing::Test
306{
307};
308
311class PeAlarmItemTests : public ::testing::Test
312{
313};
314
317class AlarmItemVariantTests : public ::testing::Test
318{
319};
320
323class AlarmNotificationTests : public ::testing::Test
324{
325};
326
328TEST_F(DiagnosisItemTests, ChannelNumberWithoutAccumulativeBit)
329{
330 DiagnosisItem item;
331 item.channelNumber = 0x1234U;
332
333 EXPECT_EQ(item.ChannelNumberValue(), 0x1234U);
334 EXPECT_FALSE(item.IsAccumulative());
335}
336
338TEST_F(DiagnosisItemTests, ChannelTypeUsesLowByte)
339{
340 DiagnosisItem item;
341 item.channelProperties = 0xABCDU;
342
343 EXPECT_EQ(item.ChannelTypeValue(), static_cast<std::uint8_t>(0xCDU));
344}
345
347TEST_F(DiagnosisItemTests, QualifiedDiagnosisIsExtendedAndQualified)
348{
349 DiagnosisItem item;
350 item.userStructureId = Usi::QualifiedChannelDiagnosis;
351
352 EXPECT_TRUE(item.IsExtended());
353 EXPECT_TRUE(item.IsQualified());
354}
355
357TEST_F(DiagnosisItemTests, ExtendedDiagnosisIsExtendedButNotQualified)
358{
359 DiagnosisItem item;
360 item.userStructureId = Usi::ExtendedChannelDiagnosis;
361
362 EXPECT_TRUE(item.IsExtended());
363 EXPECT_FALSE(item.IsQualified());
364}
365
367TEST_F(DiagnosisItemTests, StandardDiagnosisIsNeitherExtendedNorQualified)
368{
369 DiagnosisItem item;
370 item.userStructureId = Usi::ChannelDiagnosis;
371
372 EXPECT_FALSE(item.IsExtended());
373 EXPECT_FALSE(item.IsQualified());
374}
375
377TEST_F(DiagnosisItemTests, ParseQualifiedChannelDiagnosis)
378{
379 const Bytes data = BuildQualifiedDiagnosisItem(
380 0x8005U,
381 0xA5C3U,
382 0x1122U,
383 0x3344U,
384 0x55667788U,
385 0x99AABBCCU);
386
387 const auto [variant, offset] = ParseAlarmItem(data);
388
389 EXPECT_EQ(offset, data.size());
390
391 const auto* item = std::get_if<DiagnosisItem>(&variant);
392 ASSERT_NE(item, nullptr);
393
394 EXPECT_EQ(item->userStructureId, Usi::QualifiedChannelDiagnosis);
395 EXPECT_EQ(item->channelNumber, 0x8005U);
396 EXPECT_EQ(item->ChannelNumberValue(), 5U);
397 EXPECT_TRUE(item->IsAccumulative());
398 EXPECT_EQ(item->channelProperties, 0xA5C3U);
399 EXPECT_EQ(item->ChannelTypeValue(), 0xC3U);
400 EXPECT_EQ(item->channelErrorType, 0x1122U);
401 EXPECT_EQ(item->extChannelErrorType, 0x3344U);
402 EXPECT_EQ(item->extChannelAddValue, 0x55667788U);
403 EXPECT_EQ(item->qualifiedChannelQualifier, 0x99AABBCCU);
404 EXPECT_TRUE(item->IsExtended());
405 EXPECT_TRUE(item->IsQualified());
406}
407
409TEST_F(DiagnosisItemTests, RejectsTruncatedExtendedDiagnosis)
410{
411 Bytes data;
412 wire::PutU16(data, static_cast<std::uint16_t>(Usi::ExtendedChannelDiagnosis));
413 wire::PutU16(data, 1U);
414 wire::PutU16(data, 2U);
415 wire::PutU16(data, 3U);
416 wire::PutU16(data, 4U);
417 wire::PutU8(data, 0x12U);
418
419 EXPECT_THROW(ParseAlarmItem(data), std::invalid_argument);
420}
421
423TEST_F(DiagnosisItemTests, RejectsTruncatedQualifiedQualifier)
424{
425 Bytes data = BuildQualifiedDiagnosisItem(
426 1U,
427 2U,
428 3U,
429 4U,
430 0x11223344U,
431 0xAABBCCDDU);
432
433 ASSERT_GT(data.size(), 0U);
434 data.pop_back();
435
436 EXPECT_THROW(ParseAlarmItem(data), std::invalid_argument);
437}
438
440TEST_F(MaintenanceItemTests, MaintenanceStatusFlagsAreIndependent)
441{
442 MaintenanceItem item;
443
444 item.maintenanceStatus = 0U;
445 EXPECT_FALSE(item.MaintenanceRequired());
446 EXPECT_FALSE(item.MaintenanceDemanded());
447
448 item.maintenanceStatus = 0x01U;
449 EXPECT_TRUE(item.MaintenanceRequired());
450 EXPECT_FALSE(item.MaintenanceDemanded());
451
452 item.maintenanceStatus = 0x02U;
453 EXPECT_FALSE(item.MaintenanceRequired());
454 EXPECT_TRUE(item.MaintenanceDemanded());
455
456 item.maintenanceStatus = 0x03U;
457 EXPECT_TRUE(item.MaintenanceRequired());
458 EXPECT_TRUE(item.MaintenanceDemanded());
459}
460
462TEST_F(MaintenanceItemTests, ParseMaintenanceItem)
463{
464 constexpr std::uint16_t blockType = 0x8101U;
465 constexpr std::uint16_t blockLength = 0x0010U;
466 constexpr std::uint32_t maintenanceStatus = 0xA5A5A5A5U;
467
468 const Bytes data = BuildMaintenanceItem(blockType, blockLength, maintenanceStatus);
469
470 const auto [variant, offset] = ParseAlarmItem(data);
471
472 EXPECT_EQ(offset, data.size());
473
474 const auto* item = std::get_if<MaintenanceItem>(&variant);
475 ASSERT_NE(item, nullptr);
476
477 EXPECT_EQ(item->userStructureId, Usi::Maintenance);
478 EXPECT_EQ(static_cast<std::uint16_t>(item->blockType), blockType);
479 EXPECT_EQ(item->blockLength, blockLength);
480 EXPECT_EQ(item->blockVersion, 0x0102U);
481 EXPECT_EQ(item->maintenanceStatus, maintenanceStatus);
482 EXPECT_TRUE(item->MaintenanceRequired());
483 EXPECT_FALSE(item->MaintenanceDemanded());
484 EXPECT_EQ(item->UsiName(), GetUsiName(Usi::Maintenance));
485}
486
488TEST_F(MaintenanceItemTests, RejectsTruncatedMaintenanceItem)
489{
490 Bytes data;
491 wire::PutU16(data, static_cast<std::uint16_t>(Usi::Maintenance));
492
493 const Bytes header = BuildBlockHeader(0x8101U, 0x0010U);
494 data.insert(data.end(), header.begin(), header.end());
495
496 EXPECT_THROW(ParseAlarmItem(data), std::invalid_argument);
497}
498
500TEST_F(UploadRetrievalItemTests, ParseUploadItem)
501{
502 const Bytes data = BuildUploadRetrievalItem(
504 0x8201U,
505 0x0020U,
506 0x01020304U,
507 0xA1B2C3D4U);
508
509 const auto [variant, offset] = ParseAlarmItem(data);
510
511 EXPECT_EQ(offset, data.size());
512
513 const auto* item = std::get_if<UploadRetrievalItem>(&variant);
514 ASSERT_NE(item, nullptr);
515
516 EXPECT_EQ(item->userStructureId, Usi::Upload);
517 EXPECT_EQ(static_cast<std::uint16_t>(item->blockType), 0x8201U);
518 EXPECT_EQ(item->blockLength, 0x0020U);
519 EXPECT_EQ(item->blockVersion, 0x0102U);
520 EXPECT_EQ(item->urRecordIndex, 0x01020304U);
521 EXPECT_EQ(item->urRecordLength, 0xA1B2C3D4U);
522 EXPECT_TRUE(item->IsUpload());
523 EXPECT_FALSE(item->IsRetrieval());
524}
525
527TEST_F(UploadRetrievalItemTests, ParseIParameterItemAsRetrieval)
528{
529 const Bytes data = BuildUploadRetrievalItem(
531 0x8202U,
532 0x0030U,
533 0x11223344U,
534 0x55667788U);
535
536 const auto [variant, offset] = ParseAlarmItem(data);
537
538 EXPECT_EQ(offset, data.size());
539
540 const auto* item = std::get_if<UploadRetrievalItem>(&variant);
541 ASSERT_NE(item, nullptr);
542
543 EXPECT_EQ(item->userStructureId, Usi::IParameter);
544 EXPECT_EQ(item->urRecordIndex, 0x11223344U);
545 EXPECT_EQ(item->urRecordLength, 0x55667788U);
546 EXPECT_FALSE(item->IsUpload());
547 EXPECT_TRUE(item->IsRetrieval());
548}
549
551TEST_F(UploadRetrievalItemTests, RejectsTruncatedUploadRetrievalItem)
552{
553 Bytes data;
554 wire::PutU16(data, static_cast<std::uint16_t>(Usi::Upload));
555
556 const Bytes header = BuildBlockHeader(0x8201U, 0x0020U);
557 data.insert(data.end(), header.begin(), header.end());
558
559 EXPECT_THROW(ParseAlarmItem(data), std::invalid_argument);
560}
561
563TEST_F(RsAlarmItemTests, DISABLED_ParsesAllRsAlarmVariants)
564{
565 const std::vector<Usi> usis{
569 };
570
571 for (const Usi usi : usis)
572 {
573 SCOPED_TRACE(static_cast<std::uint16_t>(usi));
574
575 const Bytes data = BuildRsAlarmItem(usi, 0xF8A5U);
576
577 const auto [variant, offset] = ParseAlarmItem(data);
578
579 EXPECT_EQ(offset, data.size());
580
581 const auto* item = std::get_if<RSAlarmItem>(&variant);
582 ASSERT_NE(item, nullptr);
583
584 EXPECT_EQ(item->userStructureId, usi);
585 EXPECT_EQ(item->rsAlarmInfo, 0xF8A5U);
586 EXPECT_EQ(item->RsSpecifier(), 0x08A5U);
587 EXPECT_EQ(item->RsSequenceNumber(), 0x08A5U);
588 EXPECT_EQ(item->UsiName(), GetUsiName(usi));
589 }
590}
591
593TEST_F(RsAlarmItemTests, RejectsTruncatedRsAlarm)
594{
595 Bytes data;
596 wire::PutU16(data, static_cast<std::uint16_t>(Usi::RsAlarmLow));
597 data.push_back(0x12U);
598
599 EXPECT_THROW(ParseAlarmItem(data), std::invalid_argument);
600}
601
603TEST_F(PralAlarmItemTests, ParsePralAlarm)
604{
605 const Bytes additional{0xDEU, 0xADU, 0xBEU, 0xEFU};
606
607 const Bytes data = BuildPralAlarmItem(
608 0x8005U,
609 0x1234U,
610 0x5678U,
611 0x9ABCU,
612 additional);
613
614 const auto [variant, offset] = ParseAlarmItem(data);
615
616 EXPECT_EQ(offset, data.size());
617
618 const auto* item = std::get_if<PRALAlarmItem>(&variant);
619 ASSERT_NE(item, nullptr);
620
621 EXPECT_EQ(item->userStructureId, Usi::PralAlarm);
622 EXPECT_EQ(item->channelNumber, 0x8005U);
623 EXPECT_EQ(item->propAlChannelProperties, 0x1234U);
624 EXPECT_EQ(item->prAlReason, 0x5678U);
625 EXPECT_EQ(item->prAlExtReason, 0x9ABCU);
626 EXPECT_EQ(item->prAlReasonAddValue, additional);
627 EXPECT_EQ(item->UsiName(), GetUsiName(Usi::PralAlarm));
628}
629
631TEST_F(PralAlarmItemTests, ParsePralAlarmWithoutAdditionalData)
632{
633 const Bytes data = BuildPralAlarmItem(
634 1U,
635 2U,
636 3U,
637 4U);
638
639 const auto [variant, offset] = ParseAlarmItem(data);
640
641 EXPECT_EQ(offset, data.size());
642
643 const auto* item = std::get_if<PRALAlarmItem>(&variant);
644 ASSERT_NE(item, nullptr);
645
646 EXPECT_TRUE(item->prAlReasonAddValue.empty());
647}
648
650TEST_F(PralAlarmItemTests, RejectsTruncatedPralAlarm)
651{
652 Bytes data;
653 wire::PutU16(data, static_cast<std::uint16_t>(Usi::PralAlarm));
654
655 wire::PutU16(data, 1U);
656 wire::PutU16(data, 2U);
657 wire::PutU16(data, 3U);
658
659 EXPECT_THROW(ParseAlarmItem(data), std::invalid_argument);
660}
661
663TEST_F(PeAlarmItemTests, ParsePeAlarmFields)
664{
665 const Bytes data = BuildPeAlarmItem(
666 0x8311U,
667 0x0042U,
668 0x03U,
669 0x04U,
670 0x7FU);
671
672 const auto [variant, offset] = ParseAlarmItem(data);
673
674 EXPECT_EQ(offset, data.size());
675
676 const auto* item = std::get_if<PEAlarmItem>(&variant);
677 ASSERT_NE(item, nullptr);
678
679 EXPECT_EQ(item->userStructureId, Usi::PeAlarm);
680 EXPECT_EQ(static_cast<std::uint16_t>(item->blockType), 0x8311U);
681 EXPECT_EQ(item->blockLength, 0x0042U);
682 EXPECT_EQ(item->blockVersion, 0x0304U);
683 EXPECT_EQ(item->peOperationalMode, 0x7FU);
684 EXPECT_EQ(item->UsiName(), GetUsiName(Usi::PeAlarm));
685 EXPECT_EQ(item->ModeName(), GetPeModeName(0x7FU));
686}
687
689TEST_F(PeAlarmItemTests, RejectsTruncatedPeAlarm)
690{
691 Bytes data;
692 wire::PutU16(data, static_cast<std::uint16_t>(Usi::PeAlarm));
693
694 const Bytes header = BuildBlockHeader(0x8311U, 0x0003U);
695 data.insert(data.end(), header.begin(), header.end());
696
697 EXPECT_THROW(ParseAlarmItem(data), std::invalid_argument);
698}
699
701TEST_F(AlarmItemVariantTests, VariantHelpersCoverAllAlarmItemTypes)
702{
703 AlarmItem generic;
704 // generic.userStructureId;
705
706 DiagnosisItem diagnosis;
707 diagnosis.userStructureId = Usi::ChannelDiagnosis;
708
709 MaintenanceItem maintenance;
710 maintenance.userStructureId = Usi::Maintenance;
711
712 UploadRetrievalItem upload;
713 upload.userStructureId = Usi::Upload;
714
715 PEAlarmItem pe;
716 pe.userStructureId = Usi::PeAlarm;
717
718 RSAlarmItem rs;
719 rs.userStructureId = Usi::RsAlarmHigh;
720
721 PRALAlarmItem pral;
722 pral.userStructureId = Usi::PralAlarm;
723
724 const std::vector<AlarmItemVariant> items{
725 generic,
726 diagnosis,
727 maintenance,
728 upload,
729 pe,
730 rs,
731 pral,
732 };
733
734 for (const AlarmItemVariant& item : items)
735 {
736 SCOPED_TRACE(GetUsiName(UsiOf(item)));
737
738 EXPECT_EQ(UsiNameOf(item), GetUsiName(UsiOf(item)));
739 }
740}
741
743TEST_F(AlarmItemVariantTests, IParameterItemUsiName)
744{
745 IParameterItem item;
746
747 EXPECT_EQ(item.userStructureId, Usi::IParameter);
748 EXPECT_EQ(item.UsiName(), GetUsiName(Usi::IParameter));
749}
750
752TEST_F(AlarmItemVariantTests, ConcreteItemsExposeUsiNames)
753{
754 AlarmItem generic;
755 generic.userStructureId = Usi::ChannelDiagnosis;
756
757 DiagnosisItem diagnosis;
758 diagnosis.userStructureId = Usi::ExtendedChannelDiagnosis;
759
760 MaintenanceItem maintenance;
761 UploadRetrievalItem upload;
762 PEAlarmItem pe;
763 RSAlarmItem rs;
764 rs.userStructureId = Usi::RsAlarmLow;
765 PRALAlarmItem pral;
766
767 EXPECT_EQ(generic.UsiName(), GetUsiName(Usi::ChannelDiagnosis));
768 EXPECT_EQ(diagnosis.UsiName(), GetUsiName(Usi::ExtendedChannelDiagnosis));
769 EXPECT_EQ(maintenance.UsiName(), GetUsiName(Usi::Maintenance));
770 EXPECT_EQ(upload.UsiName(), GetUsiName(Usi::Upload));
771 EXPECT_EQ(pe.UsiName(), GetUsiName(Usi::PeAlarm));
772 EXPECT_EQ(rs.UsiName(), GetUsiName(Usi::RsAlarmLow));
773 EXPECT_EQ(pral.UsiName(), GetUsiName(Usi::PralAlarm));
774}
775
777TEST_F(AlarmItemVariantTests, ParseAlarmItemHonorsOffset)
778{
779 Bytes data{0xAAU, 0xBBU, 0xCCU};
780
781 const Bytes item = BuildChannelDiagnosisItem(
782 0x8001U,
783 0x1234U,
784 0x5678U);
785
786 data.insert(data.end(), item.begin(), item.end());
787
788 const auto [variant, offset] = ParseAlarmItem(data, 3U);
789
790 EXPECT_EQ(offset, data.size());
791
792 const auto* diagnosis = std::get_if<DiagnosisItem>(&variant);
793 ASSERT_NE(diagnosis, nullptr);
794
795 EXPECT_EQ(diagnosis->channelNumber, 0x8001U);
796 EXPECT_EQ(diagnosis->channelProperties, 0x1234U);
797 EXPECT_EQ(diagnosis->channelErrorType, 0x5678U);
798}
799
801TEST_F(AlarmItemVariantTests, ParseGenericAlarmWithOffset)
802{
803 Bytes data{0x00U, 0x01U, 0x02U};
804
805 wire::PutU16(data, 0x1234U);
806 data.push_back(0xDEU);
807 data.push_back(0xADU);
808
809 const auto [variant, offset] = ParseAlarmItem(data, 3U);
810
811 EXPECT_EQ(offset, data.size());
812
813 const auto* generic = std::get_if<AlarmItem>(&variant);
814 ASSERT_NE(generic, nullptr);
815
816 EXPECT_EQ(generic->userStructureId, static_cast<Usi>(0x1234U));
817 EXPECT_EQ(generic->rawData, Bytes({0xDEU, 0xADU}));
818}
819
821TEST_F(AlarmNotificationTests, LowPriorityHelperReturnsTrue)
822{
823 const Bytes data = BuildAlarmNotification(
824 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
825 0U);
826
827 const AlarmNotification notification = ParseAlarmNotification(data);
828
829 EXPECT_TRUE(notification.IsLowPriority());
830 EXPECT_FALSE(notification.IsHighPriority());
831}
832
834TEST_F(AlarmNotificationTests, ParsesAllAlarmNotificationFields)
835{
836 constexpr std::uint16_t sequence = 0x0345U;
837
838 const std::uint16_t specifier =
839 sequence |
840 0x0800U | // Channel diagnosis.
841 0x1000U | // Manufacturer specific.
842 0x2000U | // Submodule diagnosis state.
843 0x4000U; // AR diagnosis state.
844
845 const Bytes data = BuildAlarmNotification(
846 static_cast<std::uint16_t>(BlockType::AlarmNotificationHigh),
847 specifier);
848
849 const AlarmNotification notification = ParseAlarmNotification(data);
850
851 EXPECT_TRUE(notification.IsHighPriority());
852 EXPECT_FALSE(notification.IsLowPriority());
853
854 EXPECT_EQ(notification.blockVersion.first, 0x02U);
855 EXPECT_EQ(notification.blockVersion.second, 0x01U);
856 EXPECT_EQ(notification.alarmType, static_cast<AlarmType>(0x0007U));
857 EXPECT_EQ(notification.AlarmTypeName(), GetAlarmTypeName(
858 static_cast<AlarmType>(0x0007U)));
859
860 EXPECT_EQ(notification.api, 0x12345678U);
861 EXPECT_EQ(notification.slotNumber, 0x0021U);
862 EXPECT_EQ(notification.subslotNumber, 0x1002U);
863 EXPECT_EQ(notification.moduleIdentNumber, 0x11223344U);
864 EXPECT_EQ(notification.submoduleIdentNumber, 0x55667788U);
865
866 EXPECT_EQ(notification.alarmSequenceNumber, sequence);
867 EXPECT_TRUE(notification.channelDiagnosis);
868 EXPECT_TRUE(notification.manufacturerSpecific);
869 EXPECT_TRUE(notification.submoduleDiagnosisState);
870 EXPECT_TRUE(notification.arDiagnosisState);
871
872 EXPECT_EQ(notification.Location(), "305419896:33:0x1002");
873 EXPECT_TRUE(notification.rawPayload.empty());
874 EXPECT_TRUE(notification.items.empty());
875}
876
878TEST_F(AlarmNotificationTests, DiagnosisFlagsRemainFalseWhenUnset)
879{
880 const Bytes data = BuildAlarmNotification(
881 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
882 0x0005U);
883
884 const AlarmNotification notification = ParseAlarmNotification(data);
885
886 EXPECT_FALSE(notification.channelDiagnosis);
887 EXPECT_FALSE(notification.manufacturerSpecific);
888 EXPECT_FALSE(notification.submoduleDiagnosisState);
889 EXPECT_FALSE(notification.arDiagnosisState);
890 EXPECT_EQ(notification.alarmSequenceNumber, 5U);
891}
892
894TEST_F(AlarmNotificationTests, RawPayloadContainsOnlyBlockBodyAfterFixedHeader)
895{
896 const Bytes item = BuildChannelDiagnosisItem(
897 0x8001U,
898 0x0802U,
899 0x0009U);
900
901 const Bytes data = BuildAlarmNotification(
902 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
903 0x0001U,
904 item);
905
906 const AlarmNotification notification = ParseAlarmNotification(data);
907
908 ASSERT_EQ(notification.items.size(), 1U);
909
910 ASSERT_EQ(notification.rawPayload.size(), 8U);
911 EXPECT_EQ(Bytes(notification.rawPayload.begin(), notification.rawPayload.end()),
912 item);
913}
914
916TEST_F(AlarmNotificationTests, TruncatedTrailingAlarmItemIsIgnoredAfterValidItems)
917{
918 Bytes items = BuildChannelDiagnosisItem(
919 0x0002U,
920 0x0803U,
921 0x0004U);
922
923 items.push_back(0x80U);
924
925 const Bytes data = BuildAlarmNotification(
926 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
927 0x0001U,
928 items);
929
930 const AlarmNotification notification = ParseAlarmNotification(data);
931
932 ASSERT_EQ(notification.items.size(), 1U);
933
934 const auto* diagnosis = std::get_if<DiagnosisItem>(&notification.items.front());
935 ASSERT_NE(diagnosis, nullptr);
936
937 EXPECT_EQ(diagnosis->channelNumber, 2U);
938 EXPECT_EQ(diagnosis->channelProperties, 0x0803U);
939 EXPECT_EQ(diagnosis->channelErrorType, 4U);
940}
941
943TEST_F(AlarmNotificationTests, RejectsTooSmallBlockLength)
944{
945 const Bytes data = BuildAlarmNotification(
946 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
947 0U,
948 {},
949 21U);
950
951 EXPECT_THROW(ParseAlarmNotification(data), std::invalid_argument);
952}
953
955TEST_F(AlarmNotificationTests, RejectsBlockExtendingPastInput)
956{
957 const Bytes data = BuildAlarmNotification(
958 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
959 0U,
960 {},
961 30U);
962
963 ASSERT_EQ(data.size(), 26U);
964
965 EXPECT_THROW(ParseAlarmNotification(data), std::invalid_argument);
966}
967
969TEST_F(AlarmNotificationTests, AcceptsExactFixedSizeBlock)
970{
971 const Bytes data = BuildAlarmNotification(
972 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
973 0U);
974
975 ASSERT_EQ(data.size(), 26U);
976
977 const AlarmNotification notification = ParseAlarmNotification(data);
978
979 EXPECT_TRUE(notification.items.empty());
980 EXPECT_TRUE(notification.rawPayload.empty());
981}
982
984TEST_F(AlarmNotificationTests, IgnoresBytesAfterDeclaredBlock)
985{
986 Bytes data = BuildAlarmNotification(
987 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
988 0U);
989
990 data.push_back(0xDEU);
991 data.push_back(0xADU);
992 data.push_back(0xBEU);
993 data.push_back(0xEFU);
994
995 const AlarmNotification notification = ParseAlarmNotification(data);
996
997 EXPECT_TRUE(notification.items.empty());
998 EXPECT_TRUE(notification.rawPayload.empty());
999 EXPECT_EQ(data.size(), 30U);
1000}
1001
1003TEST_F(AlarmNotificationTests, ValidItemsRemainBoundedByBlockLength)
1004{
1005 const Bytes item = BuildChannelDiagnosisItem(
1006 7U,
1007 0x0801U,
1008 0x0033U);
1009
1010 Bytes data = BuildAlarmNotification(
1011 static_cast<std::uint16_t>(BlockType::AlarmNotificationLow),
1012 0x0001U,
1013 item);
1014
1015 const std::size_t blockSize = data.size();
1016
1017 data.insert(data.end(), 16U, 0x00U);
1018
1019 const AlarmNotification notification = ParseAlarmNotification(data);
1020
1021 EXPECT_EQ(notification.items.size(), 1U);
1022 EXPECT_EQ(notification.rawPayload.size(), 8U);
1023 EXPECT_EQ(blockSize, 34U);
1024}
1025
1027TEST_F(AlarmNotificationTests, LocationFormatsApiSlotAndSubslot)
1028{
1029 AlarmNotification notification;
1030 notification.api = 0xFFFFFFFFU;
1031 notification.slotNumber = 0xFFFFU;
1032 notification.subslotNumber = 0xFFFFU;
1033
1034 EXPECT_EQ(notification.Location(), "4294967295:65535:0xFFFF");
1035}
1036
1038TEST_F(AlarmNotificationTests, RejectsEmptyInput)
1039{
1040 EXPECT_THROW(ParseAlarmNotification({}), std::invalid_argument);
1041}
1042
1044TEST_F(AlarmItemVariantTests, UsiNameOfMatchesUsiOf)
1045{
1046 std::vector<AlarmItemVariant> items;
1047
1048 AlarmItem generic;
1049 generic.userStructureId = static_cast<Usi>(0x7001U);
1050 items.emplace_back(generic);
1051
1052 DiagnosisItem diagnosis;
1053 diagnosis.userStructureId = Usi::QualifiedChannelDiagnosis;
1054 items.emplace_back(diagnosis);
1055
1056 MaintenanceItem maintenance;
1057 items.emplace_back(maintenance);
1058
1059 UploadRetrievalItem upload;
1060 upload.userStructureId = Usi::IParameter;
1061 items.emplace_back(upload);
1062
1063 PEAlarmItem pe;
1064 items.emplace_back(pe);
1065
1066 RSAlarmItem rs;
1067 rs.userStructureId = Usi::RsAlarmSubmodule;
1068 items.emplace_back(rs);
1069
1070 PRALAlarmItem pral;
1071 items.emplace_back(pral);
1072
1073 for (const AlarmItemVariant& item : items)
1074 {
1075 EXPECT_EQ(UsiNameOf(item), GetUsiName(UsiOf(item)));
1076 }
1077}
1078
1079} // namespace
1080} // namespace profinet
Declares PROFINET alarm, diagnosis, maintenance, and USI payload types and parsers.
void PutU8(std::vector< std::uint8_t > &out, std::uint8_t value)
Append a single byte to a buffer.
Definition wire.h:89
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
@ AlarmNotificationHigh
Alarm notification high block.
@ AlarmNotificationLow
Alarm notification low block.
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
std::pair< AlarmItemVariant, std::size_t > ParseAlarmItem(const Bytes &data, std::size_t offset=0)
Parse a single alarm item starting at an offset.
Definition alarms.cpp:191
AlarmNotification ParseAlarmNotification(const Bytes &data)
Parse a complete AlarmNotification PDU (BlockHeader + body + items).
Definition alarms.cpp:245
AlarmType
PROFINET Alarm Types used in AlarmNotification blocks.
Definition indices.h:382
std::string GetUsiName(Usi usi)
Get the human-readable name for a User Structure Identifier.
Definition indices.cpp:386
std::string GetPeModeName(std::uint8_t mode)
Get the human-readable name for a PROFIenergy operational mode.
Definition indices.cpp:470
std::string UsiNameOf(const AlarmItemVariant &item)
Get the human-readable USI name for any AlarmItemVariant alternative.
Definition alarms.cpp:19
Usi
PROFINET User Structure Identifiers (USI).
Definition indices.h:526
@ Maintenance
Maintenance item.
@ IParameter
iParameter item.
@ PralAlarm
PRAL alarm item.
@ Upload
Upload record.
@ ExtendedChannelDiagnosis
Extended channel diagnosis.
@ PeAlarm
PE alarm item.
@ ChannelDiagnosis
Channel diagnosis.
@ RsAlarmHigh
RS alarm item for high alarm.
@ RsAlarmSubmodule
RS alarm item for submodule.
@ RsAlarmLow
RS alarm item for low alarm.
@ QualifiedChannelDiagnosis
Qualified channel diagnosis.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
std::string GetAlarmTypeName(AlarmType alarmType)
Get the human-readable name for an alarm type.
Definition indices.cpp:278
Usi UsiOf(const AlarmItemVariant &item)
Get the USI shared by every AlarmItemVariant alternative.
Definition alarms.cpp:13
std::variant< AlarmItem, DiagnosisItem, MaintenanceItem, UploadRetrievalItem, PEAlarmItem, RSAlarmItem, PRALAlarmItem > AlarmItemVariant
A parsed alarm payload item; the held alternative is selected by USI.
Definition alarms.h:334
PROFINET protocol wire-format structures and serialization helpers.
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.