35std::string Latin1Decode(
const std::uint8_t* data, std::size_t len)
39 for (std::size_t i = 0; i < len; ++i)
41 const std::uint8_t b = data[i];
44 out.push_back(
static_cast<char>(b));
48 out.push_back(
static_cast<char>(0xC0 | (b >> 6)));
49 out.push_back(
static_cast<char>(0x80 | (b & 0x3F)));
55const std::map<std::uint16_t, std::string>& MauTypeNames()
57 static const std::map<std::uint16_t, std::string> table = {
61 {15,
"100BASE-TX HD"},
62 {16,
"100BASE-TX FD"},
63 {17,
"100BASE-FX HD"},
64 {18,
"100BASE-FX FD"},
65 {29,
"1000BASE-T HD"},
66 {30,
"1000BASE-T FD"},
67 {21,
"1000BASE-X HD"},
68 {22,
"1000BASE-X FD"},
84 return std::format(
"SlotInfo(api={}, slot={}, subslot=0x{:04X})",
api,
slot,
subslot);
89 auto it = MauTypeNames().find(
mauType);
90 if (it != MauTypeNames().end())
94 return "Unknown(" + std::to_string(
mauType) +
")";
118 throw std::invalid_argument(
"Block header requires 6 bytes, got " +
119 std::to_string(data.size() >= offset ? data.size() - offset : 0));
133 if (data.size() < offset + 10)
135 throw std::invalid_argument(
"MultipleBlockHeader body requires 8 bytes after padding");
139 const std::uint32_t api = r.
U32();
140 const std::uint16_t slot = r.
U16();
141 const std::uint16_t subslot = r.
U16();
142 return {api, slot, subslot, offset + 10};
147 std::size_t start = offset;
149 auto alignFromBlock = [&](std::size_t bodyOffset) -> std::size_t
151 const std::size_t blockOffset = blockHeaderSize + (bodyOffset - start);
152 const std::size_t alignedBlock =
Align4(blockOffset);
153 return start + (alignedBlock - blockHeaderSize);
156 if (data.size() <= offset)
158 throw std::invalid_argument(
"Truncated interface data (missing chassis length)");
160 const std::uint8_t chassisLen = data[offset];
161 offset +=
sizeof(std::uint8_t);
163 if (data.size() < offset + chassisLen)
165 throw std::invalid_argument(
"Truncated chassis ID");
167 const std::string chassisId = Latin1Decode(&data[offset], chassisLen);
168 offset += chassisLen;
170 offset = alignFromBlock(offset);
174 throw std::invalid_argument(
"Truncated MAC address");
181 offset = alignFromBlock(offset);
183 if (data.size() < offset + 12)
185 throw std::invalid_argument(
"Truncated IP configuration");
187 std::ranges::copy(data | std::views::drop(offset) | std::views::take(4), info.
ipAddress.begin());
190 std::ranges::copy(data | std::views::drop(offset) | std::views::take(4), info.
subnetMask.begin());
193 std::ranges::copy(data | std::views::drop(offset) | std::views::take(4), info.
gateway.begin());
201 const std::size_t start = offset;
206 if (data.size() >= offset + (2 *
sizeof(std::uint16_t)))
210 offset += 2 *
sizeof(std::uint16_t);
213 if (data.size() < offset + 1)
215 return PortInfo{.
slot = slot, .subslot = subslot, .portId =
"", .mauType = 0, .linkStatePort = 0, .linkStateLink = 0, .mediaType = 0, .peers = {}, .domainBoundary = 0, .multicastBoundary = 0};
218 const std::uint8_t portIdLen = data[offset];
219 offset +=
sizeof(std::uint8_t);
222 if (data.size() >= offset + portIdLen)
224 portId = Latin1Decode(&data[offset], portIdLen);
228 std::uint8_t numPeers = 0;
229 std::vector<PeerInfo> peers;
230 if (data.size() > offset)
232 numPeers = data[offset];
233 offset +=
sizeof(std::uint8_t);
236 offset = start +
Align4(offset - start);
238 for (std::uint8_t i = 0; i < numPeers; ++i)
240 if (data.size() < offset + 1)
245 const std::uint8_t peerPortLen = data[offset];
246 offset +=
sizeof(std::uint8_t);
247 std::string peerPortId;
248 if (data.size() >= offset + peerPortLen)
250 peerPortId = Latin1Decode(&data[offset], peerPortLen);
251 offset += peerPortLen;
254 if (data.size() < offset + 1)
258 const std::uint8_t peerChassisLen = data[offset];
259 offset +=
sizeof(std::uint8_t);
260 std::string peerChassisId;
261 if (data.size() >= offset + peerChassisLen)
263 peerChassisId = Latin1Decode(&data[offset], peerChassisLen);
264 offset += peerChassisLen;
267 offset = start +
Align4(offset - start);
269 std::array<std::uint8_t, macAddressLength> peerMac{};
272 std::ranges::copy(data.begin() + offset, data.begin() + offset +
macAddressLength, peerMac.begin());
276 offset = start +
Align4(offset - start);
278 peers.push_back({.portId = peerPortId, .chassisId = peerChassisId, .macAddress = peerMac});
281 std::uint16_t mauType = 0;
282 if (data.size() >= offset + 2)
286 offset +=
sizeof(std::uint16_t);
289 offset = start +
Align4(offset - start);
291 std::uint32_t domainBoundary = 0;
292 std::uint32_t multicastBoundary = 0;
293 if (data.size() >= offset + (2 *
sizeof(std::uint32_t)))
297 domainBoundary = r.
U32();
298 offset +=
sizeof(std::uint32_t);
300 multicastBoundary = r.
U32();
301 offset +=
sizeof(std::uint32_t);
304 std::uint8_t linkStatePort = 0;
305 std::uint8_t linkStateLink = 0;
306 if (data.size() >= offset + (2 *
sizeof(std::uint8_t)))
308 linkStatePort = data[offset];
309 offset +=
sizeof(std::uint8_t);
311 linkStateLink = data[offset];
312 offset +=
sizeof(std::uint8_t);
315 offset = start +
Align4(offset - start);
317 std::uint32_t mediaType = 0;
318 if (data.size() >= offset +
sizeof(std::uint32_t))
325 return PortInfo{.
slot = slot, .subslot = subslot, .portId = portId, .mauType = mauType, .linkStatePort = linkStatePort, .linkStateLink = linkStateLink, .mediaType = mediaType, .peers = peers, .domainBoundary = domainBoundary, .multicastBoundary = multicastBoundary};
331 std::size_t offset = 0;
336 std::size_t newOffset = 0;
341 catch (
const std::invalid_argument&)
346 std::size_t blockEnd = newOffset + header.
BodyLength();
347 if (blockEnd > data.size())
349 blockEnd = data.size();
360 slotInfo.
slot = slotNr;
366 std::size_t nestedBody = 0;
371 catch (
const std::invalid_argument&)
376 std::size_t nestedEnd = nestedBody + nestedHeader.
BodyLength();
377 if (nestedEnd > blockEnd)
379 nestedEnd = blockEnd;
389 catch (
const std::invalid_argument&)
399 catch (
const std::invalid_argument&)
404 nestedOffset = nestedEnd;
407 result.
slots.push_back(slotInfo);
408 result.
rawBlocks.emplace_back(api, slotNr, subslotNr,
409 Bytes(data.begin() + newOffset, data.begin() + blockEnd));
411 catch (
const std::invalid_argument&)
425 std::size_t offset = 0;
448 catch (
const std::invalid_argument&)
455 if (data.size() < offset + 2)
460 wire::Reader rOuter(data.data() + offset, data.size() - offset);
462 const bool v11 = result.
version.first >= 1 && result.
version.second >= 1;
466 const std::uint16_t numApis = rOuter.
U16();
467 for (std::uint16_t i = 0; i < numApis; ++i)
469 if (rOuter.
Remaining() <
sizeof(std::uint32_t) +
sizeof(std::uint16_t))
473 const std::uint32_t api = rOuter.
U32();
474 const std::uint16_t numSlots = rOuter.
U16();
476 for (std::uint16_t s = 0; s < numSlots; ++s)
478 if (rOuter.
Remaining() <
sizeof(std::uint16_t) +
sizeof(std::uint32_t) +
sizeof(std::uint16_t))
482 const std::uint16_t slotNr = rOuter.
U16();
483 const std::uint32_t moduleIdent = rOuter.
U32();
484 const std::uint16_t numSubslots = rOuter.
U16();
486 for (std::uint16_t ss = 0; ss < numSubslots; ++ss)
488 if (rOuter.
Remaining() <
sizeof(std::uint16_t) +
sizeof(std::uint32_t))
492 const std::uint16_t subslotNr = rOuter.
U16();
493 const std::uint32_t submoduleIdent = rOuter.
U32();
495 result.
slots.push_back({.slot = slotNr, .subslot = subslotNr, .api = api, .moduleIdent = moduleIdent, .submoduleIdent = submoduleIdent, .blocks = {}});
502 const std::uint16_t numSlots = rOuter.
U16();
503 for (std::uint16_t s = 0; s < numSlots; ++s)
505 if (rOuter.
Remaining() <
sizeof(std::uint16_t) +
sizeof(std::uint32_t) +
sizeof(std::uint16_t))
509 const std::uint16_t slotNr = rOuter.
U16();
510 const std::uint32_t moduleIdent = rOuter.
U32();
511 const std::uint16_t numSubslots = rOuter.
U16();
513 for (std::uint16_t ss = 0; ss < numSubslots; ++ss)
515 if (rOuter.
Remaining() <
sizeof(std::uint16_t) +
sizeof(std::uint32_t))
519 const std::uint16_t subslotNr = rOuter.
U16();
520 const std::uint32_t submoduleIdent = rOuter.
U32();
521 result.
slots.push_back({.slot = slotNr, .subslot = subslotNr, .api = 0, .moduleIdent = moduleIdent, .submoduleIdent = submoduleIdent, .blocks = {}});
531 if (data.size() < offset + 26)
572 for (
const auto& mod :
modules)
578 for (
const auto& sub : mod.submodules)
591 std::vector<std::tuple<std::uint16_t, std::uint16_t, std::string>> mismatches;
592 for (
const auto& mod :
modules)
596 mismatches.emplace_back(mod.slotNumber, 0, mod.StateName());
598 for (
const auto& sub : mod.submodules)
602 mismatches.emplace_back(mod.slotNumber, sub.subslotNumber, sub.StateName());
623 const std::string message = std::format(
"Expected block type {} (0x8104), got {} (0x{:04X})",
626 static_cast<std::uint16_t
>(blockType));
628 throw std::invalid_argument(message);
635 const std::uint16_t numApis = r.
U16();
637 std::vector<ModuleDiffModule> modules;
639 for (std::uint16_t i = 0; i < numApis; ++i)
641 if (r.
Remaining() <
sizeof(std::uint32_t) +
sizeof(std::uint16_t))
645 const std::uint32_t api = r.
U32();
646 const std::uint16_t numModules = r.
U16();
648 for (std::uint16_t m = 0; m < numModules; ++m)
650 if (r.
Remaining() <
sizeof(std::uint16_t) +
sizeof(std::uint32_t) + (2 *
sizeof(std::uint16_t)))
654 const std::uint16_t slotNr = r.
U16();
655 const std::uint32_t moduleIdent = r.
U32();
656 const std::uint16_t moduleState = r.
U16();
657 const std::uint16_t numSubmodules = r.
U16();
659 std::vector<ModuleDiffSubmodule> submodules;
660 for (std::uint16_t s = 0; s < numSubmodules; ++s)
662 if (r.
Remaining() <
sizeof(std::uint16_t) +
sizeof(std::uint32_t) +
sizeof(std::uint16_t))
666 const std::uint16_t subslotNr = r.
U16();
667 const std::uint32_t submoduleIdent = r.
U32();
668 const std::uint16_t submoduleState = r.
U16();
669 submodules.push_back({.subslotNumber = subslotNr, .submoduleIdentNumber = submoduleIdent, .submoduleState = submoduleState});
672 modules.push_back({.api = api, .slotNumber = slotNr, .moduleIdentNumber = moduleIdent, .moduleState = moduleState, .submodules = submodules});
684 std::uint16_t index,
Bytes data, std::uint32_t api)
686 writes.push_back({.api = api, .slot = slot, .subslot = subslot, .index = index, .data = std::move(data)});
691 std::uint16_t subslot, std::uint16_t index,
692 const Bytes& data)
const
706 wire::PutU32(out,
static_cast<std::uint32_t
>(data.size()));
707 Bytes padding(24, 0);
708 out.insert(out.end(), padding.begin(), padding.end());
709 out.insert(out.end(), data.begin(), data.end());
727 wire::PutU32(out,
static_cast<std::uint32_t
>(blocksLen));
728 Bytes padding(24, 0);
729 out.insert(out.end(), padding.begin(), padding.end());
737 for (std::size_t i = 0; i <
writes.size(); ++i)
739 const auto& w =
writes[i];
740 Bytes block =
BuildWriteBlock(
static_cast<std::uint16_t
>(i), w.api, w.slot, w.subslot, w.index, w.data);
741 blocksData.insert(blocksData.end(), block.begin(), block.end());
743 if (i + 1 <
writes.size())
745 std::size_t padLen = (4 - (block.size() % 4)) % 4;
746 blocksData.insert(blocksData.end(), padLen);
752 out.insert(out.end(), blocksData.begin(), blocksData.end());
758 std::vector<WriteMultipleResult> results;
759 if (data.size() < 64)
764 constexpr std::uint16_t recordLenOffset = 36;
765 r.
SkipTo(recordLenOffset);
767 const std::uint32_t recordLen = r.
U32();
769 std::size_t offset = 64;
770 const std::size_t end = std::min<std::size_t>(offset + recordLen, data.size());
772 while (offset + 56 <= end)
775 const std::uint16_t blockType = r.
U16();
776 const std::uint16_t blockLen = r.
U16();
778 const std::uint16_t seqNum = r.
U16();
780 const std::uint32_t api = r.
U32();
781 const std::uint16_t slot = r.
U16();
782 const std::uint16_t subslot = r.
U16();
784 const std::uint16_t index = r.
U16();
786 const std::uint16_t addVal1 = r.
U16();
787 const std::uint16_t addVal2 = r.
U16();
788 const std::uint32_t status = r.
U32();
795 results.push_back({.seqNum = seqNum, .api = api, .slot = slot, .subslot = subslot, .index = index, .status = status, .additionalValue1 = addVal1, .additionalValue2 = addVal2});
797 const std::size_t blockSize = 4 + blockLen;
798 const std::size_t pad = (4 - (blockSize % 4)) % 4;
799 offset += blockSize + pad;
827 Bytes d = dd.ToBytes();
828 out.insert(out.end(), d.begin(), d.end());
841 if (
submodules.size() > std::numeric_limits<std::uint16_t>::max())
843 throw std::invalid_argument(
"ExpectedSubmoduleAPI: too many submodules");
849 Bytes s = sm.ToBytes();
850 out.insert(out.end(), s.begin(), s.end());
863 if (
slots.size() > std::numeric_limits<std::uint16_t>::max())
865 throw std::invalid_argument(
"ExpectedSubmoduleAPI: too many modules");
870 for (
const auto& slot :
slots)
872 Bytes s = slot.ToBytes();
873 out.insert(out.end(), s.begin(), s.end());
879 std::uint16_t subslot,
880 std::uint32_t moduleIdent,
881 std::uint32_t submoduleIdent,
882 std::uint16_t submoduleType,
883 std::uint16_t inputLength,
884 std::uint16_t outputLength)
895 if (apiEntry ==
nullptr)
899 apis.push_back(newApi);
900 apiEntry = &
apis.back();
904 auto it = std::ranges::find_if(apiEntry->
slots, [slot](
const auto& s)
906 return s.slotNumber == slot;
909 auto* slotEntry = (it != apiEntry->
slots.end()) ? &(*it) :
nullptr;
910 if (slotEntry ==
nullptr)
915 .moduleIdentNumber = moduleIdent,
916 .moduleProperties = 0,
918 slotEntry = &apiEntry->
slots.back();
921 std::vector<ExpectedSubmoduleDataDescription> dds;
922 switch (submoduleType)
926 dds.push_back({.dataDescription = 1, .submoduleDataLength = 0, .lengthIocs = 1, .lengthIops = 1});
930 dds.push_back({.dataDescription = 1, .submoduleDataLength = inputLength, .lengthIocs = 1, .lengthIops = 1});
933 dds.push_back({.dataDescription = 2, .submoduleDataLength = outputLength, .lengthIocs = 1, .lengthIops = 1});
936 dds.push_back({.dataDescription = 1, .submoduleDataLength = inputLength, .lengthIocs = 1, .lengthIops = 1});
937 dds.push_back({.dataDescription = 2, .submoduleDataLength = outputLength, .lengthIocs = 1, .lengthIops = 1});
948 slotEntry->submodules.push_back(std::move(sm));
960 if (
apis.size() > std::numeric_limits<std::uint16_t>::max())
962 throw std::invalid_argument(
"ExpectedSubmoduleBlockReq: too many APIs");
968 for (
const auto& api :
apis)
970 wire::PutU16(body,
static_cast<std::uint16_t
>(api.slots.size()));
971 Bytes a = api.ToBytes();
972 body.insert(body.end(), a.begin(), a.end());
975 auto blockLen =
static_cast<std::uint16_t
>(body.size() + 2);
976 if (blockLen > std::numeric_limits<std::uint16_t>::max())
978 throw std::invalid_argument(
"ExpectedSubmoduleBlockReq: block too large");
988 out.insert(out.end(), body.begin(), body.end());
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.
std::vector< ExpectedSubmoduleAPI > apis
APIs added so far, each with its own module/submodule list.
static constexpr std::uint16_t expectedSubmoduleBlockReqBlockType
Block type identifier for ExpectedSubmoduleBlockReq.
Builder for IODWriteMultipleReq packets (index 0xE040).
Bytes BuildHeader(std::size_t blocksLen) const
Serialize the outer IODWriteMultipleReq header.
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.
static constexpr std::uint16_t writeMultipleIndex
Record data index for WriteMultiple (0xE040).
std::vector< Write > writes
Queued write operations, in the order they'll be serialized.
Bytes BuildWriteBlock(std::uint16_t seq, std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint16_t index, const Bytes &data) const
Serialize a single nested write block.
std::uint16_t seqNum
Starting sequence number for the outer header.
std::array< std::uint8_t, uuidLenght > arUuid
AR UUID the writes belong to.
Lightweight cursor for parsing a big-endian byte buffer without copies.
std::uint8_t U8()
Read and consume one byte.
std::uint32_t U32()
Read and consume a big-endian 32-bit value.
std::size_t Remaining() const
Number of bytes not yet consumed.
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.
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.
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.
@ RealIdentificationDataApi
Real Identification Data API block.
@ ModuleDifference
Module difference block.
@ PortDataReal
PD Port Data Real block.
@ RealIdentificationData
Real identification data block.
@ PortDataInterfaceDataReal
PD Interface Data Real block.
@ MultipleHeader
Multiple Header block.
constexpr int uuidLenght
Constant lenght of a UUID.
std::string GetSubmoduleStateName(std::uint16_t state)
Get the human-readable name for a submodule state.
constexpr int blockHeaderLength
Constant lenght of a blockHeader.
std::string GetModuleStateName(std::uint16_t state)
Get the human-readable name for a module state.
std::string GetBlockTypeName(BlockType blockType)
Get the human-readable name for a block type.
constexpr std::uint16_t MODULE_STATE_PROPER_MODULE
Module State Proper Module.
constexpr int macAddressLength
Constant lenght of a mac address.
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.
static constexpr std::uint16_t BLOCK_TYPE
Block type identifier for IODWriteResHeader.
Expected submodules for a specific API/slot.
std::vector< ExpectedSubmoduleSlot > slots
Expected slots within this module.
Bytes ToBytes() const
Serialize this API entry to raw bytes.
std::uint32_t api
API number.
std::uint16_t submoduleDataLength
Length in bytes of this direction's cyclic data.
std::uint8_t lengthIops
Length in bytes of the IOPS (provider status) trailer.
std::uint8_t lengthIocs
Length in bytes of the IOCS (consumer status) trailer.
std::uint16_t dataDescription
Data direction: 1 = Input, 2 = Output.
Bytes ToBytes() const
Serialize this description to raw bytes.
One slot with in an api submodule.
std::uint16_t slotNumber
Slot number.
std::uint32_t moduleIdentNumber
Expected module ident number.
std::vector< ExpectedSubmodule > submodules
Expected submodules within this module.
std::uint32_t api
API number.
Bytes ToBytes() const
Serialize this api submodule slot.
std::uint16_t moduleProperties
Module property flags.
Expected submodule within a slot.
std::vector< ExpectedSubmoduleDataDescription > dataDescriptions
Data descriptions for this submodule's cyclic data (one per active direction).
Bytes ToBytes() const
Serialize this submodule entry to raw bytes.
std::uint16_t subslotNumber
Subslot number.
std::uint16_t submoduleProperties
Submodule property flags (encodes the I/O direction type).
std::uint32_t submoduleIdentNumber
Expected submodule ident number.
Interface information from PDInterfaceDataReal (0x0240).
std::array< std::uint8_t, 4 > gateway
Interface IPv4 default gateway.
std::string chassisId
LLDP chassis ID.
std::array< std::uint8_t, 4 > subnetMask
Interface IPv4 subnet mask.
std::array< std::uint8_t, macAddressLength > macAddress
Interface MAC address.
std::array< std::uint8_t, 4 > ipAddress
Interface IPv4 address.
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.
std::uint16_t moduleState
Module state (see MODULE_STATE_* constants).
bool IsProper() const
Whether this module matches the expected configuration.
std::string StateName() const
Human-readable name for ModuleState.
std::string StateName() const
Human-readable name for SubmoduleState.
bool IsOk() const
Whether this submodule matches the expected configuration.
std::uint16_t submoduleState
Submodule state (see SUBMODULE_STATE_* constants).
Parsed PDRealData (0xF841) structure.
std::optional< InterfaceInfo > interface
Interface information, if a PDInterfaceDataReal block was found.
std::vector< SlotInfo > slots
Slots discovered in the response.
std::vector< PortInfo > ports
Port information for each port found.
std::vector< std::tuple< std::uint32_t, std::uint16_t, std::uint16_t, Bytes > > rawBlocks
Raw (api, slot, subslot, MultipleBlockHeader payload) tuples, one per slot.
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::uint16_t slot
Slot number.
std::string LinkState() const
Human-readable name for LinkStateLink.
std::uint8_t linkStateLink
Link-level link state.
Parsed PDPortStatistic (0x0251) counters.
std::uint32_t outDiscards
Transmitted discarded frame count.
std::uint32_t outOctets
Transmitted octet count.
std::uint32_t inErrors
Received error frame count.
std::uint32_t inDiscards
Received discarded frame count.
std::uint32_t inOctets
Received octet count.
std::uint32_t outErrors
Transmitted error frame count.
std::uint16_t counterStatus
Counter validity status.
Parsed RealIdentificationData (0xF000/0x0013) structure.
std::vector< SlotInfo > slots
Populated slots/subslots.
std::pair< std::uint8_t, std::uint8_t > version
Block format version (determines v1.0 vs v1.1 layout).
Slot/subslot discovered from a device.
std::vector< std::string > blocks
Names of nested blocks found for this slot (e.g. "PDPortDataReal").
std::string ToString() const
Human-readable summary of this slot.
std::uint32_t api
API number.
std::uint16_t slot
Slot number.
std::uint16_t subslot
Subslot number.
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.