PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_blocksMore.cpp
Go to the documentation of this file.
1
9
10#include <gtest/gtest.h>
11
12#include <array>
13#include <cstdint>
14#include <string>
15#include <tuple>
16#include <utility>
17#include <vector>
18
19#include "profinet/blocks.h"
20#include "profinet/indices.h"
21#include "profinet/wire.h"
22
23using namespace profinet;
24using namespace profinet::blocks;
25
26namespace
27{
34Bytes MakeHeader(const BlockType blockType, const std::uint16_t blockLength,
35 const std::uint8_t versionHigh = 1, const std::uint8_t versionLow = 0)
36{
37 Bytes out;
38 wire::PutU16(out, static_cast<std::uint16_t>(blockType));
39 wire::PutU16(out, blockLength);
40 wire::PutU8(out, versionHigh);
41 wire::PutU8(out, versionLow);
42 return out;
43}
44
48void AlignFromHeader(Bytes& data, const std::size_t blockHeaderSize)
49{
50 while (((blockHeaderSize + data.size()) % 4U) != 0U)
51 {
52 data.push_back(0);
53 }
54}
55
64Bytes MakeInterfaceBody(const std::string& chassisId, const MacAddress& mac,
65 const std::array<std::uint8_t, 4>& ip, const std::array<std::uint8_t, 4>& subnet,
66 const std::array<std::uint8_t, 4>& gateway, const std::size_t blockHeaderSize = blockHeaderLength)
67{
68 Bytes body;
69 wire::PutU8(body, static_cast<std::uint8_t>(chassisId.size()));
70 body.insert(body.end(), chassisId.begin(), chassisId.end());
71 AlignFromHeader(body, blockHeaderSize);
72 body.insert(body.end(), mac.begin(), mac.end());
73 AlignFromHeader(body, blockHeaderSize);
74 body.insert(body.end(), ip.begin(), ip.end());
75 body.insert(body.end(), subnet.begin(), subnet.end());
76 body.insert(body.end(), gateway.begin(), gateway.end());
77 return body;
78}
79
88Bytes MakePortBody(const std::string& portId, const std::vector<PeerInfo>& peers,
89 const bool includeMau = true, const bool includeBoundaries = true,
90 const bool includeLink = true, const bool includeMedia = true)
91{
92 Bytes body;
93 wire::PutU16(body, 1);
94 wire::PutU16(body, 0x8001);
95
96 wire::PutU8(body, static_cast<std::uint8_t>(portId.size()));
97 body.insert(body.end(), portId.begin(), portId.end());
98 wire::PutU8(body, static_cast<std::uint8_t>(peers.size()));
99 while ((body.size() % 4U) != 0U)
100 {
101 body.push_back(0);
102 }
103
104 for (const auto& peer : peers)
105 {
106 wire::PutU8(body, static_cast<std::uint8_t>(peer.portId.size()));
107 body.insert(body.end(), peer.portId.begin(), peer.portId.end());
108 wire::PutU8(body, static_cast<std::uint8_t>(peer.chassisId.size()));
109 body.insert(body.end(), peer.chassisId.begin(), peer.chassisId.end());
110
111 while ((body.size() % 4U) != 0U)
112 {
113 body.push_back(0);
114 }
115
116 body.insert(body.end(), peer.macAddress.begin(), peer.macAddress.end());
117
118 while ((body.size() % 4U) != 0U)
119 {
120 body.push_back(0);
121 }
122 }
123
124 if (includeMau)
125 {
126 wire::PutU16(body, 30);
127 }
128
129 while ((body.size() % 4U) != 0U)
130 {
131 body.push_back(0);
132 }
133
134 if (includeBoundaries)
135 {
136 wire::PutU32(body, 0x11223344);
137 wire::PutU32(body, 0x55667788);
138 }
139
140 if (includeLink)
141 {
142 wire::PutU8(body, 1);
143 wire::PutU8(body, 2);
144 }
145
146 while ((body.size() % 4U) != 0U)
147 {
148 body.push_back(0);
149 }
150
151 if (includeMedia)
152 {
153 wire::PutU32(body, 0x99AABBCC);
154 }
155
156 return body;
157}
158
163Bytes MakeBlock(const BlockType type, const Bytes& body)
164{
165 Bytes out = MakeHeader(type, static_cast<std::uint16_t>(body.size() + 2U));
166 wire::PutFixed(out, body);
167
168 return out;
169}
170
177Bytes MakeMultipleBlock(const std::uint32_t api, const std::uint16_t slot,
178 const std::uint16_t subslot, const std::vector<Bytes>& nested)
179{
180 Bytes body;
181 wire::PutU16(body, 0);
182 wire::PutU32(body, api);
183 wire::PutU16(body, slot);
184 wire::PutU16(body, subslot);
185
186 for (const auto& block : nested)
187 {
188 body.insert(body.end(), block.begin(), block.end());
189 }
190
191 return MakeBlock(BlockType::MultipleHeader, body);
192}
193
197Bytes MakeRidBody(const std::vector<std::tuple<std::uint16_t, std::uint16_t, std::uint32_t, std::uint32_t>>& entries)
198{
199 Bytes out;
200 wire::PutU16(out, static_cast<std::uint16_t>(entries.size()));
201 for (const auto& [slot, subslot, moduleIdent, submoduleIdent] : entries)
202 {
203 wire::PutU16(out, slot);
204 wire::PutU32(out, moduleIdent);
205 wire::PutU16(out, 1);
206 wire::PutU16(out, subslot);
207 wire::PutU32(out, submoduleIdent);
208 }
209 return out;
210}
211
216Bytes MakeModuleDiff(const std::uint16_t moduleState, const std::uint16_t submoduleState)
217{
218 Bytes body;
219 wire::PutU16(body, 1);
220 wire::PutU32(body, 0);
221 wire::PutU16(body, 1);
222 wire::PutU16(body, 1);
223 wire::PutU32(body, 0x1234);
224 wire::PutU16(body, moduleState);
225 wire::PutU16(body, 1);
226 wire::PutU16(body, 1);
227 wire::PutU32(body, 0x5678);
228 wire::PutU16(body, submoduleState);
229 return MakeBlock(BlockType::ModuleDifference, body);
230}
231
236Bytes MakeMinimalWriteMultipleResponse()
237{
238 Bytes data(64U, 0);
239 data[0] = 0x00;
240 data[1] = 0x08;
241 data[2] = 0x00;
242 data[3] = 0x3A;
243 data[4] = 0x01;
244 data[5] = 0x00;
245 return data;
246}
248Bytes MakeRidV11Body()
249{
250 Bytes out;
251
252 wire::PutU16(out, 1); // NumberOfAPIs
253 wire::PutU32(out, 0x12345678); // API
254 wire::PutU16(out, 1); // NumberOfSlots
255
256 wire::PutU16(out, 2); // Slot
257 wire::PutU32(out, 0x1000); // ModuleIdent
258 wire::PutU16(out, 2); // NumberOfSubslots
259
260 wire::PutU16(out, 0x8001);
261 wire::PutU32(out, 0x2001);
262
263 wire::PutU16(out, 0x8002);
264 wire::PutU32(out, 0x2002);
265
266 return out;
267}
268
270Bytes MakeWriteMultipleResponseBlock(
271 std::uint16_t blockType,
272 std::uint16_t sequence,
273 std::uint32_t api,
274 std::uint16_t slot,
275 std::uint16_t subslot,
276 std::uint16_t index,
277 std::uint16_t additionalValue1,
278 std::uint16_t additionalValue2,
279 std::uint32_t status)
280{
281 Bytes block;
282
283 // Block header: 4 bytes.
284 wire::PutU16(block, blockType);
285 wire::PutU16(block, 52); // BlockLength: total block size is 4 + 52 = 56.
286
287 // Version + sequence number: 4 bytes.
288 wire::PutU8(block, 0x01);
289 wire::PutU8(block, 0x00);
290 wire::PutU16(block, sequence);
291
292 // AR UUID: 16 bytes.
293 block.insert(block.end(), uuidLenght, 0);
294
295 // API + slot + subslot + padding.
296 wire::PutU32(block, api);
297 wire::PutU16(block, slot);
298 wire::PutU16(block, subslot);
299 wire::PutU16(block, 0);
300
301 // Record index + record-data length.
302 wire::PutU16(block, index);
303 wire::PutU32(block, 0);
304
305 // Additional status values + PNIO status.
306 wire::PutU16(block, additionalValue1);
307 wire::PutU16(block, additionalValue2);
308 wire::PutU32(block, status);
309
310 // The parser consumes a fixed 56-byte result block.
311 EXPECT_EQ(block.size(), 48U);
312 if (block.size() < 48U)
313 {
314 return {};
315 }
316 block.resize(56U, 0);
317
318 return block;
319}
320} // namespace
321
322namespace
323{
325class BlocksMoreTests : public ::testing::Test
326{
327};
328} // namespace
329
331TEST_F(BlocksMoreTests, BlockHeaderBodyLengthHandlesLengthsBelowTwo)
332{
333 EXPECT_EQ(BlockHeader{.blockLength = 0}.BodyLength(), 0U);
334 EXPECT_EQ(BlockHeader{.blockLength = 1}.BodyLength(), 0U);
335 EXPECT_EQ(BlockHeader{.blockLength = 2}.BodyLength(), 0U);
336 EXPECT_EQ(BlockHeader{.blockLength = 9}.BodyLength(), 7U);
337}
338
340TEST_F(BlocksMoreTests, BlockHeaderTypeNameHandlesUnknownType)
341{
342 BlockHeader header{.blockType = static_cast<BlockType>(0xFFFF)};
343 EXPECT_FALSE(header.TypeName().empty());
344}
345
347TEST_F(BlocksMoreTests, SlotInfoToStringFormatsSubslot)
348{
349 const SlotInfo info{.slot = 7, .subslot = 0x8001, .api = 0x42};
350
351 EXPECT_EQ(info.ToString(), "SlotInfo(api=66, slot=7, subslot=0x8001)");
352}
353
355TEST_F(BlocksMoreTests, PeerInfoMacStrFormatsAddress)
356{
357 const PeerInfo peer{.macAddress = {0x00, 0x01, 0x02, 0xA0, 0xB0, 0xC0}};
358
359 EXPECT_EQ(peer.MacStr(), "00:01:02:a0:b0:c0");
360}
361
363TEST_F(BlocksMoreTests, PortInfoMauTypeNameCoversKnownAndUnknownValues)
364{
365 const std::vector<std::pair<std::uint16_t, std::string>> values{
366 {0, "Unknown"},
367 {10, "10BASE-T HD"},
368 {11, "10BASE-T FD"},
369 {15, "100BASE-TX HD"},
370 {16, "100BASE-TX FD"},
371 {17, "100BASE-FX HD"},
372 {18, "100BASE-FX FD"},
373 {29, "1000BASE-T HD"},
374 {30, "1000BASE-T FD"},
375 {21, "1000BASE-X HD"},
376 {22, "1000BASE-X FD"},
377 {40, "10GBASE-T"}};
378
379 for (const auto& [value, expected] : values)
380 {
381 PortInfo info;
382 info.mauType = value;
383 EXPECT_EQ(info.MauTypeName(), expected);
384 }
385
386 PortInfo unknown;
387 unknown.mauType = 999;
388 EXPECT_EQ(unknown.MauTypeName(), "Unknown(999)");
389}
390
392TEST_F(BlocksMoreTests, PortInfoLinkStateCoversAllBranches)
393{
394 const std::vector<std::pair<std::uint8_t, std::string>> values{{0, "Unknown"},
395 {1, "Up"},
396 {2, "Down"},
397 {3, "Testing"},
398 {4, "Unknown(4)"}};
399
400 for (const auto& [value, expected] : values)
401 {
402 PortInfo info;
403 info.linkStateLink = value;
404 EXPECT_EQ(info.LinkState(), expected);
405 }
406}
407
409TEST_F(BlocksMoreTests, InterfaceInfoFormattingHelpers)
410{
411 const InterfaceInfo info{
412 .chassisId = "chassis", .macAddress = {1, 2, 3, 4, 5, 6}, .ipAddress = {192, 168, 1, 10}, .subnetMask = {255, 255, 255, 0}, .gateway = {192, 168, 1, 1}};
413
414 EXPECT_EQ(info.MacStr(), "01:02:03:04:05:06");
415 EXPECT_EQ(info.IpStr(), "192.168.1.10");
416 EXPECT_EQ(info.SubnetStr(), "255.255.255.0");
417 EXPECT_EQ(info.GatewayStr(), "192.168.1.1");
418}
419
421TEST_F(BlocksMoreTests, ParseBlockHeaderSupportsOffset)
422{
423 Bytes data{0xAA, 0xBB};
424 const Bytes header = MakeHeader(BlockType::ModuleDifference, 12, 2, 3);
425 data.insert(data.end(), header.begin(), header.end());
426
427 const auto [parsed, next] = ParseBlockHeader(data, 2);
428
429 EXPECT_EQ(parsed.blockType, BlockType::ModuleDifference);
430 EXPECT_EQ(parsed.blockLength, 12U);
431 EXPECT_EQ(parsed.versionHigh, 2U);
432 EXPECT_EQ(parsed.versionLow, 3U);
433 EXPECT_EQ(next, 8U);
434}
435
437TEST_F(BlocksMoreTests, ParseBlockHeaderRejectsTruncatedOffset)
438{
439 const Bytes data{0x00, 0x01, 0x02, 0x03};
440 EXPECT_THROW(ParseBlockHeader(data, 3), std::invalid_argument);
441}
442
444TEST_F(BlocksMoreTests, ParseMultipleBlockHeaderParsesAllFields)
445{
446 Bytes data(3U, 0xAA);
447 wire::PutU16(data, 0x0000);
448 wire::PutU32(data, 0x01020304);
449 wire::PutU16(data, 0x0007);
450 wire::PutU16(data, 0x8001);
451
452 const auto [api, slot, subslot, next] = ParseMultipleBlockHeader(data, 3);
453
454 EXPECT_EQ(api, 0x01020304U);
455 EXPECT_EQ(slot, 7U);
456 EXPECT_EQ(subslot, 0x8001U);
457 EXPECT_EQ(next, 13U);
458}
459
461TEST_F(BlocksMoreTests, ParseMultipleBlockHeaderRejectsTruncatedInput)
462{
463 EXPECT_THROW(ParseMultipleBlockHeader(Bytes(12U, 0), 3), std::invalid_argument);
464}
465
467TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealParsesCompleteData)
468{
469 const auto body = MakeInterfaceBody("Chass\xC3\xA9", {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, {10, 20, 30, 40}, {255, 255, 255, 0}, {10, 20, 30, 1});
470
471 const auto result = ParsePdInterfaceDataReal(body);
472
473 EXPECT_EQ(result.chassisId, "Chassé");
474 EXPECT_EQ(result.MacStr(), "00:11:22:33:44:55");
475 EXPECT_EQ(result.IpStr(), "10.20.30.40");
476 EXPECT_EQ(result.SubnetStr(), "255.255.255.0");
477 EXPECT_EQ(result.GatewayStr(), "10.20.30.1");
478}
479
481TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsMissingChassisLength)
482{
483 EXPECT_THROW(ParsePdInterfaceDataReal(Bytes{}), std::invalid_argument);
484}
485
487TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsTruncatedChassis)
488{
489 EXPECT_THROW(ParsePdInterfaceDataReal(Bytes{0x05, 'a', 'b'}), std::invalid_argument);
490}
491
493TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsTruncatedMac)
494{
495 Bytes body{0x00};
496 body.push_back(0);
497 EXPECT_THROW(ParsePdInterfaceDataReal(body), std::invalid_argument);
498}
499
501TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsTruncatedIpConfiguration)
502{
503 const auto body = MakeInterfaceBody("A",
504 {0, 1, 2, 3, 4, 5},
505 {1, 2, 3, 4},
506 {255, 255, 255, 0},
507 {1, 2, 3, 1});
508
509 Bytes truncated = body;
510 truncated.resize(truncated.size() - 1U);
511
512 EXPECT_THROW(ParsePdInterfaceDataReal(truncated), std::invalid_argument);
513}
514
516TEST_F(BlocksMoreTests, ParsePdPortDataRealKeepsSuppliedLocationForEmptyInput)
517{
518 const auto result = ParsePdPortDataReal({}, 0, 7, 0x8002);
519
520 EXPECT_EQ(result.slot, 7U);
521 EXPECT_EQ(result.subslot, 0x8002U);
522 EXPECT_TRUE(result.portId.empty());
523}
524
526TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingPortLength)
527{
528 const Bytes body{0x00, 0x01, 0x80, 0x01};
529 const auto result = ParsePdPortDataReal(body, 0, 9, 9);
530
531 EXPECT_EQ(result.slot, 1U);
532 EXPECT_EQ(result.subslot, 0x8001U);
533 EXPECT_TRUE(result.portId.empty());
534}
535
537TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingPeerCount)
538{
539 const Bytes body{0x00, 0x01, 0x80, 0x01, 0x01, 'P'};
540
541 const auto result = ParsePdPortDataReal(body, 0, 9, 9);
542
543 EXPECT_EQ(result.portId, "P");
544 EXPECT_TRUE(result.peers.empty());
545}
546
548TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingPeerMac)
549{
550 const auto expectedMacAddress = MacAddress{0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
551 const PeerInfo peer{.portId = "P", .chassisId = "C", .macAddress = expectedMacAddress};
552
553 Bytes body = MakePortBody("Port", {peer});
554 body.resize(body.size() - 4U);
555
556 const auto result = ParsePdPortDataReal(body);
557
558 ASSERT_EQ(result.peers.size(), 1U);
559 EXPECT_EQ(result.peers[0].portId, "P");
560 EXPECT_EQ(result.peers[0].chassisId, "C");
561 EXPECT_EQ(result.peers[0].macAddress, expectedMacAddress);
562}
563
565TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingMau)
566{
567 const auto result = ParsePdPortDataReal(MakePortBody("Port", {}, false, false, false, false));
568
569 EXPECT_EQ(result.mauType, 0U);
570 EXPECT_EQ(result.domainBoundary, 0U);
571 EXPECT_EQ(result.multicastBoundary, 0U);
572 EXPECT_EQ(result.linkStatePort, 0U);
573 EXPECT_EQ(result.linkStateLink, 0U);
574 EXPECT_EQ(result.mediaType, 0U);
575}
576
578TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesIncompleteBoundaries)
579{
580 Bytes body = MakePortBody("Port", {}, true, true, false, false);
581 body.resize(body.size() - 4U);
582
583 const auto result = ParsePdPortDataReal(body);
584
585 EXPECT_EQ(result.mauType, 30U);
586 EXPECT_EQ(result.domainBoundary, 0U);
587 EXPECT_EQ(result.multicastBoundary, 0U);
588}
589
591TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesIncompleteLinkState)
592{
593 Bytes body = MakePortBody("Port", {}, true, true, true, false);
594
595 // The helper adds two alignment bytes after the two link-state bytes.
596 // Remove the padding and one link-state byte, leaving only one byte
597 // of the required two-byte link-state field.
598 body.resize(body.size() - 3U);
599 const auto result = ParsePdPortDataReal(body);
600
601 EXPECT_EQ(result.domainBoundary, 0x11223344U);
602 EXPECT_EQ(result.multicastBoundary, 0x55667788U);
603 EXPECT_EQ(result.linkStatePort, 0U);
604 EXPECT_EQ(result.linkStateLink, 0U);
605}
606
608TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesIncompleteMediaType)
609{
610 Bytes body = MakePortBody("Port", {}, true, true, true, true);
611 body.resize(body.size() - 1U);
612
613 const auto result = ParsePdPortDataReal(body);
614
615 EXPECT_EQ(result.linkStatePort, 1U);
616 EXPECT_EQ(result.linkStateLink, 2U);
617 EXPECT_EQ(result.mediaType, 0U);
618}
619
621TEST_F(BlocksMoreTests, ParsePdRealDataCollectsMultipleHeaderContent)
622{
623 const auto interfaceBody = MakeInterfaceBody("PLC",
624 {0x10, 0x11, 0x12, 0x13, 0x14,
625 0x15},
626 {192, 168, 1, 10},
627 {255, 255, 255, 0},
628 {192, 168, 1, 1});
629
630 const auto interfaceBlock = MakeBlock(BlockType::PortDataInterfaceDataReal, interfaceBody);
631
632 const auto portBlock = MakeBlock(BlockType::PortDataReal, MakePortBody("P1", {}));
633
634 const auto outer = MakeMultipleBlock(0, 1, 0x8001, {interfaceBlock, portBlock});
635
636 const auto result = ParsePdRealData(outer);
637
638 ASSERT_EQ(result.slots.size(), 1U);
639 EXPECT_EQ(result.slots[0].api, 0U);
640 EXPECT_EQ(result.slots[0].slot, 1U);
641 EXPECT_EQ(result.slots[0].subslot, 0x8001U);
642 ASSERT_EQ(result.slots[0].blocks.size(), 2U);
643 EXPECT_EQ(result.ports.size(), 1U);
644 EXPECT_TRUE(result.interface.has_value());
645 ASSERT_EQ(result.rawBlocks.size(), 1U);
646 EXPECT_EQ(std::get<1>(result.rawBlocks[0]), 1U);
647}
648
650TEST_F(BlocksMoreTests, ParsePdRealDataSkipsNonMultipleBlock)
651{
652 const auto data = MakeBlock(BlockType::PortDataReal, MakePortBody("P1", {}));
653
654 const auto result = ParsePdRealData(data);
655
656 EXPECT_TRUE(result.slots.empty());
657 EXPECT_TRUE(result.ports.empty());
658 EXPECT_TRUE(result.rawBlocks.empty());
659}
660
662TEST_F(BlocksMoreTests, ParsePdRealDataIgnoresMalformedMultipleHeader)
663{
664 const auto malformed = MakeHeader(BlockType::MultipleHeader, 20);
665
666 const auto result = ParsePdRealData(malformed);
667
668 EXPECT_TRUE(result.slots.empty());
669 EXPECT_TRUE(result.rawBlocks.empty());
670}
671
673TEST_F(BlocksMoreTests, ParseRealIdentificationDataAcceptsRecognizedHeader)
674{
675 const auto body = MakeRidBody({{1, 0x8001, 0x1000, 0x2000}});
676 const auto data = MakeBlock(BlockType::RealIdentificationDataApi, body);
677
678 const auto result = ParseRealIdentificationData(data);
679
680 EXPECT_EQ(result.version.first, 1U);
681 EXPECT_EQ(result.version.second, 0U);
682 ASSERT_EQ(result.slots.size(), 1U);
683 EXPECT_EQ(result.slots[0].slot, 1U);
684 EXPECT_EQ(result.slots[0].subslot, 0x8001U);
685}
686
688TEST_F(BlocksMoreTests, ParseRealIdentificationDataFallsBackToRawBody)
689{
690 const auto body = MakeRidBody({{2, 3, 0x1234, 0x5678}});
691 const auto result = ParseRealIdentificationData(body);
692
693 const auto expectedVersion = std::pair<std::uint8_t, std::uint8_t>{1, 0};
694
695 EXPECT_EQ(result.version, expectedVersion);
696 ASSERT_EQ(result.slots.size(), 1U);
697 EXPECT_EQ(result.slots[0].moduleIdent, 0x1234U);
698 EXPECT_EQ(result.slots[0].submoduleIdent, 0x5678U);
699}
700
702TEST_F(BlocksMoreTests, ParseRealIdentificationDataHandlesTruncatedEntry)
703{
704 Bytes body;
705 wire::PutU16(body, 1);
706 wire::PutU16(body, 1);
707 wire::PutU32(body, 0x1234);
708 wire::PutU16(body, 1);
709 wire::PutU16(body, 2);
710 body.push_back(0x56);
711
712 const auto result = ParseRealIdentificationData(body);
713
714 EXPECT_TRUE(result.slots.empty());
715}
716
718TEST_F(BlocksMoreTests, ParsePortStatisticsSupportsOffset)
719{
720 Bytes data(4U, 0xAA);
721 wire::PutU16(data, 0x0007);
722 wire::PutU32(data, 1);
723 wire::PutU32(data, 2);
724 wire::PutU32(data, 3);
725 wire::PutU32(data, 4);
726 wire::PutU32(data, 5);
727 wire::PutU32(data, 6);
728
729 const auto result = ParsePortStatistics(data, 4);
730
731 ASSERT_TRUE(result.has_value());
732 EXPECT_EQ(result->counterStatus, 7U);
733 EXPECT_EQ(result->inOctets, 1U);
734 EXPECT_EQ(result->outErrors, 6U);
735}
736
738TEST_F(BlocksMoreTests, ModuleDiffSubmoduleStateNameCoversValues)
739{
740 ModuleDiffSubmodule submodule;
742 EXPECT_EQ(submodule.StateName(), "OK");
743 EXPECT_TRUE(submodule.IsOk());
744
746 EXPECT_FALSE(submodule.IsOk());
747 EXPECT_FALSE(submodule.StateName().empty());
748
749 submodule.submoduleState = 0xFFFF;
750 EXPECT_FALSE(submodule.IsOk());
751 EXPECT_FALSE(submodule.StateName().empty());
752}
753
755TEST_F(BlocksMoreTests, ModuleDiffModuleEvaluatesState)
756{
757 ModuleDiffModule module;
758 module.moduleState = MODULE_STATE_PROPER_MODULE;
759 EXPECT_TRUE(module.IsProper());
760
761 module.moduleState = 0xFFFF;
762 EXPECT_FALSE(module.IsProper());
763 EXPECT_FALSE(module.StateName().empty());
764}
765
767TEST_F(BlocksMoreTests, ModuleDiffBlockReportsAllMismatches)
768{
769 ModuleDiffBlock block;
770
771 ModuleDiffModule proper;
772 proper.slotNumber = 1;
774 proper.submodules.push_back(ModuleDiffSubmodule{.subslotNumber = 1, .submoduleState = SUBMODULE_STATE_OK});
775
776 ModuleDiffModule wrongModule;
777 wrongModule.slotNumber = 2;
778 wrongModule.moduleState = 0xFFFF;
779 wrongModule.submodules.push_back(ModuleDiffSubmodule{.subslotNumber = 3, .submoduleState = SUBMODULE_STATE_WRONG_SUBMODULE});
780
781 block.modules = {proper, wrongModule};
782
783 EXPECT_FALSE(block.AllOk());
784 const auto mismatches = block.GetMismatches();
785 ASSERT_EQ(mismatches.size(), 2U);
786 EXPECT_EQ(std::get<0>(mismatches[0]), 2U);
787 EXPECT_EQ(std::get<1>(mismatches[0]), 0U);
788 EXPECT_EQ(std::get<0>(mismatches[1]), 2U);
789 EXPECT_EQ(std::get<1>(mismatches[1]), 3U);
790}
791
793TEST_F(BlocksMoreTests, IODWriteMultipleBuilderBuildsEmptyPacket)
794{
795 std::array<std::uint8_t, uuidLenght> uuid{};
796 IODWriteMultipleBuilder builder(uuid);
797
798 const auto packet = builder.Build();
799
800 EXPECT_FALSE(packet.empty());
801 wire::Reader r(packet);
803}
804
806TEST_F(BlocksMoreTests, IODWriteMultipleBuilderStoresApiAndMultipleWrites)
807{
808 std::array<std::uint8_t, uuidLenght> uuid{};
809 uuid[15] = 0xEE;
810
811 IODWriteMultipleBuilder builder(uuid, 0x1234);
812 builder.AddWrite(1, 1, 0xAFF0, {0x01}, 0x1000)
813 .AddWrite(2, 3, 0xAFF1, {0x02, 0x03}, 0x2000);
814
815 const auto packet = builder.Build();
816
817 EXPECT_GT(packet.size(), 64U);
818 wire::Reader r(packet);
819 r.Skip(34);
820 EXPECT_EQ(r.U16(), WRITE_MULTIPLE);
821}
822
824TEST_F(BlocksMoreTests, ParseWriteMultipleResponseRejectsShortInput)
825{
826 EXPECT_TRUE(ParseWriteMultipleResponse({}).empty());
827 EXPECT_TRUE(ParseWriteMultipleResponse(Bytes(63U, 0)).empty());
828}
829
831TEST_F(BlocksMoreTests, ParseWriteMultipleResponseHandlesMinimalResponse)
832{
833 const auto response = MakeMinimalWriteMultipleResponse();
834 const auto results = ParseWriteMultipleResponse(response);
835
836 EXPECT_TRUE(results.empty());
837}
838
840TEST_F(BlocksMoreTests, ExpectedSubmoduleDataDescriptionSerializes)
841{
843 .dataDescription = 2,
844 .submoduleDataLength = 42,
845 .lengthIocs = 2,
846 .lengthIops = 3};
847
848 const auto bytes = description.ToBytes();
849
850 ASSERT_EQ(bytes.size(), 6U);
851 EXPECT_EQ(bytes[0], 0U);
852 EXPECT_EQ(bytes[1], 2U);
853 EXPECT_EQ(bytes[2], 0U);
854 EXPECT_EQ(bytes[3], 42U);
855 EXPECT_EQ(bytes[4], 2U);
856 EXPECT_EQ(bytes[5], 3U);
857}
858
860TEST_F(BlocksMoreTests, ExpectedSubmoduleSubmoduleTypeMasksLowBits)
861{
862 ExpectedSubmodule submodule{.submoduleProperties = 0xFFFC};
863 EXPECT_EQ(submodule.SubmoduleType(), 0U);
864
865 submodule.submoduleProperties = 0xFFFF;
866 EXPECT_EQ(submodule.SubmoduleType(), 3U);
867}
868
870TEST_F(BlocksMoreTests, ExpectedSubmoduleSerializes)
871{
872 ExpectedSubmodule submodule{
873 .subslotNumber = 0x8001,
874 .submoduleIdentNumber = 0x12345678,
875 .submoduleProperties = 3};
876
877 submodule.dataDescriptions.push_back(ExpectedSubmoduleDataDescription{.dataDescription = 1, .submoduleDataLength = 4});
878
879 const auto bytes = submodule.ToBytes();
880
881 EXPECT_GT(bytes.size(), 8U);
882}
883
885TEST_F(BlocksMoreTests, ExpectedSubmoduleSlotSerializes)
886{
887 ExpectedSubmoduleSlot slot{.api = 0, .slotNumber = 1, .moduleIdentNumber = 0x100, .moduleProperties = 0};
888
889 slot.submodules.push_back(ExpectedSubmodule{
890 .subslotNumber = 1,
891 .submoduleIdentNumber = 0x200,
892 .submoduleProperties = 1});
893
894 const auto bytes = slot.ToBytes();
895
896 EXPECT_GT(bytes.size(), 14U);
897}
898
900TEST_F(BlocksMoreTests, ExpectedSubmoduleAPISerializes)
901{
902 ExpectedSubmoduleAPI api{.api = 0x1234};
903
904 api.slots.push_back(ExpectedSubmoduleSlot{
905 .api = 0x1234,
906 .slotNumber = 1,
907 .moduleIdentNumber = 0x100});
908
909 const auto bytes = api.ToBytes();
910
911 EXPECT_GT(bytes.size(), 0U);
912}
913
915TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqCreatesSeparateApis)
916{
918 req.AddSubmodule(801, 1, 1, 0x100, 0x200, 1, 4, 0)
919 .AddSubmodule(1, 2, 1, 0x300, 0x400, 2, 0, 4);
920
921 const auto bytes = req.ToBytes();
922
923 ASSERT_GE(bytes.size(), 8U);
924
925 // The body starts at offset 6. The first API contains one slot.
926 wire::Reader r(bytes);
927 r.Skip(6);
928 EXPECT_EQ(r.U16(), 1U);
929
930 // The first API slot starts at offset 8:
931 // API(4), Slot(2), ModuleIdent(4), ModuleProperties(2),
932 // NumberOfSubmodules(2), ...
933 ASSERT_GE(bytes.size(), 14U);
934
935 EXPECT_EQ(r.U32(), 801U);
936}
937
939TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqBuildsOutputOnlySubmodule)
940{
942
943 req.AddSubmodule(0, 1, 2, 0x100, 0x200, 2, 0, 4);
944
945 const auto bytes = req.ToBytes();
946
947 EXPECT_GT(bytes.size(), 30U);
948}
949
951TEST_F(BlocksMoreTests, WriteMultipleResultSuccessReflectsStatus)
952{
953 WriteMultipleResult result;
954
955 result.status = 0;
956 EXPECT_TRUE(result.Success());
957
958 result.status = 1;
959 EXPECT_FALSE(result.Success());
960
961 result.status = 0xFFFFFFFFU;
962 EXPECT_FALSE(result.Success());
963}
964
966TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqCoversAllSubmoduleTypes)
967{
968 struct Case
969 {
970 std::uint16_t type;
971 std::size_t expectedDescriptionCount;
972 };
973
974 const std::vector<Case> cases{
975 {0, 1},
976 {1, 1},
977 {2, 1},
978 {3, 2},
979 {99, 0},
980 };
981
982 for (const auto& testCase : cases)
983 {
985
986 req.AddSubmodule(
987 0,
988 1,
989 1,
990 0x100,
991 0x200,
992 testCase.type,
993 4,
994 8);
995
996 const auto bytes = req.ToBytes();
997
998 // Header = 6 bytes.
999 // API entry starts with NumberOfSlots = 2 bytes.
1000 // Slot header:
1001 // API(4) + Slot(2) + ModuleIdent(4) + Properties(2) +
1002 // NumberOfSubmodules(2) = 14 bytes.
1003 constexpr std::size_t headerAndSlot = 6U + 2U + 14U;
1004
1005 const std::size_t expectedSize =
1006 headerAndSlot + 2U + 4U + 2U + testCase.expectedDescriptionCount * 6U;
1007
1008 EXPECT_EQ(bytes.size(), expectedSize)
1009 << "submoduleType=" << testCase.type;
1010 }
1011}
1012
1014TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqReusesExistingApiAndSlot)
1015{
1017
1018 req.AddSubmodule(0, 1, 1, 0x100, 0x200, 1, 4, 0);
1019 req.AddSubmodule(0, 1, 2, 0x100, 0x201, 2, 0, 8);
1020
1021 const auto bytes = req.ToBytes();
1022 wire::Reader r(bytes);
1023 r.Skip(6);
1024 // One API, one slot, two submodules.
1025 EXPECT_EQ(r.U16(), 1U);
1026
1027 // NumberOfSubmodules is at offset:
1028 // 6 header + 2 API slot count + 4 API + 2 slot + 4 ident + 2 properties.
1029 constexpr std::size_t numberOfSubmodulesOffset = 6U + 2U + 4U + 2U + 4U + 2U;
1030 r.SkipTo(numberOfSubmodulesOffset);
1031 EXPECT_EQ(r.U16(), 2U);
1032}
1033
1035TEST_F(BlocksMoreTests, ParseModuleDiffBlockHandlesHeaderWithoutBody)
1036{
1037 const auto data = MakeHeader(BlockType::ModuleDifference, 2);
1038
1039 const auto result = ParseModuleDiffBlock(data);
1040
1041 EXPECT_TRUE(result.modules.empty());
1042}
1043
1045TEST_F(BlocksMoreTests, ParseModuleDiffBlockStopsOnTruncatedApi)
1046{
1047 Bytes data = MakeHeader(BlockType::ModuleDifference, 8);
1048
1049 wire::PutU16(data, 1);
1050 wire::PutU32(data, 0x12345678);
1051
1052 const auto result = ParseModuleDiffBlock(data);
1053
1054 EXPECT_TRUE(result.modules.empty());
1055}
1056
1058TEST_F(BlocksMoreTests, ParseModuleDiffBlockStopsOnTruncatedModule)
1059{
1060 Bytes data = MakeHeader(BlockType::ModuleDifference, 8);
1061
1062 wire::PutU16(data, 1);
1063 wire::PutU32(data, 0);
1064 wire::PutU16(data, 1);
1065
1066 const auto result = ParseModuleDiffBlock(data);
1067
1068 EXPECT_TRUE(result.modules.empty());
1069}
1070
1072TEST_F(BlocksMoreTests, ParsePortStatisticsRejectsTruncatedOffset)
1073{
1074 Bytes data(4U, 0xAA);
1075 data.resize(4U + 25U);
1076
1077 EXPECT_FALSE(ParsePortStatistics(data, 4).has_value());
1078}
1079
1081TEST_F(BlocksMoreTests, ParseRealIdentificationDataParsesV11ApiGroupedLayout)
1082{
1083 auto data = MakeBlock(
1084 BlockType::RealIdentificationDataApi,
1085 MakeRidV11Body());
1086
1087 // Change the version bytes to 1.1.
1088 ASSERT_GE(data.size(), 6U);
1089 data[4] = 1;
1090 data[5] = 1;
1091
1092 const auto result = ParseRealIdentificationData(data);
1093 const auto expectedVersion = std::pair<std::uint8_t, std::uint8_t>{1, 1};
1094 EXPECT_EQ(result.version, expectedVersion);
1095
1096 ASSERT_EQ(result.slots.size(), 2U);
1097
1098 EXPECT_EQ(result.slots[0].api, 0x12345678U);
1099 EXPECT_EQ(result.slots[0].slot, 2U);
1100 EXPECT_EQ(result.slots[0].moduleIdent, 0x1000U);
1101 EXPECT_EQ(result.slots[0].subslot, 0x8001U);
1102 EXPECT_EQ(result.slots[0].submoduleIdent, 0x2001U);
1103
1104 EXPECT_EQ(result.slots[1].api, 0x12345678U);
1105 EXPECT_EQ(result.slots[1].subslot, 0x8002U);
1106 EXPECT_EQ(result.slots[1].submoduleIdent, 0x2002U);
1107}
1108
1110TEST_F(BlocksMoreTests, ParseWriteMultipleResponseParsesSuccessfulResult)
1111{
1112 Bytes response(64U, 0);
1113
1114 const auto block = MakeWriteMultipleResponseBlock(
1115 0x8008,
1116 7,
1117 0x12345678,
1118 3,
1119 0x8001,
1120 0xAFF0,
1121 0x1111,
1122 0x2222,
1123 0);
1124
1125 ASSERT_EQ(block.size(), 56U);
1126
1127 // The outer response stores the length of the nested result data
1128 // at byte offsets 36..39.
1129 const std::uint32_t recordLength =
1130 static_cast<std::uint32_t>(block.size());
1131
1132 response[36] = static_cast<std::uint8_t>(recordLength >> 24U);
1133 response[37] = static_cast<std::uint8_t>(recordLength >> 16U);
1134 response[38] = static_cast<std::uint8_t>(recordLength >> 8U);
1135 response[39] = static_cast<std::uint8_t>(recordLength);
1136
1137 response.insert(response.end(), block.begin(), block.end());
1138
1139 const auto results = ParseWriteMultipleResponse(response);
1140
1141 ASSERT_EQ(results.size(), 1U);
1142
1143 EXPECT_EQ(results[0].seqNum, 7U);
1144 EXPECT_EQ(results[0].api, 0x12345678U);
1145 EXPECT_EQ(results[0].slot, 3U);
1146 EXPECT_EQ(results[0].subslot, 0x8001U);
1147 EXPECT_EQ(results[0].index, 0xAFF0U);
1148 EXPECT_EQ(results[0].additionalValue1, 0x1111U);
1149 EXPECT_EQ(results[0].additionalValue2, 0x2222U);
1150 EXPECT_EQ(results[0].status, 0U);
1151 EXPECT_TRUE(results[0].Success());
1152}
1153
1155TEST_F(BlocksMoreTests, ParseWriteMultipleResponseParsesFailedResult)
1156{
1157 Bytes response(64U, 0);
1158
1159 const auto block = MakeWriteMultipleResponseBlock(
1160 0x8008,
1161 2,
1162 0x00000010,
1163 1,
1164 2,
1165 3,
1166 0xAAAA,
1167 0xBBBB,
1168 0xC0000001U);
1169
1170 ASSERT_EQ(block.size(), 56U);
1171
1172 const std::uint32_t recordLength =
1173 static_cast<std::uint32_t>(block.size());
1174
1175 response[36] = static_cast<std::uint8_t>(recordLength >> 24U);
1176 response[37] = static_cast<std::uint8_t>(recordLength >> 16U);
1177 response[38] = static_cast<std::uint8_t>(recordLength >> 8U);
1178 response[39] = static_cast<std::uint8_t>(recordLength);
1179
1180 response.insert(response.end(), block.begin(), block.end());
1181
1182 const auto results = ParseWriteMultipleResponse(response);
1183
1184 ASSERT_EQ(results.size(), 1U);
1185
1186 EXPECT_EQ(results[0].seqNum, 2U);
1187 EXPECT_EQ(results[0].api, 0x00000010U);
1188 EXPECT_EQ(results[0].slot, 1U);
1189 EXPECT_EQ(results[0].subslot, 2U);
1190 EXPECT_EQ(results[0].index, 3U);
1191 EXPECT_EQ(results[0].additionalValue1, 0xAAAAU);
1192 EXPECT_EQ(results[0].additionalValue2, 0xBBBBU);
1193 EXPECT_EQ(results[0].status, 0xC0000001U);
1194 EXPECT_FALSE(results[0].Success());
1195}
1196
1198TEST_F(BlocksMoreTests, ParseWriteMultipleResponseStopsOnUnexpectedBlockType)
1199{
1200 Bytes response(64U, 0);
1201
1202 const auto block = MakeWriteMultipleResponseBlock(
1203 0x9999,
1204 1,
1205 0,
1206 1,
1207 1,
1208 1,
1209 0,
1210 0,
1211 0);
1212
1213 ASSERT_EQ(block.size(), 56U);
1214
1215 response[36] = 0;
1216 response[37] = 0;
1217 response[38] = 0;
1218 response[39] = 56;
1219
1220 response.insert(response.end(), block.begin(), block.end());
1221
1222 EXPECT_TRUE(ParseWriteMultipleResponse(response).empty());
1223}
1224
1226TEST_F(BlocksMoreTests, IODWriteMultipleBuilderSerializesWriteDetails)
1227{
1228 std::array<std::uint8_t, uuidLenght> uuid{};
1229 uuid[0] = 0xDE;
1230 uuid[1] = 0xAD;
1231 uuid[15] = 0xEF;
1232
1233 IODWriteMultipleBuilder builder(uuid, 0x1234);
1234
1235 builder.AddWrite(1, 0x8001, 0x0100, {0x11, 0x22}, 0x1000)
1236 .AddWrite(2, 0x8002, 0x0101, {0x33, 0x44, 0x55}, 0x2000);
1237
1238 const auto packet = builder.Build();
1239
1240 ASSERT_GT(packet.size(), 64U);
1241 wire::Reader r(packet);
1242 r.SkipTo(6U);
1243
1244 // Outer sequence number.
1245 EXPECT_EQ(r.U16(), 0x1234U);
1246
1247 // First nested block starts immediately after the 64-byte outer header.
1248 constexpr std::size_t firstBlock = 64U;
1249 r.SkipTo(firstBlock);
1250
1252 r.SkipTo(firstBlock + 6U);
1253 // Nested sequence number.
1254 EXPECT_EQ(r.U16(), 0U);
1255}
Declares PROFINET IO connection-management block structures and wire serialization.
ExpectedSubmoduleBlockReq (0x0104) builder.
Definition blocks.h:673
Bytes ToBytes() const
Serialize the complete block to raw bytes.
Definition blocks.cpp:958
ExpectedSubmoduleBlockReq & AddSubmodule(std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint32_t moduleIdent, std::uint32_t submoduleIdent, std::uint16_t submoduleType=0, std::uint16_t inputLength=0, std::uint16_t outputLength=0)
Add a single submodule.
Definition blocks.cpp:878
Builder for IODWriteMultipleReq packets (index 0xE040).
Definition blocks.h:487
static constexpr std::uint16_t writeMultipleBlockType
Block type used for each nested write block.
Definition blocks.h:493
Bytes Build() const
Build the complete IODWriteMultipleReq packet.
Definition blocks.cpp:733
IODWriteMultipleBuilder & AddWrite(std::uint16_t slot, std::uint16_t subslot, std::uint16_t index, Bytes data, std::uint32_t api=0)
Add a write operation.
Definition blocks.cpp:683
Lightweight cursor for parsing a big-endian byte buffer without copies.
Definition wire.h:192
std::uint32_t U32()
Read and consume a big-endian 32-bit value.
Definition wire.h:268
void SkipTo(std::size_t offset)
Move the read position to an absolute byte offset.
Definition wire.h:343
void Skip(std::size_t n)
Advance the read position without returning the skipped bytes.
Definition wire.h:330
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
Definition wire.h:258
PROFINET record-data indices, block types, and protocol enumerations.
ModuleDiffBlock ParseModuleDiffBlock(const Bytes &data)
Parse a ModuleDiffBlock (0x8104) from raw bytes.
Definition blocks.cpp:609
PortInfo ParsePdPortDataReal(const Bytes &data, std::size_t offset=0, std::uint16_t slot=0, std::uint16_t subslot=0)
Parse a PDPortDataReal (0x020F) block body.
Definition blocks.cpp:199
std::tuple< std::uint32_t, std::uint16_t, std::uint16_t, std::size_t > ParseMultipleBlockHeader(const Bytes &data, std::size_t offset=0)
Parse a MultipleBlockHeader (0x0400) body.
Definition blocks.cpp:130
std::vector< WriteMultipleResult > ParseWriteMultipleResponse(const Bytes &data)
Parse an IODWriteMultipleRes into individual per-write results.
Definition blocks.cpp:756
PDRealData ParsePdRealData(const Bytes &data)
Parse a complete PDRealData (0xF841) response.
Definition blocks.cpp:328
RealIdentificationData ParseRealIdentificationData(const Bytes &data)
Parse a RealIdentificationData (0xF000 or 0x0013) response.
Definition blocks.cpp:422
std::pair< BlockHeader, std::size_t > ParseBlockHeader(const Bytes &data, std::size_t offset=0)
Parse a 6-byte PROFINET block header at an offset.
Definition blocks.cpp:114
std::optional< PortStatistics > ParsePortStatistics(const Bytes &data, std::size_t offset=0)
Parse a PDPortStatistic (0x0251) block body.
Definition blocks.cpp:529
InterfaceInfo ParsePdInterfaceDataReal(const Bytes &data, std::size_t offset=0, std::size_t blockHeaderSize=blockHeaderLength)
Parse a PDInterfaceDataReal (0x0240) block body.
Definition blocks.cpp:145
void PutFixed(std::vector< std::uint8_t > &out, const std::array< std::uint8_t, N > &data)
Append a fixed-size byte array to a buffer.
Definition wire.h:124
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
BlockType
PROFINET block types.
Definition indices.h:89
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
constexpr int uuidLenght
Constant lenght of a UUID.
Definition util.h:40
constexpr int blockHeaderLength
Constant lenght of a blockHeader.
Definition protocol.h:58
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
constexpr std::uint16_t SUBMODULE_STATE_WRONG_SUBMODULE
Submodule State Wrong Submodule.
Definition indices.h:652
constexpr std::uint16_t MODULE_STATE_PROPER_MODULE
Module State Proper Module.
Definition indices.h:645
constexpr std::uint16_t WRITE_MULTIPLE
Write Multiple.
Definition indices.h:976
constexpr std::uint16_t SUBMODULE_STATE_OK
Submodule State Ok.
Definition indices.h:658
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
PROFINET block header (6 bytes).
Definition blocks.h:33
BlockType blockType
Block type identifier.
Definition blocks.h:35
std::uint16_t blockLength
Length in bytes of the block body, including the 2 version bytes.
Definition blocks.h:38
Expected submodules for a specific API/slot.
Definition blocks.h:647
std::uint32_t api
API number.
Definition blocks.h:649
Describes I/O data for a submodule (one direction: Input or Output).
Definition blocks.h:578
std::uint16_t dataDescription
Data direction: 1 = Input, 2 = Output.
Definition blocks.h:580
One slot with in an api submodule.
Definition blocks.h:624
std::uint32_t api
API number.
Definition blocks.h:626
Expected submodule within a slot.
Definition blocks.h:598
std::uint16_t subslotNumber
Subslot number.
Definition blocks.h:600
std::uint16_t submoduleProperties
Submodule property flags (encodes the I/O direction type).
Definition blocks.h:606
Interface information from PDInterfaceDataReal (0x0240).
Definition blocks.h:168
std::string chassisId
LLDP chassis ID.
Definition blocks.h:170
Parsed ModuleDiffBlock (0x8104).
Definition blocks.h:415
std::vector< ModuleDiffModule > modules
Modules described by this block.
Definition blocks.h:417
bool AllOk() const
Whether every module and submodule matches the expected configuration.
Definition blocks.cpp:570
std::vector< std::tuple< std::uint16_t, std::uint16_t, std::string > > GetMismatches() const
List every non-OK module/submodule.
Definition blocks.cpp:589
Difference info for a single module (slot).
Definition blocks.h:381
std::vector< ModuleDiffSubmodule > submodules
Submodules within this module.
Definition blocks.h:395
std::uint16_t moduleState
Module state (see MODULE_STATE_* constants).
Definition blocks.h:392
std::uint16_t slotNumber
Slot number.
Definition blocks.h:386
Difference info for a single submodule.
Definition blocks.h:353
std::string StateName() const
Human-readable name for SubmoduleState.
Definition blocks.cpp:551
std::uint16_t subslotNumber
Subslot number.
Definition blocks.h:355
bool IsOk() const
Whether this submodule matches the expected configuration.
Definition blocks.cpp:556
std::uint16_t submoduleState
Submodule state (see SUBMODULE_STATE_* constants).
Definition blocks.h:361
LLDP peer information from PDPortDataReal.
Definition blocks.h:93
std::string portId
Peer's LLDP port ID.
Definition blocks.h:95
std::array< std::uint8_t, macAddressLength > macAddress
Peer's MAC address.
Definition blocks.h:101
Port information from PDPortDataReal (0x020F).
Definition blocks.h:119
std::string MauTypeName() const
Human-readable name for MauType.
Definition blocks.cpp:87
std::uint16_t mauType
MAU type (media attachment unit) code.
Definition blocks.h:130
std::string LinkState() const
Human-readable name for LinkStateLink.
Definition blocks.cpp:97
std::uint8_t linkStateLink
Link-level link state.
Definition blocks.h:136
Slot/subslot discovered from a device.
Definition blocks.h:60
std::uint16_t slot
Slot number.
Definition blocks.h:62
Result of a single write within a WriteMultiple operation.
Definition blocks.h:450
std::uint32_t status
PNIO status code (0 = success).
Definition blocks.h:467
bool Success() const
Whether this individual write succeeded.
Definition blocks.h:477
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.