21#include <gtest/gtest.h>
38 std::uint16_t blockLength,
39 std::uint8_t verHi = 1,
40 std::uint8_t verLo = 0)
43 wire::PutU16(out,
static_cast<std::uint16_t
>(blockType));
50Bytes MakeModuleDiffBlock(std::uint16_t slot,
51 std::uint16_t subslot,
52 std::uint16_t moduleState,
53 std::uint16_t submoduleState)
68 const Bytes hdr = MakeBlockHeader(BlockType::ModuleDifference, body.size() + 2);
71 out.insert(out.end(), body.begin(), body.end());
76Bytes MakeRealIdDataV10(
const std::vector<std::pair<std::uint16_t, std::uint16_t>>& slots)
83 for (
auto [Slot, Subslot] : slots)
95Bytes MakeExpectedSubmoduleDD(std::uint16_t dataDescription,
96 std::uint16_t dataLen)
115class BlocksTest :
public ::testing::Test
127 const Bytes data = MakeBlockHeader(BlockType::ApplicationRelationRequest, 58);
131 EXPECT_EQ(hdr.blockType, BlockType::ApplicationRelationRequest);
132 EXPECT_EQ(hdr.blockLength, 58);
133 EXPECT_EQ(hdr.versionHigh, 1);
134 EXPECT_EQ(hdr.versionLow, 0);
135 EXPECT_EQ(newOffset, 6U);
136 EXPECT_EQ(hdr.BodyLength(), 56);
137 EXPECT_EQ(hdr.TypeName(),
"Application Relation Request");
141TEST_F(BlocksTest, ParseBlockHeaderRejectsTooShortData)
143 const Bytes data = {0x01, 0x01, 0x00};
164TEST_F(BlocksTest, ParseModuleDiffBlockAllOk)
166 const Bytes data = MakeModuleDiffBlock(
174 ASSERT_EQ(mdb.modules.size(), 1);
176 ASSERT_FALSE(mdb.modules[0].submodules.empty());
178 EXPECT_TRUE(mdb.modules[0].IsProper());
179 EXPECT_TRUE(mdb.modules[0].submodules[0].IsOk());
180 EXPECT_TRUE(mdb.AllOk());
181 EXPECT_TRUE(mdb.GetMismatches().empty());
185TEST_F(BlocksTest, ParseModuleDiffBlockWrongSubmodule)
187 const Bytes data = MakeModuleDiffBlock(
195 EXPECT_FALSE(mdb.AllOk());
197 auto mismatches = mdb.GetMismatches();
199 ASSERT_EQ(mismatches.size(), 1);
201 EXPECT_EQ(std::get<0>(mismatches[0]), 1);
203 EXPECT_EQ(std::get<1>(mismatches[0]), 2);
205 EXPECT_EQ(std::get<2>(mismatches[0]),
"WrongSubmodule");
209TEST_F(BlocksTest, ParseModuleDiffBlockRejectsWrongBlockType)
211 Bytes data = MakeBlockHeader(BlockType::ExpectedSubmoduleRequest, 10);
213 data.insert(data.end(), 10, 0);
219TEST_F(BlocksTest, ParseModuleDiffBlockTooShortReturnsEmpty)
221 const Bytes data = {0x01, 0x02};
225 EXPECT_TRUE(mdb.modules.empty());
233TEST_F(BlocksTest, ParseRealIdentificationDataV10)
235 const Bytes data = MakeRealIdDataV10({{0, 1},
241 ASSERT_EQ(result.slots.size(), 2);
243 EXPECT_EQ(result.slots[0].slot, 0);
244 EXPECT_EQ(result.slots[1].slot, 1);
245 EXPECT_EQ(result.slots[0].moduleIdent, 0x42);
246 EXPECT_EQ(result.slots[0].submoduleIdent, 0x99);
250TEST_F(BlocksTest, ParseRealIdentificationDataTooShortReturnsEmpty)
252 const Bytes data = {0x01};
256 EXPECT_TRUE(result.slots.empty());
264TEST_F(BlocksTest, IODWriteMultipleBuilderBuildsValidPacket)
266 std::array<std::uint8_t, uuidLenght> arUuid{};
276 {0x11, 0x22, 0x33, 0x44});
298 ASSERT_GE(packet.size(), 64);
301 ASSERT_GE(packet.size(), 36);
305 const auto outerIdx = r.
U16();
315TEST_F(BlocksTest, ExpectedSubmoduleBlockReqBuildsInputBlock)
332 ASSERT_GE(out.size(), 6U);
334 const auto blockType = r.
U16();
336 EXPECT_EQ(blockType, BlockType::ExpectedSubmoduleRequest);
341 const auto numApis = r.
U16();
343 EXPECT_EQ(numApis, 1U);
347TEST_F(BlocksTest, ExpectedSubmoduleBlockReqInputOutputIncludesTwoDataDescriptions)
371 EXPECT_EQ(out.size(), 42);
375TEST_F(BlocksTest, ExpectedSubmoduleBlockReqNoIoProducesOneDataDescription)
399 EXPECT_EQ(out.size(), 36);
421 ASSERT_TRUE(stats.has_value());
423 EXPECT_EQ(stats->counterStatus, 0x0003);
424 EXPECT_EQ(stats->inOctets, 100000);
425 EXPECT_EQ(stats->outOctets, 200000);
426 EXPECT_EQ(stats->inErrors, 5);
430TEST_F(BlocksTest, ParsePortStatisticsTooShortReturnsNullopt)
432 const Bytes data = {0x00, 0x03, 0x00};
436 EXPECT_FALSE(stats.has_value());
443constexpr std::size_t TestAlign4(
const std::size_t offset)
445 return (offset + 3U) & ~3U;
449void AlignBuffer4(
Bytes& data)
451 data.resize(TestAlign4(data.size()), 0);
474Bytes BuildPdPortData(
475 const std::uint16_t slot,
476 const std::uint16_t subslot,
477 const std::string& portId,
478 const std::vector<PeerInfo>& peers,
479 const std::uint16_t mauType = 0,
480 const std::uint32_t domainBoundary = 0,
481 const std::uint32_t multicastBoundary = 0,
482 const std::uint8_t linkStatePort = 0,
483 const std::uint8_t linkStateLink = 0,
484 const std::uint32_t mediaType = 0)
502 for (
const auto& peer : peers)
546TEST_F(BlocksTest, ParsePdPortDataRealParsesSlotAndSubslot)
548 const Bytes data = BuildPdPortData(
556 EXPECT_EQ(result.slot, 1);
557 EXPECT_EQ(result.subslot, 0x8001);
561TEST_F(BlocksTest, ParsePdPortDataRealDecodesAsciiPortId)
563 const Bytes data = BuildPdPortData(
571 EXPECT_EQ(result.slot, 1);
572 EXPECT_EQ(result.subslot, 0x8001);
573 EXPECT_EQ(result.portId,
"Port");
577TEST_F(BlocksTest, ParsePdPortDataRealDecodesExtendedLatin1PortId)
579 const std::string latin1PortId{
581 static_cast<char>(0xE9),
582 static_cast<char>(0xF6)
585 const Bytes data = BuildPdPortData(
595 "P\xC3\xA9\xC3\xB6");
599TEST_F(BlocksTest, ParsePdPortDataRealParsesPeer)
603 .chassisId =
"Chass1",
608 const Bytes data = BuildPdPortData(
616 ASSERT_EQ(result.peers.size(), 1U);
618 EXPECT_EQ(result.peers[0].portId,
"Peer1");
619 EXPECT_EQ(result.peers[0].chassisId,
"Chass1");
621 const std::array<std::uint8_t, 6> expectedMac{
625 EXPECT_EQ(result.peers[0].macAddress, expectedMac);
629TEST_F(BlocksTest, ParsePdPortDataRealDecodesExtendedLatin1PeerIds)
633 static_cast<char>(0xE9)
635 .chassisId = std::string{
636 static_cast<char>(0xF6)
638 .macAddress = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55}};
640 const Bytes data = BuildPdPortData(
648 ASSERT_EQ(result.peers.size(), 1U);
650 EXPECT_EQ(result.peers[0].portId,
"\xC3\xA9");
651 EXPECT_EQ(result.peers[0].chassisId,
"\xC3\xB6");
655TEST_F(BlocksTest, ParsePdPortDataRealParsesMauType)
657 const Bytes data = BuildPdPortData(
666 EXPECT_EQ(result.mauType, 30);
670TEST_F(BlocksTest, ParsePdPortDataRealParsesPortProperties)
672 const Bytes data = BuildPdPortData(
686 EXPECT_EQ(result.mauType, 30);
687 EXPECT_EQ(result.domainBoundary, 0x12345678U);
688 EXPECT_EQ(result.multicastBoundary, 0x9ABCDEF0U);
689 EXPECT_EQ(result.linkStatePort, 0x01);
690 EXPECT_EQ(result.linkStateLink, 0x02);
691 EXPECT_EQ(result.mediaType, 0x11223344U);
695TEST_F(BlocksTest, ParsePdPortDataRealParsesCompletePortData)
699 .chassisId =
"Chass1",
704 const Bytes data = BuildPdPortData(
718 EXPECT_EQ(result.slot, 1);
719 EXPECT_EQ(result.subslot, 0x8001);
720 EXPECT_EQ(result.portId,
"Port");
722 EXPECT_EQ(result.mauType, 30);
723 EXPECT_EQ(result.domainBoundary, 0x12345678U);
724 EXPECT_EQ(result.multicastBoundary, 0x9ABCDEF0U);
725 EXPECT_EQ(result.linkStatePort, 0x01);
726 EXPECT_EQ(result.linkStateLink, 0x02);
727 EXPECT_EQ(result.mediaType, 0x11223344U);
729 ASSERT_EQ(result.peers.size(), 1U);
731 EXPECT_EQ(result.peers[0].portId,
"Peer1");
732 EXPECT_EQ(result.peers[0].chassisId,
"Chass1");
734 const std::array<std::uint8_t, 6> expectedMac{
738 EXPECT_EQ(result.peers[0].macAddress, expectedMac);
742TEST_F(BlocksTest, ParsePdPortDataRealHandlesEmptyData)
748 EXPECT_EQ(result.slot, 7);
749 EXPECT_EQ(result.subslot, 0x8001);
750 EXPECT_TRUE(result.portId.empty());
751 EXPECT_EQ(result.mauType, 0);
752 EXPECT_EQ(result.linkStatePort, 0);
753 EXPECT_EQ(result.linkStateLink, 0);
754 EXPECT_EQ(result.mediaType, 0);
755 EXPECT_TRUE(result.peers.empty());
756 EXPECT_EQ(result.domainBoundary, 0U);
757 EXPECT_EQ(result.multicastBoundary, 0U);
761TEST_F(BlocksTest, ParsePdPortDataRealHandlesMissingPortIdLength)
769 EXPECT_EQ(result.slot, 1);
770 EXPECT_EQ(result.subslot, 0x8001);
771 EXPECT_TRUE(result.portId.empty());
772 EXPECT_TRUE(result.peers.empty());
776TEST_F(BlocksTest, ParsePdPortDataRealHandlesTruncatedPortId)
789 EXPECT_TRUE(result.portId.empty());
790 EXPECT_TRUE(result.peers.empty());
794TEST_F(BlocksTest, ParsePdPortDataRealHandlesMissingPeerCount)
805 EXPECT_EQ(result.portId,
"P");
806 EXPECT_TRUE(result.peers.empty());
810TEST_F(BlocksTest, DISABLED_ParsePdPortDataRealHandlesTruncatedPeerPortId)
832 ASSERT_EQ(data.size(), 11U);
836 EXPECT_EQ(result.slot, 1);
837 EXPECT_EQ(result.subslot, 0x8001);
838 EXPECT_EQ(result.portId,
"P");
839 EXPECT_TRUE(result.peers.empty());
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).
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.
void SkipTo(std::size_t offset)
Move the read position to an absolute byte offset.
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::size_t Align4(std::size_t offset)
Round an offset up to the next 4-byte boundary.
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.
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 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.
LLDP peer information from PDPortDataReal.
std::string portId
Peer's LLDP port ID.
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.