9#include <gtest/gtest.h>
26Bytes MakeDiagnosisHeader(
const std::uint16_t bodyLength,
const std::uint16_t type = 0x0010)
30 wire::PutU16(data,
static_cast<std::uint16_t
>(bodyLength + 2U));
42Bytes MakeEntry(
const std::uint16_t channel,
const std::uint16_t properties,
43 const std::uint16_t usi,
const std::uint16_t errorType)
60Bytes MakeExtendedEntry(
const std::uint16_t channel,
const std::uint16_t properties,
61 const std::uint16_t errorType,
const std::uint16_t extErrorType,
62 const std::uint32_t extAddValue)
64 Bytes out = MakeEntry(channel, properties,
65 static_cast<std::uint16_t
>(Usi::Bit15AndExternalChannelDiagnosisOfChannelNumber), errorType);
79Bytes MakeQualifiedEntry(
const std::uint16_t channel,
const std::uint16_t properties,
80 const std::uint16_t errorType,
const std::uint16_t extErrorType,
81 const std::uint32_t extAddValue,
const std::uint32_t qualifier)
83 Bytes out = MakeEntry(channel, properties,
84 static_cast<std::uint16_t
>(Usi::Bit15AndQualifiedChannelDiagnosisOfChannelNumber), errorType);
96 Bytes out = MakeDiagnosisHeader(
static_cast<std::uint16_t
>(entries.size()));
97 out.insert(out.end(), entries.begin(), entries.end());
106class DiagnosisMoreTests :
public ::testing::Test
112TEST_F(DiagnosisMoreTests, ChannelTypeNamesCoverAllValues)
133TEST_F(DiagnosisMoreTests, SpecifierNamesCoverAllValues)
137 props.
specifier = ChannelSpecifier::AllDisappears;
140 props.
specifier = ChannelSpecifier::Appears;
143 props.
specifier = ChannelSpecifier::Disappears;
146 props.
specifier = ChannelSpecifier::DisappearsOther;
154TEST_F(DiagnosisMoreTests, DirectionNamesCoverAllValues)
158 props.
direction = ChannelDirection::Manufacturer;
161 props.
direction = ChannelDirection::Input;
164 props.
direction = ChannelDirection::Output;
167 props.
direction = ChannelDirection::Bidirectional;
175TEST_F(DiagnosisMoreTests, ChannelPropertiesFromUint16RetainsRawValue)
179 EXPECT_EQ(props.raw, 0xFFFFU);
180 EXPECT_EQ(props.channelTypeValue, ChannelType::Submodule);
181 EXPECT_EQ(props.accumulative, ChannelAccumulative::No);
182 EXPECT_EQ(props.specifier, ChannelSpecifier::AllDisappears);
183 EXPECT_EQ(props.direction, ChannelDirection::Bidirectional);
187TEST_F(DiagnosisMoreTests, AnyChannelDiagnosisUsiClassification)
190 entry.
usi = Usi::NoIdentifier;
195 entry.
usi = Usi::Bit15OfChannelNumber;
198 entry.
usi = Usi::Bit15AndExternalChannelDiagnosisOfChannelNumber;
202 entry.
usi = Usi::Bit15AndQualifiedChannelDiagnosisOfChannelNumber;
208TEST_F(DiagnosisMoreTests, DiagnosisDataMaintenanceFlagsAreFalseWhenClear)
222TEST_F(DiagnosisMoreTests, DiagnosisDataDetectsMaintenanceDemanded)
235TEST_F(DiagnosisMoreTests, DecodeChannelErrorTypeCoversAdditionalRanges)
243TEST_F(DiagnosisMoreTests, DecodeExtChannelErrorTypeHandlesUnknownValues)
251TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockAcceptsUsiZero)
253 Bytes body = MakeEntry(4, 0, 0, 0x0001);
254 Bytes data = MakeDiagnosisHeader(
static_cast<std::uint16_t
>(body.size()));
255 data.insert(data.end(), body.begin(), body.end());
259 ASSERT_EQ(result.entries.size(), 1U);
260 EXPECT_EQ(result.entries[0].base.api, 7U);
261 EXPECT_EQ(result.entries[0].base.slot, 8U);
262 EXPECT_EQ(result.entries[0].base.subslot, 9U);
263 EXPECT_TRUE(result.entries[0].IsChannelDiagnosis());
267TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockStoresExtendedDiagnosis)
269 Bytes body = MakeExtendedEntry(2, 0, 0x8001, 0x8005, 0x11223344);
270 Bytes data = MakeDiagnosisHeader(
static_cast<std::uint16_t
>(body.size()));
271 data.insert(data.end(), body.begin(), body.end());
275 ASSERT_EQ(result.entries.size(), 1U);
276 ASSERT_TRUE(result.entries[0].ext.has_value());
277 EXPECT_EQ(result.entries[0].ext->extErrorType, 0x8005U);
278 EXPECT_EQ(result.entries[0].ext->extAddValue, 0x11223344U);
279 EXPECT_EQ(result.entries[0].ext->extErrorTypeName,
"No peer detected");
283TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockStoresQualifiedDiagnosis)
285 Bytes body = MakeQualifiedEntry(3, 0, 0x8000, 0x8000, 0x01020304, 0xAABBCCDD);
286 Bytes data = MakeDiagnosisHeader(
static_cast<std::uint16_t
>(body.size()));
287 data.insert(data.end(), body.begin(), body.end());
291 ASSERT_EQ(result.entries.size(), 1U);
292 ASSERT_TRUE(result.entries[0].qualified.has_value());
293 EXPECT_EQ(result.entries[0].qualified->extErrorType, 0x8000U);
294 EXPECT_EQ(result.entries[0].qualified->extAddValue, 0x01020304U);
295 EXPECT_EQ(result.entries[0].qualified->qualifier, 0xAABBCCDDU);
299TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockStopsOnTruncatedEntry)
301 Bytes data = MakeDiagnosisHeader(5);
302 data.insert(data.end(), {0, 1, 0, 0, 0});
306 EXPECT_TRUE(result.entries.empty());
310TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockStopsOnZeroEntry)
312 Bytes data = MakeDiagnosisHeader(8);
313 data.insert(data.end(), 8, 0);
317 EXPECT_TRUE(result.entries.empty());
321TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockAcceptsAlternativeBlockTypes)
323 const std::vector<std::uint16_t> types{0x0010, 0x0011, 0x8010, 0x8011, 0x8012};
325 for (
const auto type : types)
327 Bytes entry = MakeEntry(1, 0, 0x8000, 1);
328 Bytes data = MakeDiagnosisHeader(
static_cast<std::uint16_t
>(entry.size()), type);
329 data.insert(data.end(), entry.begin(), entry.end());
332 ASSERT_EQ(result.entries.size(), 1U) <<
"type=0x" << std::hex << type;
337TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockAcceptsRawEntry)
339 const Bytes data = MakeEntry(1, 0, 0x8000, 1);
343 ASSERT_EQ(result.entries.size(), 1U);
344 EXPECT_EQ(result.entries[0].base.slot, 2U);
345 EXPECT_EQ(result.entries[0].base.subslot, 3U);
349TEST_F(DiagnosisMoreTests, ParseDiagnosisSimpleStopsOnIncompleteEntry)
351 Bytes data = MakeDiagnosisHeader(9);
360 ASSERT_EQ(result.entries.size(), 1U);
361 EXPECT_EQ(result.entries[0].base.channelNumber, 1U);
365TEST_F(DiagnosisMoreTests, ParseDiagnosisSimplePreservesLocation)
367 Bytes data = MakeDiagnosisHeader(12);
377 ASSERT_EQ(result.entries.size(), 1U);
378 EXPECT_EQ(result.entries[0].base.slot, 4U);
379 EXPECT_EQ(result.entries[0].base.subslot, 5U);
382TEST_F(DiagnosisMoreTests, ChannelPropertiesFromUint16CoversAllValidFields)
384 for (std::uint16_t channelType = 0; channelType <= 3; ++channelType)
387 EXPECT_EQ(
static_cast<std::uint8_t
>(props.channelTypeValue), channelType);
390 for (std::uint16_t accumulative = 0; accumulative <= 2; ++accumulative)
394 EXPECT_EQ(
static_cast<std::uint8_t
>(props.accumulative), accumulative);
398 static_cast<std::uint16_t
>((1U << 5U) | (1U << 6U)));
400 EXPECT_TRUE(maintenance.maintenanceRequired);
401 EXPECT_TRUE(maintenance.naintenanceDemanded);
403 for (std::uint16_t specifier = 0; specifier <= 3; ++specifier)
407 EXPECT_EQ(
static_cast<std::uint8_t
>(props.specifier), specifier);
410 for (std::uint16_t direction = 0; direction <= 3; ++direction)
414 EXPECT_EQ(
static_cast<std::uint8_t
>(props.direction), direction);
419TEST_F(DiagnosisMoreTests, ChannelPropertiesFromUint16UsesFallbacks)
422 EXPECT_EQ(invalidAccumulative.accumulative, ChannelAccumulative::No);
425 EXPECT_EQ(invalidSpecifier.specifier, ChannelSpecifier::AllDisappears);
429TEST_F(DiagnosisMoreTests, ChannelDiagnosisDetectsSubmoduleLevel)
433 diagnosis.
channelNumber =
static_cast<std::uint16_t
>(Usi::Bit15OfChannelNumber);
441TEST_F(DiagnosisMoreTests, DiagnosisDataGetByChannelFiltersAllMatchingEntries)
454 data.
entries = {first, second, third};
458 ASSERT_EQ(matches.size(), 2U);
459 EXPECT_EQ(matches[0], &data.
entries[0]);
460 EXPECT_EQ(matches[1], &data.
entries[2]);
466TEST_F(DiagnosisMoreTests, DiagnosisDataHasErrorsReflectsEntries)
478TEST_F(DiagnosisMoreTests, DecodeChannelErrorTypeCoversAllFallbackRanges)
502TEST_F(DiagnosisMoreTests, DecodeExtChannelErrorTypeCoversFallbackRanges)
505 "Manufacturer-specific (0x0001)");
508 "Reserved (0x8001)");
511 "Profile-specific (0x9000)");
521TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockConsumesLocationHeader)
529 const Bytes entry = MakeEntry(5, 0, 0x8000, 0x0001);
530 data.insert(data.end(), entry.begin(), entry.end());
534 ASSERT_EQ(result.entries.size(), 1U);
535 EXPECT_EQ(result.entries[0].base.api, 0x1234U);
536 EXPECT_EQ(result.entries[0].base.slot, 7U);
537 EXPECT_EQ(result.entries[0].base.subslot, 0x8001U);
541TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockHandlesUnknownUsi)
543 const Bytes data = MakeEntry(4, 0, 0x1234, 0x0009);
547 ASSERT_EQ(result.entries.size(), 1U);
548 EXPECT_EQ(result.entries[0].usi, 0x1234U);
549 EXPECT_EQ(result.entries[0].base.errorType, 0x0009U);
550 EXPECT_EQ(result.entries[0].base.errorTypeName,
"Error");
554TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockStopsOnTruncatedExtendedDiagnosis)
556 Bytes data = MakeDiagnosisHeader(10);
561 static_cast<std::uint16_t
>(Usi::Bit15AndExternalChannelDiagnosisOfChannelNumber));
567 EXPECT_TRUE(result.entries.empty());
571TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockStopsOnTruncatedQualifiedDiagnosis)
573 Bytes data = MakeDiagnosisHeader(14);
578 static_cast<std::uint16_t
>(Usi::Bit15AndQualifiedChannelDiagnosisOfChannelNumber));
586 EXPECT_TRUE(result.entries.empty());
590TEST_F(DiagnosisMoreTests, ParseDiagnosisBlockRejectsTooShortInput)
597TEST_F(DiagnosisMoreTests, ParseDiagnosisSimpleRejectsTooShortInput)
PROFINET diagnosis data structures, error decoding, and parsers.
std::string DecodeChannelErrorType(std::uint16_t errorType)
Decode a ChannelErrorType to a human-readable string.
ChannelDirection
Data direction, packed into ChannelProperties bits 11-12.
DiagnosisData ParseDiagnosisSimple(const Bytes &data, std::uint32_t api=0, std::uint16_t slot=0, std::uint16_t subslot=0)
Parse a simple, fixed-format diagnosis block.
DiagnosisData ParseDiagnosisBlock(const Bytes &data, std::uint32_t api=0, std::uint16_t slot=0, std::uint16_t subslot=0)
Parse a full DiagnosisData block from raw bytes.
ChannelType
Channel type, packed into ChannelProperties bits 0-1.
ChannelSpecifier
Diagnosis specifier, packed into ChannelProperties bits 8-10.
std::string DecodeExtChannelErrorType(std::uint16_t channelErrorType, std::uint16_t extErrorType)
Decode an ExtChannelErrorType given its ChannelErrorType context.
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.
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Any diagnosis entry (USI 0x8000, 0x8002, or 0x8003).
bool IsExtChannelDiagnosis() const
Whether this is the ExtChannelDiagnosis variant.
bool IsQualifiedChannelDiagnosis() const
Whether this is the QualifiedChannelDiagnosis variant.
Usi usi
The USI that determined which entry type was parsed.
bool IsChannelDiagnosis() const
Whether this is the plain ChannelDiagnosis variant.
ChannelDiagnosis base
Common fields, always populated.
ChannelDiagnosis entry (USI 0x8000).
std::uint16_t channelNumber
Channel number, with the accumulative bit packed into bit 15.
bool IsSubmoduleLevel() const
Whether this diagnosis applies to the whole submodule.
ChannelProperties properties
Decoded channel properties.
Parsed ChannelProperties word (16 bits).
std::string SpecifierName() const
Human-readable name for Specifier.
std::string DirectionName() const
Human-readable name for Direction.
bool maintenanceRequired
Maintenance required flag (bit 5).
bool naintenanceDemanded
Maintenance demanded flag (bit 6).
std::string ChannelTypeName() const
Human-readable name for ChannelTypeValue.
ChannelType channelTypeValue
Channel type (bits 0-1).
ChannelDirection direction
Data direction (bits 11-12).
ChannelSpecifier specifier
Diagnosis specifier (bits 8-10).
static ChannelProperties FromUint16(std::uint16_t value)
Decode a raw 16-bit ChannelProperties value.
Complete diagnosis data for one slot/subslot.
bool HasMaintenanceRequired() const
Whether any entry has its maintenance-required flag set.
bool HasMaintenanceDemanded() const
Whether any entry has its maintenance-demanded flag set.
std::vector< AnyChannelDiagnosis > entries
Parsed diagnosis entries.
std::vector< const AnyChannelDiagnosis * > GetByChannel(std::uint16_t channel) const
Find every entry for a specific channel number.
bool HasErrors() const
Whether any diagnosis entries were found.
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.