PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_rtMore.cpp
Go to the documentation of this file.
1
6
7#include <gtest/gtest.h>
8
9#include <array>
10#include <cstdint>
11#include <string>
12#include <vector>
13
14#include "profinet/rpc.h"
15#include "profinet/rt.h"
16#include "profinet/util.h"
17
18using namespace profinet;
19using namespace profinet::rt;
20
21namespace
22{
25IOCRConfig MakeConfig()
26{
27 IOCRConfig config;
28 config.dataLength = 12;
29 config.objects = {{.slot = 1,
30 .subslot = 1,
31 .frameOffset = 0,
32 .dataLength = 4,
33 .offsetIOPS = 4,
34 .offsetIOCS = 0},
35 {.slot = 2,
36 .subslot = 1,
37 .frameOffset = 6,
38 .dataLength = 2,
39 .offsetIOPS = 8,
40 .offsetIOCS = 10},
41 {.slot = 3,
42 .subslot = 1,
43 .frameOffset = 9,
44 .dataLength = 0,
45 .offsetIOCS = 11}};
46 return config;
47}
48
55rpc::IOSlot MakeSlot(std::uint16_t slot,
56 std::uint16_t subslot,
57 std::uint16_t inputLength,
58 std::uint16_t outputLength)
59{
60 rpc::IOSlot s{};
61 s.slot = slot;
62 s.subslot = subslot;
63 s.inputLength = inputLength;
64 s.outputLength = outputLength;
65 return s;
66}
67
72Bytes MakeEthernetFrame(const std::uint16_t etherType, const Bytes& rt)
73{
74 const MacAddress dst{0x02, 0, 0, 0, 0, 1};
75 const MacAddress src{0x10, 0x11, 0x12, 0x13, 0x14, 0x15};
76
77 Bytes out(dst.begin(), dst.end());
78 out.insert(out.end(), src.begin(), src.end());
79 wire::PutU16(out, etherType);
80 out.insert(out.end(), rt.begin(), rt.end());
81 return out;
82}
83
84} // namespace
85
86namespace
87{
89class RtMoreTests : public ::testing::Test
90{
91 protected:
94 static RTFrame MakeFrame()
95 {
97 frame.frameId = 0xC123;
98 frame.cycleCounter = 0x4567;
99 frame.dataStatus = 0;
100 frame.transferStatus = 0xAA;
101 frame.payload = {1, 2, 3};
102 return frame;
103 }
104};
105} // namespace
106
108TEST_F(RtMoreTests, RTFrameStatusAccessorsCoverAllBits)
109{
110 RTFrame frame = MakeFrame();
111
112 EXPECT_FALSE(frame.IsValid());
113 EXPECT_FALSE(frame.IsRunning());
114 EXPECT_FALSE(frame.IsOk());
115 EXPECT_FALSE(frame.IsPrimary());
116
117 frame.dataStatus = DATA_STATUS_VALID;
118 EXPECT_TRUE(frame.IsValid());
119 EXPECT_FALSE(frame.IsRunning());
120 EXPECT_FALSE(frame.IsOk());
121 EXPECT_FALSE(frame.IsPrimary());
122
123 frame.dataStatus = DATA_STATUS_PROVIDER_RUN;
124 EXPECT_FALSE(frame.IsValid());
125 EXPECT_TRUE(frame.IsRunning());
126
127 frame.dataStatus = DATA_STATUS_STATION_OK;
128 EXPECT_FALSE(frame.IsValid());
129 EXPECT_TRUE(frame.IsOk());
130
131 frame.dataStatus = DATA_STATUS_STATE;
132 EXPECT_TRUE(frame.IsPrimary());
133}
134
136TEST_F(RtMoreTests, RTFrameSerializationPreservesAllFields)
137{
138 const RTFrame frame = MakeFrame();
139
140 const auto encoded = frame.ToBytes();
141 const auto decoded = RTFrame::FromBytes(encoded);
142
143 EXPECT_EQ(decoded.frameId, frame.frameId);
144 EXPECT_EQ(decoded.cycleCounter, frame.cycleCounter);
145 EXPECT_EQ(decoded.dataStatus, frame.dataStatus);
146 EXPECT_EQ(decoded.transferStatus, frame.transferStatus);
147 EXPECT_EQ(decoded.payload, frame.payload);
148}
149
151TEST_F(RtMoreTests, RTFrameFromBytesRejectsShortBuffers)
152{
153 constexpr std::size_t fixedHeaderSize = 6U;
154
155 for (std::size_t length = 0; length < fixedHeaderSize; ++length)
156 {
157 EXPECT_THROW(RTFrame::FromBytes(Bytes(length, 0)), std::exception)
158 << "length=" << length;
159 }
160}
161
163TEST_F(RtMoreTests, RTFrameToStringIsNonEmpty)
164{
165 const auto text = MakeFrame().ToString();
166
167 EXPECT_FALSE(text.empty());
168}
169
171TEST_F(RtMoreTests, ParseEthernetFrameRejectsShortFrames)
172{
173 for (std::size_t length = 0; length < 14U; ++length)
174 {
175 EXPECT_FALSE(ParseEthernetFrame(Bytes(length, 0)).has_value());
176 }
177}
178
180TEST_F(RtMoreTests, ParseEthernetFrameAcceptsPriorityVlan)
181{
182 RTFrame frame = MakeFrame();
183 const auto rt = frame.ToBytes();
184
185 Bytes raw{
186 0x02, 0, 0, 0, 0, 1, 0x10,
187 0x11, 0x12, 0x13, 0x14, 0x15,
188 0x81, 0x00, // 802.1Q VLAN TPID
189 0xC0, 0x00, // PCP=6, DEI=0, VID=0
190 0x88, 0x92 // PROFINET RT EtherType
191 };
192
193 raw.insert(raw.end(), rt.begin(), rt.end());
194
195 const auto parsed = ParseEthernetFrame(raw);
196
197 ASSERT_TRUE(parsed.has_value());
198 EXPECT_EQ(parsed->frameId, frame.frameId);
199 EXPECT_EQ(parsed->payload, frame.payload);
200}
201
203TEST_F(RtMoreTests, ParseEthernetFrameRejectsVlanNonProfinet)
204{
205 const auto rt = MakeFrame().ToBytes();
206
207 Bytes raw{0x02, 0, 0, 0, 0, 1, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x81, 0x00, 0xC0, 0x00, 0x08, 0x00};
208
209 raw.insert(raw.end(), rt.begin(), rt.end());
210
211 EXPECT_FALSE(ParseEthernetFrame(raw).has_value());
212}
213
215TEST_F(RtMoreTests, CyclicDataBuilderSetIopsUpdatesDataObject)
216{
217 CyclicDataBuilder builder(MakeConfig());
218
219 builder.SetIops(1, 1, 0x12);
220 builder.Swap();
221
222 const auto data = builder.Build();
223
224 EXPECT_EQ(data[4], 0x12);
225}
226
228TEST_F(RtMoreTests, CyclicDataBuilderSetIocsUpdatesIocsOnlyObject)
229{
230 CyclicDataBuilder builder(MakeConfig());
231
232 builder.SetIocs(3, 1, 0x34);
233 builder.Swap();
234
235 const auto data = builder.Build();
236
237 EXPECT_EQ(data[11], 0x34);
238}
239
241TEST_F(RtMoreTests, CyclicDataBuilderSetIopsIgnoresIocsOnlyObject)
242{
243 CyclicDataBuilder builder(MakeConfig());
244
245 builder.SetIops(3, 1, 0x55);
246 builder.Swap();
247
248 EXPECT_EQ(builder.Build()[11], 0U);
249}
250
252TEST_F(RtMoreTests, CyclicDataBuilderSetIocsUpdatesDataObject)
253{
254 CyclicDataBuilder builder(MakeConfig());
255
256 builder.SetIocs(1, 1, 0x77);
257 builder.Swap();
258
259 EXPECT_EQ(builder.Build()[0], 0x77U);
260}
261
263TEST_F(RtMoreTests, CyclicDataBuilderSetAllIopsUpdatesAllDataObjects)
264{
265 CyclicDataBuilder builder(MakeConfig());
266
267 builder.SetAllIops(0x66);
268 builder.Swap();
269
270 const auto data = builder.Build();
271
272 EXPECT_EQ(data[4], 0x66);
273 EXPECT_EQ(data[8], 0x66);
274 EXPECT_EQ(data[11], 0U);
275}
276
278TEST_F(RtMoreTests, CyclicDataBuilderSetAllIocsUpdatesAllIocs)
279{
280 rt::IOCRConfig config = MakeConfig();
281 config.dataLength = 32;
282
283 config.objects = {
285 .slot = 1,
286 .subslot = 1,
287 .frameOffset = 0,
288 .dataLength = 4,
289 .offsetIOPS = 4,
290 .offsetIOCS = 5},
292 .slot = 2,
293 .subslot = 1,
294 .frameOffset = 6,
295 .dataLength = 4,
296 .offsetIOPS = 12,
297 .offsetIOCS = 13},
299 .slot = 3,
300 .subslot = 1,
301 .frameOffset = 16,
302 .dataLength = 4,
303 .offsetIOPS = 20,
304 .offsetIOCS = 21}};
305
306 CyclicDataBuilder builder(config);
307
308 builder.SetAllIocs(0x88U);
309 builder.Swap();
310
311 const auto data = builder.Build();
312
313 EXPECT_EQ(data.at(5), 0x88U);
314 EXPECT_EQ(data.at(13), 0x88U);
315 EXPECT_EQ(data.at(21), 0x88U);
316}
317
319TEST_F(RtMoreTests, CyclicDataBuilderClearResetsBuffers)
320{
321 rt::IOCRConfig config;
322 config.dataLength = 32;
323
324 config.objects = {
326 .slot = 1,
327 .subslot = 1,
328 .frameOffset = 0,
329 .dataLength = 4,
330 .offsetIOPS = 4,
331 .offsetIOCS = 5}};
332
333 CyclicDataBuilder builder(config);
334
335 builder.SetData(1, 1, {1, 2, 3, 4});
336 builder.SetIocs(1, 1, 0x55U);
337 builder.Swap();
338
339 ASSERT_EQ(builder.Build().at(0), 1U);
340 ASSERT_EQ(builder.Build().at(1), 2U);
341 ASSERT_EQ(builder.Build().at(2), 3U);
342 ASSERT_EQ(builder.Build().at(3), 4U);
343 ASSERT_EQ(builder.Build().at(5), 0x55U);
344
345 builder.Clear();
346
347 // Clear() modifies the write buffer; Swap() promotes it to the send buffer.
348 builder.Swap();
349
350 EXPECT_EQ(builder.Build(), Bytes(32U, 0));
351 EXPECT_EQ(builder.GetData(1, 1), Bytes(4U, 0));
352}
353
355TEST_F(RtMoreTests, CyclicDataBuilderLoadCopiesAndClearsRemainder)
356{
357 CyclicDataBuilder builder(MakeConfig());
358
359 builder.Load({1, 2, 3});
360 builder.Swap();
361
362 const auto data = builder.Build();
363
364 ASSERT_EQ(data.size(), 12U);
365 EXPECT_EQ(data[0], 1U);
366 EXPECT_EQ(data[1], 2U);
367 EXPECT_EQ(data[2], 3U);
368 EXPECT_EQ(data[3], 0U);
369}
370
372TEST_F(RtMoreTests, CyclicDataBuilderLoadTruncatesLongPayload)
373{
374 CyclicDataBuilder builder(MakeConfig());
375
376 builder.Load(Bytes(32U, 0xAA));
377 builder.Swap();
378
379 const auto data = builder.Build();
380
381 EXPECT_EQ(data.size(), 12U);
382 EXPECT_EQ(data[0], 0xAAU);
383 EXPECT_EQ(data[11], 0xAAU);
384}
385
387TEST_F(RtMoreTests, CyclicDataBuilderGetDataRejectsUnknownSlotSubslot)
388{
389 CyclicDataBuilder builder(MakeConfig());
390
391 EXPECT_THROW(builder.GetData(9, 9), std::exception);
392}
393
395TEST_F(RtMoreTests, CyclicDataBuilderSetDataTruncatesOversizedData)
396{
397 rt::IOCRConfig config;
398 config.dataLength = 16;
399
400 config.objects = {
402 .slot = 1,
403 .subslot = 1,
404 .frameOffset = 2,
405 .dataLength = 4,
406 .offsetIOPS = 6,
407 .offsetIOCS = 7}};
408
409 CyclicDataBuilder builder(config);
410
411 builder.SetData(1, 1, Bytes{0x01, 0x02, 0x03, 0x04, 0x05});
412
413 EXPECT_EQ(builder.GetData(1, 1), (Bytes{0x01, 0x02, 0x03, 0x04}));
414}
415
417TEST_F(RtMoreTests, CyclicDataBuilderSwapWithoutChangesPreservesData)
418{
419 CyclicDataBuilder builder(MakeConfig());
420
421 builder.SetData(1, 1, {1, 2, 3, 4});
422 builder.Swap();
423 const auto before = builder.Build();
424
425 builder.Swap();
426 const auto after = builder.Build();
427
428 EXPECT_EQ(after, before);
429}
430
432TEST_F(RtMoreTests, IOCRTimingHelpersCalculateCycleAndWatchdog)
433{
434 IOCRConfig config;
435 config.sendClockFactor = 32;
436 config.reductionRatio = 8;
437 config.watchdogFactor = 3;
438
439 EXPECT_EQ(config.CycleTimeUs(), 8000U);
440 EXPECT_DOUBLE_EQ(config.CycleTimeMs(), 8.0);
441 EXPECT_EQ(config.WatchdogTimeUs(), 24000U);
442}
443
445TEST_F(RtMoreTests, IOCRTypePredicatesCoverBothDirections)
446{
447 IOCRConfig config;
448
449 config.typeIOCR = IOCR_TYPE_INPUT;
450 EXPECT_TRUE(config.IsInput());
451 EXPECT_FALSE(config.IsOutput());
452
453 config.typeIOCR = IOCR_TYPE_OUTPUT;
454 EXPECT_FALSE(config.IsInput());
455 EXPECT_TRUE(config.IsOutput());
456
457 config.typeIOCR = 0;
458 EXPECT_FALSE(config.IsInput());
459 EXPECT_FALSE(config.IsOutput());
460}
461
463TEST_F(RtMoreTests, BuildIocrConfigsHandlesEmptySlotList)
464{
465 const std::vector<rpc::IOSlot> slots;
466
467 const auto [input, output] = BuildIocrConfigs(slots, 0xC001, 0x8001);
468
469 EXPECT_TRUE(input.objects.empty());
470 EXPECT_TRUE(output.objects.empty());
471 EXPECT_EQ(input.dataLength, 40U);
472 EXPECT_EQ(output.dataLength, 40U);
473}
474
477TEST_F(RtMoreTests, BuildIocrConfigsCoversInputAndOutputBranches)
478{
479 const std::vector<rpc::IOSlot> slots{
480 MakeSlot(1, 1, 0, 0), // input == 0, output == 0
481 MakeSlot(2, 1, 4, 0), // input > 0, output == 0
482 MakeSlot(3, 1, 0, 6), // input == 0, output > 0
483 MakeSlot(4, 1, 3, 2) // input > 0, output > 0
484 };
485
486 const auto [input, output] =
487 BuildIocrConfigs(slots, 0xC001, 0xC002, 32, 8, 3, 43);
488
489 ASSERT_EQ(input.objects.size(), 5U);
490 ASSERT_EQ(output.objects.size(), 5U);
491
492 // -------------------------------------------------------------------------
493 // Input IOCR
494 //
495 // IOCS section:
496 // slot 1 -> IOCS @ 0
497 // slot 3 -> IOCS @ 1
498 // slot 4 -> IOCS @ 2
499 //
500 // Input data section:
501 // slot 2 -> data @ 3, length 4, IOPS @ 7
502 // slot 4 -> data @ 8, length 3, IOPS @ 11
503 // -------------------------------------------------------------------------
504
505 EXPECT_EQ(input.objects[0].slot, 1U);
506 EXPECT_EQ(input.objects[0].subslot, 1U);
507 EXPECT_EQ(input.objects[0].dataLength, 0U);
508 EXPECT_EQ(input.objects[0].frameOffset, 0U);
509 EXPECT_EQ(input.objects[0].offsetIOPS, std::nullopt);
510 EXPECT_EQ(input.objects[0].offsetIOCS, 0U);
511
512 EXPECT_EQ(input.objects[1].slot, 3U);
513 EXPECT_EQ(input.objects[1].subslot, 1U);
514 EXPECT_EQ(input.objects[1].dataLength, 0U);
515 EXPECT_EQ(input.objects[1].frameOffset, 1U);
516 EXPECT_EQ(input.objects[1].offsetIOPS, std::nullopt);
517 EXPECT_EQ(input.objects[1].offsetIOCS, 1U);
518
519 // Bidirectional submodule: IOCS entry in addition to input data entry.
520 EXPECT_EQ(input.objects[2].slot, 4U);
521 EXPECT_EQ(input.objects[2].subslot, 1U);
522 EXPECT_EQ(input.objects[2].dataLength, 0U);
523 EXPECT_EQ(input.objects[2].frameOffset, 2U);
524 EXPECT_EQ(input.objects[2].offsetIOPS, std::nullopt);
525 EXPECT_EQ(input.objects[2].offsetIOCS, 2U);
526
527 EXPECT_EQ(input.objects[3].slot, 2U);
528 EXPECT_EQ(input.objects[3].subslot, 1U);
529 EXPECT_EQ(input.objects[3].dataLength, 4U);
530 EXPECT_EQ(input.objects[3].frameOffset, 2U); // DIT LIJKT FOUT!
531 EXPECT_EQ(input.objects[3].offsetIOPS, 7U);
532 EXPECT_EQ(input.objects[3].offsetIOCS, std::nullopt);
533
534 // Bidirectional submodule: input data entry as well as IOCS entry.
535 EXPECT_EQ(input.objects[4].slot, 4U);
536 EXPECT_EQ(input.objects[4].subslot, 1U);
537 EXPECT_EQ(input.objects[4].dataLength, 3U);
538 EXPECT_EQ(input.objects[4].frameOffset, 7U);
539 EXPECT_EQ(input.objects[4].offsetIOPS, 11U);
540 EXPECT_EQ(input.objects[4].offsetIOCS, std::nullopt);
541
542 // -------------------------------------------------------------------------
543 // Output IOCR
544 //
545 // IOCS section:
546 // slot 1 -> IOCS @ 0
547 // slot 2 -> IOCS @ 1
548 // slot 4 -> IOCS @ 2
549 //
550 // Output data section:
551 // slot 3 -> data @ 3, length 6, IOPS @ 9
552 // slot 4 -> data @ 10, length 2, IOPS @ 12
553 // -------------------------------------------------------------------------
554
555 EXPECT_EQ(output.objects[0].slot, 1U);
556 EXPECT_EQ(output.objects[0].subslot, 1U);
557 EXPECT_EQ(output.objects[0].dataLength, 0U);
558 EXPECT_EQ(output.objects[0].frameOffset, 0U);
559 EXPECT_EQ(output.objects[0].offsetIOPS, std::nullopt);
560 EXPECT_EQ(output.objects[0].offsetIOCS, 0U);
561
562 EXPECT_EQ(output.objects[1].slot, 2U);
563 EXPECT_EQ(output.objects[1].subslot, 1U);
564 EXPECT_EQ(output.objects[1].dataLength, 0U);
565 EXPECT_EQ(output.objects[1].frameOffset, 1U);
566 EXPECT_EQ(output.objects[1].offsetIOPS, std::nullopt);
567 EXPECT_EQ(output.objects[1].offsetIOCS, 1U);
568
569 // Bidirectional submodule: IOCS entry in addition to output data entry.
570 EXPECT_EQ(output.objects[2].slot, 4U);
571 EXPECT_EQ(output.objects[2].subslot, 1U);
572 EXPECT_EQ(output.objects[2].dataLength, 2U);
573 EXPECT_EQ(output.objects[2].frameOffset, 2U);
574 EXPECT_EQ(output.objects[2].offsetIOPS, std::nullopt);
575 EXPECT_EQ(output.objects[2].offsetIOCS, 2U);
576
577 EXPECT_EQ(output.objects[3].slot, 3U);
578 EXPECT_EQ(output.objects[3].subslot, 1U);
579 EXPECT_EQ(output.objects[3].dataLength, 6U);
580 EXPECT_EQ(output.objects[3].frameOffset, 3U);
581 EXPECT_EQ(output.objects[3].offsetIOPS, 9U);
582 EXPECT_EQ(output.objects[3].offsetIOCS, std::nullopt);
583
584 // Bidirectional submodule: output data entry as well as IOCS entry.
585 EXPECT_EQ(output.objects[4].slot, 4U);
586 EXPECT_EQ(output.objects[4].subslot, 1U);
587 EXPECT_EQ(output.objects[4].dataLength, 2U);
588 EXPECT_EQ(output.objects[4].frameOffset, 10U);
589 EXPECT_EQ(output.objects[4].offsetIOPS, 12U);
590 EXPECT_EQ(output.objects[4].offsetIOCS, std::nullopt);
591
592 // -------------------------------------------------------------------------
593 // IOCR configuration.
594 // -------------------------------------------------------------------------
595
596 EXPECT_EQ(input.typeIOCR, IOCR_TYPE_INPUT);
597 EXPECT_EQ(output.typeIOCR, IOCR_TYPE_OUTPUT);
598
599 EXPECT_EQ(input.referenceIOCR, 1U);
600 EXPECT_EQ(output.referenceIOCR, 2U);
601
602 EXPECT_EQ(input.frameId, 0xC001U);
603 EXPECT_EQ(output.frameId, 0xC002U);
604
605 EXPECT_EQ(input.sendClockFactor, 32U);
606 EXPECT_EQ(output.sendClockFactor, 32U);
607
608 EXPECT_EQ(input.reductionRatio, 8U);
609 EXPECT_EQ(output.reductionRatio, 8U);
610
611 EXPECT_EQ(input.phase, 0U);
612 EXPECT_EQ(output.phase, 0U);
613
614 EXPECT_EQ(input.watchdogFactor, 3);
615 EXPECT_EQ(output.watchdogFactor, 3);
616
617 // The configured minimum C_SDU length is larger than the calculated
618 // layouts, so both IOCRs are padded to 43 bytes.
619 EXPECT_EQ(input.dataLength, 43U);
620 EXPECT_EQ(output.dataLength, 43U);
621}
622
624TEST_F(RtMoreTests, BuildIocrConfigsUsesMinimumCSduLength)
625{
626 const std::vector<rpc::IOSlot> slots{
627 MakeSlot(1, 1, 0, 0)};
628
629 const auto [input, output] =
630 BuildIocrConfigs(slots, 0xC001, 0xC002, 32, 8, 3, 43);
631
632 EXPECT_EQ(input.dataLength, 43U);
633 EXPECT_EQ(output.dataLength, 43U);
634}
635
637TEST_F(RtMoreTests, BuildIocrConfigsUsesCalculatedSizeWhenLarger)
638{
639 const std::vector<rpc::IOSlot> slots{
640 MakeSlot(1, 1, 0, 0),
641 MakeSlot(2, 1, 20, 20),
642 MakeSlot(3, 1, 20, 20)};
643
644 const auto [input, output] =
645 BuildIocrConfigs(slots, 0xC001, 0xC002, 32, 8, 3, 1);
646
647 EXPECT_GT(input.dataLength, 1U);
648 EXPECT_GT(output.dataLength, 1U);
649}
650
652TEST_F(RtMoreTests, CyclicDataBuilderSetDataRejectsUnknownSlotSubslot)
653{
654 CyclicDataBuilder builder(MakeConfig());
655
656 EXPECT_THROW(builder.SetData(9, 9, {1, 2, 3}), std::invalid_argument);
657}
658
660TEST_F(RtMoreTests, CyclicDataBuilderSwapPromotesDirtyBuffer)
661{
662 CyclicDataBuilder builder(MakeConfig());
663
664 builder.SetData(1, 1, {0x11, 0x22, 0x33, 0x44});
665
666 ASSERT_EQ(builder.Build(), Bytes(12U, 0));
667
668 builder.Swap();
669
670 EXPECT_EQ(builder.Build().at(0), 0x11U);
671 EXPECT_EQ(builder.Build().at(1), 0x22U);
672 EXPECT_EQ(builder.Build().at(2), 0x33U);
673 EXPECT_EQ(builder.Build().at(3), 0x44U);
674}
675
677TEST_F(RtMoreTests, CyclicDataBuilderConfigReturnsConfiguration)
678{
679 const auto config = MakeConfig();
680 CyclicDataBuilder builder(config);
681
682 EXPECT_EQ(builder.Config().dataLength, config.dataLength);
683 ASSERT_EQ(builder.Config().objects.size(), config.objects.size());
684}
Builds C_SDU payload from IO data objects with double-buffering.
Definition rt.h:287
const IOCRConfig & Config() const
The IOCR configuration this builder was constructed with.
Definition rt.h:346
Bytes GetData(int slot, int subslot) const
Read one object's data from the write buffer.
Definition rt.cpp:133
void Clear()
Zero the entire write buffer.
Definition rt.cpp:211
void Load(const Bytes &payload)
Load received payload data into the write buffer.
Definition rt.cpp:234
void SetAllIocs(std::uint8_t status=IOXS_GOOD)
Set every object's IOCS byte to the same value.
Definition rt.cpp:198
void SetData(int slot, int subslot, const Bytes &data)
Write one object's data into the write buffer.
Definition rt.cpp:116
void SetIocs(int slot, int subslot, std::uint8_t status=IOXS_GOOD)
Set one object's IOCS (consumer status) byte, if it has one.
Definition rt.cpp:167
void SetIops(int slot, int subslot, std::uint8_t status=IOXS_GOOD)
Set one object's IOPS (provider status) byte.
Definition rt.cpp:149
void Swap()
Promote the write buffer to the send buffer.
Definition rt.cpp:218
Bytes Build() const
Get the current send buffer contents.
Definition rt.cpp:228
void SetAllIops(std::uint8_t status=IOXS_GOOD)
Set every object's IOPS byte to the same value.
Definition rt.cpp:185
std::optional< RTFrame > ParseEthernetFrame(const Bytes &data)
Parse a complete Ethernet frame and extract its RT frame.
Definition rt.cpp:261
constexpr std::uint16_t IOCR_TYPE_INPUT
IOCR direction: Device -> Controller.
Definition rt.h:66
constexpr std::uint8_t DATA_STATUS_STATION_OK
DataStatus bit: station health, 0=Problem, 1=OK.
Definition rt.h:89
constexpr std::uint8_t DATA_STATUS_PROVIDER_RUN
DataStatus bit: provider run state, 0=Stop, 1=Run.
Definition rt.h:87
std::pair< IOCRConfig, IOCRConfig > BuildIocrConfigs(const std::vector< SlotT > &slots, std::uint16_t inputFrameId, std::uint16_t outputFrameId, std::uint16_t sendClockFactor=32, std::uint16_t reductionRatio=32, int watchdogFactor=3, std::uint16_t cSduLength=40)
Definition rt.h:454
constexpr std::uint8_t DATA_STATUS_STATE
DataStatus bit: provider state, 0=Backup, 1=Primary.
Definition rt.h:79
constexpr std::uint8_t DATA_STATUS_VALID
DataStatus bit: data validity, 0=Invalid, 1=Valid.
Definition rt.h:83
constexpr std::uint16_t IOCR_TYPE_OUTPUT
IOCR direction: Controller -> Device.
Definition rt.h:68
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
TEST_F(AlarmListenerTests, InitializesWithEndpoint)
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
PROFINET DCE/RPC protocol structures and IO-Device connection API.
Declares PROFINET RT_CLASS_1 cyclic frame and IOCR data structures.
One slot/subslot's expected module configuration and IO data sizes.
Definition rpcTypes.h:96
std::uint16_t slot
Slot number.
Definition rpcTypes.h:98
IOCR configuration derived from AR setup: timing parameters and IO object mappings needed for cyclic ...
Definition rt.h:137
std::uint32_t CycleTimeUs() const
Compute the cycle time in microseconds.
Definition rt.h:171
std::vector< IODataObject > objects
IO data objects carried in this IOCR's cyclic frame.
Definition rt.h:163
std::uint16_t dataLength
Total cyclic data length in bytes.
Definition rt.h:160
std::uint16_t typeIOCR
IOCR direction: 1=Input, 2=Output.
Definition rt.h:139
std::uint16_t sendClockFactor
Send clock base factor (31.25us units).
Definition rt.h:148
bool IsInput() const
Whether this is an input (device -> controller) IOCR.
Definition rt.h:192
double CycleTimeMs() const
Compute the cycle time in milliseconds.
Definition rt.h:178
std::uint16_t reductionRatio
Reduction ratio relative to the send clock.
Definition rt.h:151
std::uint16_t watchdogFactor
Watchdog factor (missed-frame tolerance before a timeout fault).
Definition rt.h:157
bool IsOutput() const
Whether this is an output (controller -> device) IOCR.
Definition rt.h:199
std::uint32_t WatchdogTimeUs() const
Compute the watchdog timeout in microseconds.
Definition rt.h:185
Single IO data object within the C_SDU: one piece of process data mapped to a slot/subslot,...
Definition rt.h:109
std::uint16_t slot
Slot number.
Definition rt.h:111
A single PROFINET Real-Time cyclic frame: Frame ID + C_SDU payload + cycle counter/status trailer.
Definition rt.h:208
static RTFrame FromBytes(const Bytes &data)
Parse an RT frame from raw bytes (after the Ethernet header).
Definition rt.cpp:14
std::uint16_t frameId
Frame ID identifying which IOCR this frame belongs to.
Definition rt.h:210
Bytes frame
std::uint16_t length
Definition test_dcp.cpp:391
Declares Linux Ethernet, addressing, timing, and utility helpers used by the PROFINET stack.