10#include <gtest/gtest.h>
34Bytes MakeHeader(
const BlockType blockType,
const std::uint16_t blockLength,
35 const std::uint8_t versionHigh = 1,
const std::uint8_t versionLow = 0)
38 wire::PutU16(out,
static_cast<std::uint16_t
>(blockType));
48void AlignFromHeader(
Bytes& data,
const std::size_t blockHeaderSize)
50 while (((blockHeaderSize + data.size()) % 4U) != 0U)
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)
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());
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)
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)
104 for (
const auto& peer : peers)
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());
111 while ((body.size() % 4U) != 0U)
116 body.insert(body.end(), peer.macAddress.begin(), peer.macAddress.end());
118 while ((body.size() % 4U) != 0U)
129 while ((body.size() % 4U) != 0U)
134 if (includeBoundaries)
146 while ((body.size() % 4U) != 0U)
165 Bytes out = MakeHeader(type,
static_cast<std::uint16_t
>(body.size() + 2U));
177Bytes MakeMultipleBlock(
const std::uint32_t api,
const std::uint16_t slot,
178 const std::uint16_t subslot,
const std::vector<Bytes>& nested)
186 for (
const auto& block : nested)
188 body.insert(body.end(), block.begin(), block.end());
191 return MakeBlock(BlockType::MultipleHeader, body);
197Bytes MakeRidBody(
const std::vector<std::tuple<std::uint16_t, std::uint16_t, std::uint32_t, std::uint32_t>>& entries)
200 wire::PutU16(out,
static_cast<std::uint16_t
>(entries.size()));
201 for (
const auto& [slot, subslot, moduleIdent, submoduleIdent] : entries)
216Bytes MakeModuleDiff(
const std::uint16_t moduleState,
const std::uint16_t submoduleState)
229 return MakeBlock(BlockType::ModuleDifference, body);
236Bytes MakeMinimalWriteMultipleResponse()
248Bytes MakeRidV11Body()
270Bytes MakeWriteMultipleResponseBlock(
271 std::uint16_t blockType,
272 std::uint16_t sequence,
275 std::uint16_t subslot,
277 std::uint16_t additionalValue1,
278 std::uint16_t additionalValue2,
279 std::uint32_t status)
311 EXPECT_EQ(block.size(), 48U);
312 if (block.size() < 48U)
316 block.resize(56U, 0);
325class BlocksMoreTests :
public ::testing::Test
331TEST_F(BlocksMoreTests, BlockHeaderBodyLengthHandlesLengthsBelowTwo)
340TEST_F(BlocksMoreTests, BlockHeaderTypeNameHandlesUnknownType)
343 EXPECT_FALSE(header.TypeName().empty());
347TEST_F(BlocksMoreTests, SlotInfoToStringFormatsSubslot)
349 const SlotInfo info{.
slot = 7, .subslot = 0x8001, .api = 0x42};
351 EXPECT_EQ(info.ToString(),
"SlotInfo(api=66, slot=7, subslot=0x8001)");
355TEST_F(BlocksMoreTests, PeerInfoMacStrFormatsAddress)
359 EXPECT_EQ(peer.MacStr(),
"00:01:02:a0:b0:c0");
363TEST_F(BlocksMoreTests, PortInfoMauTypeNameCoversKnownAndUnknownValues)
365 const std::vector<std::pair<std::uint16_t, std::string>> values{
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"},
379 for (
const auto& [value, expected] : values)
392TEST_F(BlocksMoreTests, PortInfoLinkStateCoversAllBranches)
394 const std::vector<std::pair<std::uint8_t, std::string>> values{{0,
"Unknown"},
400 for (
const auto& [value, expected] : values)
409TEST_F(BlocksMoreTests, InterfaceInfoFormattingHelpers)
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}};
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");
421TEST_F(BlocksMoreTests, ParseBlockHeaderSupportsOffset)
423 Bytes data{0xAA, 0xBB};
424 const Bytes header = MakeHeader(BlockType::ModuleDifference, 12, 2, 3);
425 data.insert(data.end(), header.begin(), header.end());
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);
437TEST_F(BlocksMoreTests, ParseBlockHeaderRejectsTruncatedOffset)
439 const Bytes data{0x00, 0x01, 0x02, 0x03};
444TEST_F(BlocksMoreTests, ParseMultipleBlockHeaderParsesAllFields)
446 Bytes data(3U, 0xAA);
454 EXPECT_EQ(api, 0x01020304U);
456 EXPECT_EQ(subslot, 0x8001U);
457 EXPECT_EQ(next, 13U);
461TEST_F(BlocksMoreTests, ParseMultipleBlockHeaderRejectsTruncatedInput)
467TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealParsesCompleteData)
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});
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");
481TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsMissingChassisLength)
487TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsTruncatedChassis)
493TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsTruncatedMac)
501TEST_F(BlocksMoreTests, ParsePdInterfaceDataRealRejectsTruncatedIpConfiguration)
503 const auto body = MakeInterfaceBody(
"A",
509 Bytes truncated = body;
510 truncated.resize(truncated.size() - 1U);
516TEST_F(BlocksMoreTests, ParsePdPortDataRealKeepsSuppliedLocationForEmptyInput)
520 EXPECT_EQ(result.slot, 7U);
521 EXPECT_EQ(result.subslot, 0x8002U);
522 EXPECT_TRUE(result.portId.empty());
526TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingPortLength)
528 const Bytes body{0x00, 0x01, 0x80, 0x01};
531 EXPECT_EQ(result.slot, 1U);
532 EXPECT_EQ(result.subslot, 0x8001U);
533 EXPECT_TRUE(result.portId.empty());
537TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingPeerCount)
539 const Bytes body{0x00, 0x01, 0x80, 0x01, 0x01,
'P'};
543 EXPECT_EQ(result.portId,
"P");
544 EXPECT_TRUE(result.peers.empty());
548TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingPeerMac)
550 const auto expectedMacAddress =
MacAddress{0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
551 const PeerInfo peer{.
portId =
"P", .chassisId =
"C", .macAddress = expectedMacAddress};
553 Bytes body = MakePortBody(
"Port", {peer});
554 body.resize(body.size() - 4U);
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);
565TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesMissingMau)
567 const auto result =
ParsePdPortDataReal(MakePortBody(
"Port", {},
false,
false,
false,
false));
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);
578TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesIncompleteBoundaries)
580 Bytes body = MakePortBody(
"Port", {},
true,
true,
false,
false);
581 body.resize(body.size() - 4U);
585 EXPECT_EQ(result.mauType, 30U);
586 EXPECT_EQ(result.domainBoundary, 0U);
587 EXPECT_EQ(result.multicastBoundary, 0U);
591TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesIncompleteLinkState)
593 Bytes body = MakePortBody(
"Port", {},
true,
true,
true,
false);
598 body.resize(body.size() - 3U);
601 EXPECT_EQ(result.domainBoundary, 0x11223344U);
602 EXPECT_EQ(result.multicastBoundary, 0x55667788U);
603 EXPECT_EQ(result.linkStatePort, 0U);
604 EXPECT_EQ(result.linkStateLink, 0U);
608TEST_F(BlocksMoreTests, ParsePdPortDataRealHandlesIncompleteMediaType)
610 Bytes body = MakePortBody(
"Port", {},
true,
true,
true,
true);
611 body.resize(body.size() - 1U);
615 EXPECT_EQ(result.linkStatePort, 1U);
616 EXPECT_EQ(result.linkStateLink, 2U);
617 EXPECT_EQ(result.mediaType, 0U);
621TEST_F(BlocksMoreTests, ParsePdRealDataCollectsMultipleHeaderContent)
623 const auto interfaceBody = MakeInterfaceBody(
"PLC",
624 {0x10, 0x11, 0x12, 0x13, 0x14,
630 const auto interfaceBlock = MakeBlock(BlockType::PortDataInterfaceDataReal, interfaceBody);
632 const auto portBlock = MakeBlock(BlockType::PortDataReal, MakePortBody(
"P1", {}));
634 const auto outer = MakeMultipleBlock(0, 1, 0x8001, {interfaceBlock, portBlock});
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);
650TEST_F(BlocksMoreTests, ParsePdRealDataSkipsNonMultipleBlock)
652 const auto data = MakeBlock(BlockType::PortDataReal, MakePortBody(
"P1", {}));
656 EXPECT_TRUE(result.slots.empty());
657 EXPECT_TRUE(result.ports.empty());
658 EXPECT_TRUE(result.rawBlocks.empty());
662TEST_F(BlocksMoreTests, ParsePdRealDataIgnoresMalformedMultipleHeader)
664 const auto malformed = MakeHeader(BlockType::MultipleHeader, 20);
668 EXPECT_TRUE(result.slots.empty());
669 EXPECT_TRUE(result.rawBlocks.empty());
673TEST_F(BlocksMoreTests, ParseRealIdentificationDataAcceptsRecognizedHeader)
675 const auto body = MakeRidBody({{1, 0x8001, 0x1000, 0x2000}});
676 const auto data = MakeBlock(BlockType::RealIdentificationDataApi, body);
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);
688TEST_F(BlocksMoreTests, ParseRealIdentificationDataFallsBackToRawBody)
690 const auto body = MakeRidBody({{2, 3, 0x1234, 0x5678}});
693 const auto expectedVersion = std::pair<std::uint8_t, std::uint8_t>{1, 0};
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);
702TEST_F(BlocksMoreTests, ParseRealIdentificationDataHandlesTruncatedEntry)
710 body.push_back(0x56);
714 EXPECT_TRUE(result.slots.empty());
718TEST_F(BlocksMoreTests, ParsePortStatisticsSupportsOffset)
720 Bytes data(4U, 0xAA);
731 ASSERT_TRUE(result.has_value());
732 EXPECT_EQ(result->counterStatus, 7U);
733 EXPECT_EQ(result->inOctets, 1U);
734 EXPECT_EQ(result->outErrors, 6U);
738TEST_F(BlocksMoreTests, ModuleDiffSubmoduleStateNameCoversValues)
743 EXPECT_TRUE(submodule.
IsOk());
746 EXPECT_FALSE(submodule.
IsOk());
747 EXPECT_FALSE(submodule.
StateName().empty());
750 EXPECT_FALSE(submodule.
IsOk());
751 EXPECT_FALSE(submodule.
StateName().empty());
755TEST_F(BlocksMoreTests, ModuleDiffModuleEvaluatesState)
758 module.moduleState = MODULE_STATE_PROPER_MODULE;
759 EXPECT_TRUE(module.IsProper());
761 module.moduleState = 0xFFFF;
762 EXPECT_FALSE(module.IsProper());
763 EXPECT_FALSE(module.StateName().empty());
767TEST_F(BlocksMoreTests, ModuleDiffBlockReportsAllMismatches)
781 block.
modules = {proper, wrongModule};
783 EXPECT_FALSE(block.
AllOk());
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);
793TEST_F(BlocksMoreTests, IODWriteMultipleBuilderBuildsEmptyPacket)
795 std::array<std::uint8_t, uuidLenght> uuid{};
798 const auto packet = builder.
Build();
800 EXPECT_FALSE(packet.empty());
806TEST_F(BlocksMoreTests, IODWriteMultipleBuilderStoresApiAndMultipleWrites)
808 std::array<std::uint8_t, uuidLenght> uuid{};
812 builder.
AddWrite(1, 1, 0xAFF0, {0x01}, 0x1000)
813 .AddWrite(2, 3, 0xAFF1, {0x02, 0x03}, 0x2000);
815 const auto packet = builder.
Build();
817 EXPECT_GT(packet.size(), 64U);
824TEST_F(BlocksMoreTests, ParseWriteMultipleResponseRejectsShortInput)
831TEST_F(BlocksMoreTests, ParseWriteMultipleResponseHandlesMinimalResponse)
833 const auto response = MakeMinimalWriteMultipleResponse();
836 EXPECT_TRUE(results.empty());
840TEST_F(BlocksMoreTests, ExpectedSubmoduleDataDescriptionSerializes)
844 .submoduleDataLength = 42,
848 const auto bytes = description.ToBytes();
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);
860TEST_F(BlocksMoreTests, ExpectedSubmoduleSubmoduleTypeMasksLowBits)
863 EXPECT_EQ(submodule.SubmoduleType(), 0U);
865 submodule.submoduleProperties = 0xFFFF;
866 EXPECT_EQ(submodule.SubmoduleType(), 3U);
870TEST_F(BlocksMoreTests, ExpectedSubmoduleSerializes)
874 .submoduleIdentNumber = 0x12345678,
875 .submoduleProperties = 3};
879 const auto bytes = submodule.ToBytes();
881 EXPECT_GT(bytes.size(), 8U);
885TEST_F(BlocksMoreTests, ExpectedSubmoduleSlotSerializes)
891 .submoduleIdentNumber = 0x200,
892 .submoduleProperties = 1});
894 const auto bytes = slot.ToBytes();
896 EXPECT_GT(bytes.size(), 14U);
900TEST_F(BlocksMoreTests, ExpectedSubmoduleAPISerializes)
907 .moduleIdentNumber = 0x100});
909 const auto bytes = api.ToBytes();
911 EXPECT_GT(bytes.size(), 0U);
915TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqCreatesSeparateApis)
921 const auto bytes = req.
ToBytes();
923 ASSERT_GE(bytes.size(), 8U);
928 EXPECT_EQ(r.
U16(), 1U);
933 ASSERT_GE(bytes.size(), 14U);
935 EXPECT_EQ(r.
U32(), 801U);
939TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqBuildsOutputOnlySubmodule)
945 const auto bytes = req.
ToBytes();
947 EXPECT_GT(bytes.size(), 30U);
951TEST_F(BlocksMoreTests, WriteMultipleResultSuccessReflectsStatus)
959 EXPECT_FALSE(result.
Success());
961 result.
status = 0xFFFFFFFFU;
962 EXPECT_FALSE(result.
Success());
966TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqCoversAllSubmoduleTypes)
971 std::size_t expectedDescriptionCount;
974 const std::vector<Case> cases{
982 for (
const auto& testCase : cases)
996 const auto bytes = req.
ToBytes();
1003 constexpr std::size_t headerAndSlot = 6U + 2U + 14U;
1005 const std::size_t expectedSize =
1006 headerAndSlot + 2U + 4U + 2U + testCase.expectedDescriptionCount * 6U;
1008 EXPECT_EQ(bytes.size(), expectedSize)
1009 <<
"submoduleType=" << testCase.type;
1014TEST_F(BlocksMoreTests, ExpectedSubmoduleBlockReqReusesExistingApiAndSlot)
1021 const auto bytes = req.
ToBytes();
1025 EXPECT_EQ(r.
U16(), 1U);
1029 constexpr std::size_t numberOfSubmodulesOffset = 6U + 2U + 4U + 2U + 4U + 2U;
1030 r.
SkipTo(numberOfSubmodulesOffset);
1031 EXPECT_EQ(r.
U16(), 2U);
1035TEST_F(BlocksMoreTests, ParseModuleDiffBlockHandlesHeaderWithoutBody)
1037 const auto data = MakeHeader(BlockType::ModuleDifference, 2);
1041 EXPECT_TRUE(result.modules.empty());
1045TEST_F(BlocksMoreTests, ParseModuleDiffBlockStopsOnTruncatedApi)
1047 Bytes data = MakeHeader(BlockType::ModuleDifference, 8);
1054 EXPECT_TRUE(result.modules.empty());
1058TEST_F(BlocksMoreTests, ParseModuleDiffBlockStopsOnTruncatedModule)
1060 Bytes data = MakeHeader(BlockType::ModuleDifference, 8);
1068 EXPECT_TRUE(result.modules.empty());
1072TEST_F(BlocksMoreTests, ParsePortStatisticsRejectsTruncatedOffset)
1074 Bytes data(4U, 0xAA);
1075 data.resize(4U + 25U);
1081TEST_F(BlocksMoreTests, ParseRealIdentificationDataParsesV11ApiGroupedLayout)
1083 auto data = MakeBlock(
1084 BlockType::RealIdentificationDataApi,
1088 ASSERT_GE(data.size(), 6U);
1093 const auto expectedVersion = std::pair<std::uint8_t, std::uint8_t>{1, 1};
1094 EXPECT_EQ(result.version, expectedVersion);
1096 ASSERT_EQ(result.slots.size(), 2U);
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);
1104 EXPECT_EQ(result.slots[1].api, 0x12345678U);
1105 EXPECT_EQ(result.slots[1].subslot, 0x8002U);
1106 EXPECT_EQ(result.slots[1].submoduleIdent, 0x2002U);
1110TEST_F(BlocksMoreTests, ParseWriteMultipleResponseParsesSuccessfulResult)
1112 Bytes response(64U, 0);
1114 const auto block = MakeWriteMultipleResponseBlock(
1125 ASSERT_EQ(block.size(), 56U);
1129 const std::uint32_t recordLength =
1130 static_cast<std::uint32_t
>(block.size());
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);
1137 response.insert(response.end(), block.begin(), block.end());
1141 ASSERT_EQ(results.size(), 1U);
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());
1155TEST_F(BlocksMoreTests, ParseWriteMultipleResponseParsesFailedResult)
1157 Bytes response(64U, 0);
1159 const auto block = MakeWriteMultipleResponseBlock(
1170 ASSERT_EQ(block.size(), 56U);
1172 const std::uint32_t recordLength =
1173 static_cast<std::uint32_t
>(block.size());
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);
1180 response.insert(response.end(), block.begin(), block.end());
1184 ASSERT_EQ(results.size(), 1U);
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());
1198TEST_F(BlocksMoreTests, ParseWriteMultipleResponseStopsOnUnexpectedBlockType)
1200 Bytes response(64U, 0);
1202 const auto block = MakeWriteMultipleResponseBlock(
1213 ASSERT_EQ(block.size(), 56U);
1220 response.insert(response.end(), block.begin(), block.end());
1226TEST_F(BlocksMoreTests, IODWriteMultipleBuilderSerializesWriteDetails)
1228 std::array<std::uint8_t, uuidLenght> uuid{};
1235 builder.
AddWrite(1, 0x8001, 0x0100, {0x11, 0x22}, 0x1000)
1236 .AddWrite(2, 0x8002, 0x0101, {0x33, 0x44, 0x55}, 0x2000);
1238 const auto packet = builder.
Build();
1240 ASSERT_GT(packet.size(), 64U);
1245 EXPECT_EQ(r.
U16(), 0x1234U);
1248 constexpr std::size_t firstBlock = 64U;
1252 r.
SkipTo(firstBlock + 6U);
1254 EXPECT_EQ(r.
U16(), 0U);
Declares PROFINET IO connection-management block structures and wire serialization.
ExpectedSubmoduleBlockReq (0x0104) builder.
Bytes ToBytes() const
Serialize the complete block to raw bytes.
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.
Builder for IODWriteMultipleReq packets (index 0xE040).
static constexpr std::uint16_t writeMultipleBlockType
Block type used for each nested write block.
Bytes Build() const
Build the complete IODWriteMultipleReq packet.
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.
Lightweight cursor for parsing a big-endian byte buffer without copies.
std::uint32_t U32()
Read and consume a big-endian 32-bit value.
void SkipTo(std::size_t offset)
Move the read position to an absolute byte offset.
void Skip(std::size_t n)
Advance the read position without returning the skipped bytes.
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
PROFINET record-data indices, block types, and protocol enumerations.
ModuleDiffBlock ParseModuleDiffBlock(const Bytes &data)
Parse a ModuleDiffBlock (0x8104) from raw bytes.
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.
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.
std::vector< WriteMultipleResult > ParseWriteMultipleResponse(const Bytes &data)
Parse an IODWriteMultipleRes into individual per-write results.
PDRealData ParsePdRealData(const Bytes &data)
Parse a complete PDRealData (0xF841) response.
RealIdentificationData ParseRealIdentificationData(const Bytes &data)
Parse a RealIdentificationData (0xF000 or 0x0013) response.
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.
std::optional< PortStatistics > ParsePortStatistics(const Bytes &data, std::size_t offset=0)
Parse a PDPortStatistic (0x0251) block body.
InterfaceInfo ParsePdInterfaceDataReal(const Bytes &data, std::size_t offset=0, std::size_t blockHeaderSize=blockHeaderLength)
Parse a PDInterfaceDataReal (0x0240) block body.
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.
void PutU8(std::vector< std::uint8_t > &out, std::uint8_t value)
Append a single byte to a buffer.
void PutU16(std::vector< std::uint8_t > &out, std::uint16_t value)
Append a 16-bit value to a buffer in big-endian order.
void PutU32(std::vector< std::uint8_t > &out, std::uint32_t value)
Append a 32-bit value to a buffer in big-endian order.
BlockType
PROFINET block types.
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
constexpr int uuidLenght
Constant lenght of a UUID.
constexpr int blockHeaderLength
Constant lenght of a blockHeader.
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
constexpr std::uint16_t SUBMODULE_STATE_WRONG_SUBMODULE
Submodule State Wrong Submodule.
constexpr std::uint16_t MODULE_STATE_PROPER_MODULE
Module State Proper Module.
constexpr std::uint16_t WRITE_MULTIPLE
Write Multiple.
constexpr std::uint16_t SUBMODULE_STATE_OK
Submodule State Ok.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Expected submodules for a specific API/slot.
std::uint32_t api
API number.
Describes I/O data for a submodule (one direction: Input or Output).
std::uint16_t dataDescription
Data direction: 1 = Input, 2 = Output.
One slot with in an api submodule.
std::uint32_t api
API number.
Expected submodule within a slot.
std::uint16_t subslotNumber
Subslot number.
std::uint16_t submoduleProperties
Submodule property flags (encodes the I/O direction type).
Interface information from PDInterfaceDataReal (0x0240).
std::string chassisId
LLDP chassis ID.
Parsed ModuleDiffBlock (0x8104).
std::vector< ModuleDiffModule > modules
Modules described by this block.
bool AllOk() const
Whether every module and submodule matches the expected configuration.
std::vector< std::tuple< std::uint16_t, std::uint16_t, std::string > > GetMismatches() const
List every non-OK module/submodule.
Difference info for a single module (slot).
std::vector< ModuleDiffSubmodule > submodules
Submodules within this module.
std::uint16_t moduleState
Module state (see MODULE_STATE_* constants).
std::uint16_t slotNumber
Slot number.
Difference info for a single submodule.
std::string StateName() const
Human-readable name for SubmoduleState.
std::uint16_t subslotNumber
Subslot number.
bool IsOk() const
Whether this submodule matches the expected configuration.
std::uint16_t submoduleState
Submodule state (see SUBMODULE_STATE_* constants).
LLDP peer information from PDPortDataReal.
std::string portId
Peer's LLDP port ID.
std::array< std::uint8_t, macAddressLength > macAddress
Peer's MAC address.
Port information from PDPortDataReal (0x020F).
std::string MauTypeName() const
Human-readable name for MauType.
std::uint16_t mauType
MAU type (media attachment unit) code.
std::string LinkState() const
Human-readable name for LinkStateLink.
std::uint8_t linkStateLink
Link-level link state.
Slot/subslot discovered from a device.
std::uint16_t slot
Slot number.
Result of a single write within a WriteMultiple operation.
std::uint32_t status
PNIO status code (0 = success).
bool Success() const
Whether this individual write succeeded.
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.