PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_diagnosis.cpp
Go to the documentation of this file.
1
18
19#include <gtest/gtest.h>
20
21#include "profinet/diagnosis.h"
22#include "profinet/wire.h"
23
24using namespace profinet;
25using namespace profinet::diagnosis;
26
27class DiagnosisTest : public ::testing::Test
28{
29};
30
32TEST_F(DiagnosisTest, ChannelPropertiesFromUint16DecodesAllBitFields)
33{
34 // channel_type=Specific(1) | accumulative=MainFault(1)<<2 |
35 // maintenance_required(1)<<5 | maintenance_demanded(1)<<6 |
36 // specifier=Appears(1)<<8 | direction=Input(1)<<11
37 const std::uint16_t raw =
38 0x01 |
39 (0x01 << 2) |
40 (1 << 5) |
41 (1 << 6) |
42 (0x01 << 8) |
43 (0x01 << 11);
44
45 auto props = ChannelProperties::FromUint16(raw);
46
47 EXPECT_EQ(props.channelTypeValue, ChannelType::Specific);
48 EXPECT_EQ(props.accumulative, ChannelAccumulative::MainFault);
49 EXPECT_TRUE(props.maintenanceRequired);
50 EXPECT_TRUE(props.naintenanceDemanded);
51 EXPECT_EQ(props.specifier, ChannelSpecifier::Appears);
52 EXPECT_EQ(props.direction, ChannelDirection::Input);
53}
54
56TEST_F(DiagnosisTest, ChannelPropertiesFromUint16HandlesZeroValue)
57{
58 auto props = ChannelProperties::FromUint16(0);
59
60 EXPECT_EQ(props.channelTypeValue, ChannelType::Reserved);
61 EXPECT_FALSE(props.maintenanceRequired);
62 EXPECT_FALSE(props.naintenanceDemanded);
63}
64
66TEST_F(DiagnosisTest, DecodeChannelErrorTypeResolvesKnownAndRangeBasedFallbacks)
67{
68 EXPECT_EQ(
70 "Short circuit");
71
72 EXPECT_EQ(
74 "Data transmission impossible");
75
76 EXPECT_EQ(
78 "IO-Link device event");
79
80 EXPECT_EQ(
82 "Reserved (0x0050)");
83
84 EXPECT_EQ(
86 "Manufacturer-specific (0x0200)");
87
88 EXPECT_EQ(
90 "Profile-specific (0x9100)");
91}
92
94TEST_F(DiagnosisTest, DecodeExtChannelErrorTypeUsesContextSpecificTables)
95{
96 // 0x8000 (Data transmission impossible) + ext 0x8000 -> link state,
97 // NOT "Accumulative info".
98 EXPECT_EQ(
99 DecodeExtChannelErrorType(0x8000, 0x8000),
100 "Link state mismatch - Loss of link");
101
102 // 0x8001 (Remote mismatch) + ext 0x8005 -> "No peer detected".
103 EXPECT_EQ(
104 DecodeExtChannelErrorType(0x8001, 0x8005),
105 "No peer detected");
106
107 // Error type with no dedicated table falls back to general table.
108 EXPECT_EQ(
109 DecodeExtChannelErrorType(0x0001, 0x8000),
110 "Accumulative info");
111
112 EXPECT_EQ(
113 DecodeExtChannelErrorType(0x0001, 0x1234),
114 "Manufacturer-specific (0x1234)");
115}
116
117namespace
118{
119
120// Build a ChannelDiagnosis-only entry (USI 0x8000):
121// ChannelNumber(2) + ChannelProperties(2) + USI(2) +
122// ChannelErrorType(2) = 8 bytes.
123Bytes MakeChannelDiagEntry(
124 std::uint16_t channel,
125 std::uint16_t props,
126 std::uint16_t errorType)
127{
128 Bytes out;
129
130 wire::PutU16(out, channel);
131 wire::PutU16(out, props);
132 wire::PutU16(out, 0x8000); // USI
133 wire::PutU16(out, errorType);
134
135 return out;
136}
137
138Bytes MakeExtChannelDiagEntry(
139 std::uint16_t channel,
140 std::uint16_t props,
141 std::uint16_t errorType,
142 std::uint16_t extErrorType,
143 std::uint32_t extAddValue)
144{
145 Bytes out;
146
147 wire::PutU16(out, channel);
148 wire::PutU16(out, props);
149 wire::PutU16(out, 0x8002); // USI
150 wire::PutU16(out, errorType);
151 wire::PutU16(out, extErrorType);
152 wire::PutU32(out, extAddValue);
153
154 return out;
155}
156
157} // namespace
158
160TEST_F(DiagnosisTest, ParseDiagnosisBlockParsesSingleChannelDiagnosisEntry)
161{
162 Bytes data;
163
164 // 6-byte diagnosis block header (type=0x0010)
165 wire::PutU16(data, 0x0010);
166 wire::PutU16(data, 14); // block_length
167 wire::PutU8(data, 1);
168 wire::PutU8(data, 0);
169
170 // Entry
171 Bytes entry =
172 MakeChannelDiagEntry(0x0001, 0x0000, 0x0006); // "Line break"
173
174 data.insert(data.end(), entry.begin(), entry.end());
175
176 auto result = ParseDiagnosisBlock(
177 data,
178 /*api=*/0,
179 /*slot=*/1,
180 /*subslot=*/1);
181
182 ASSERT_EQ(result.entries.size(), 1U);
183
184 EXPECT_TRUE(result.HasErrors());
185 EXPECT_EQ(result.entries[0].base.channelNumber, 0x0001);
186 EXPECT_EQ(result.entries[0].base.errorType, 0x0006);
187 EXPECT_EQ(result.entries[0].base.errorTypeName, "Line break");
188 EXPECT_TRUE(result.entries[0].IsChannelDiagnosis());
189}
190
192TEST_F(DiagnosisTest, ParseDiagnosisBlockParsesExtChannelDiagnosisEntry)
193{
194 Bytes data;
195
196 wire::PutU16(data, 0x0010);
197 wire::PutU16(data, 18);
198 wire::PutU8(data, 1);
199 wire::PutU8(data, 0);
200
201 Bytes entry =
202 MakeExtChannelDiagEntry(
203 0x0002,
204 0x0000,
205 0x8001,
206 0x8005,
207 0); // Remote mismatch / No peer
208
209 data.insert(data.end(), entry.begin(), entry.end());
210
211 auto result = ParseDiagnosisBlock(data, 0, 1, 1);
212
213 ASSERT_EQ(result.entries.size(), 1U);
214
215 EXPECT_TRUE(result.entries[0].IsExtChannelDiagnosis());
216 ASSERT_TRUE(result.entries[0].ext.has_value());
217
218 EXPECT_EQ(
219 result.entries[0].ext->extErrorTypeName,
220 "No peer detected");
221}
222
224TEST_F(DiagnosisTest, ParseDiagnosisBlockLocationHeaderUpdatesLocation)
225{
226 Bytes data;
227
228 wire::PutU16(data, 0x0010);
229 wire::PutU16(data, 22);
230 wire::PutU8(data, 1);
231 wire::PutU8(data, 0);
232
233 // Location header:
234 // API(4)=0, Slot(2)=3, Subslot(2)=1
235 wire::PutU32(data, 0);
236 wire::PutU16(data, 3);
237 wire::PutU16(data, 1);
238
239 Bytes entry =
240 MakeChannelDiagEntry(0x0001, 0x0000, 0x0001);
241
242 data.insert(data.end(), entry.begin(), entry.end());
243
244 auto result = ParseDiagnosisBlock(data);
245
246 ASSERT_EQ(result.entries.size(), 1U);
247
248 EXPECT_EQ(result.entries[0].base.slot, 3);
249 EXPECT_EQ(result.entries[0].base.subslot, 1);
250}
251
253TEST_F(DiagnosisTest, ParseDiagnosisBlockReturnsEmptyForTooShortData)
254{
255 const Bytes data = {0x01, 0x02, 0x03};
256
257 auto result = ParseDiagnosisBlock(data);
258
259 EXPECT_FALSE(result.HasErrors());
260 EXPECT_TRUE(result.entries.empty());
261}
262
264TEST_F(DiagnosisTest, ParseDiagnosisSimpleStopsOnZeroFilledSentinelEntry)
265{
266 Bytes data;
267
268 wire::PutU16(data, 0x0010);
269 wire::PutU16(data, 6);
270 wire::PutU8(data, 1);
271 wire::PutU8(data, 0);
272
273 // One real entry:
274 // ChannelNumber(2) + Properties(2) + ErrorType(2)
275 // Simple format, no USI field.
276 wire::PutU16(data, 0x0001);
277 wire::PutU16(data, 0x0000);
278 wire::PutU16(data, 0x0009); // "Error"
279
280 // Sentinel (all zero)
281 wire::PutU16(data, 0);
282 wire::PutU16(data, 0);
283 wire::PutU16(data, 0);
284
285 auto result = ParseDiagnosisSimple(data, 0, 2, 1);
286
287 ASSERT_EQ(result.entries.size(), 1U);
288
289 EXPECT_EQ(result.entries[0].base.errorTypeName, "Error");
290 EXPECT_EQ(result.entries[0].base.slot, 2);
291}
292
294TEST_F(DiagnosisTest, DiagnosisDataMaintenanceFlagsReflectEntryFlags)
295{
296 Bytes data;
297
298 wire::PutU16(data, 0x0010);
299 wire::PutU16(data, 14);
300 wire::PutU8(data, 1);
301 wire::PutU8(data, 0);
302
303 // maintenance_required = bit 5 set
304 Bytes entry =
305 MakeChannelDiagEntry(
306 0x0001,
307 (1 << 5),
308 0x0009);
309
310 data.insert(data.end(), entry.begin(), entry.end());
311
312 auto result = ParseDiagnosisBlock(data);
313
314 EXPECT_TRUE(result.HasMaintenanceRequired());
315 EXPECT_FALSE(result.HasMaintenanceDemanded());
316}
317
319TEST_F(DiagnosisTest, DiagnosisDataGetByChannelFiltersEntries)
320{
321 Bytes data;
322
323 wire::PutU16(data, 0x0010);
324 wire::PutU16(data, 22);
325 wire::PutU8(data, 1);
326 wire::PutU8(data, 0);
327
328 Bytes e1 = MakeChannelDiagEntry(0x0001, 0, 0x0001);
329
330 Bytes e2 = MakeChannelDiagEntry(0x0002, 0, 0x0002);
331
332 data.insert(data.end(), e1.begin(), e1.end());
333 data.insert(data.end(), e2.begin(), e2.end());
334
335 auto result = ParseDiagnosisBlock(data);
336
337 ASSERT_EQ(result.entries.size(), 2U);
338
339 auto matches = result.GetByChannel(0x0002);
340
341 ASSERT_EQ(matches.size(), 1U);
342 EXPECT_EQ(matches[0]->base.channelNumber, 0x0002);
343}
PROFINET diagnosis data structures, error decoding, and parsers.
std::string DecodeChannelErrorType(std::uint16_t errorType)
Decode a ChannelErrorType to a human-readable string.
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.
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.
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
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
static ChannelProperties FromUint16(std::uint16_t value)
Decode a raw 16-bit ChannelProperties value.
Definition diagnosis.cpp:19
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.