PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_cyclicControllerConformance.cpp
Go to the documentation of this file.
1
17
18#include <gtest/gtest.h>
19
20#include <array>
21#include <cstdint>
22
23#include "profinet/cyclic.h"
24#include "profinet/rt.h"
25
26namespace
27{
28constexpr std::array<std::uint8_t, 6> kControllerMac{0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
29constexpr std::array<std::uint8_t, 6> kDeviceMac{0x02, 0x00, 0x00, 0x00, 0x00, 0x01};
30constexpr std::array<std::uint8_t, 4> kVlanTag{0x81, 0x00, 0xC0, 0x00};
31constexpr std::uint16_t kProfinetEtherType = 0x8892;
32} // namespace
33
34namespace profinet::cyclic
35{
36
43class CyclicControllerConformanceTest : public ::testing::Test
44{
45 protected:
46 void SetUp() override
47 {
49 }
50
52 {
53 controller->Transition(state);
54 }
55
57 {
58 controller->TxCycle();
59 }
60
62 {
63 controller->ProcessInputFrame(data);
64 }
65
67 std::unique_ptr<CyclicController> controller;
68
73 {
75
77 config.referenceIOCR = 1;
78 config.frameId = 0xC001;
79 config.sendClockFactor = 1;
80 config.reductionRatio = 1;
81 config.watchdogFactor = 3;
82 config.dataLength = 40;
83
84 config.objects.push_back(profinet::rt::IODataObject{
85 .slot = 1,
86 .subslot = 1,
87 .frameOffset = 0,
88 .dataLength = 4,
89 .offsetIOPS = 4});
90
91 return config;
92 }
93
98 {
100
102 config.frameId = 0xC000;
103 // config.referenceIOCR = 2;
104 config.sendClockFactor = 32;
105 config.reductionRatio = 32;
106 config.watchdogFactor = 3;
107 config.dataLength = 40;
108
109 config.objects.push_back(profinet::rt::IODataObject{
110 .slot = 1,
111 .subslot = 1,
112 .frameOffset = 0,
113 .dataLength = 4,
114 .offsetIOPS = 4,
115 .offsetIOCS = 5,
116 });
117
118 return config;
119 }
120
124 static std::unique_ptr<profinet::cyclic::CyclicController> MakeController()
125 {
126 asio::io_context ioContext;
127 return std::make_unique<CyclicController>(
128 ioContext,
129 "eth0",
130 kControllerMac,
131 kDeviceMac,
134 }
135
148 static profinet::Bytes BuildInputFrame(bool vlan = false, std::uint8_t transferStatus = 0, profinet::Bytes payload = {})
149 {
150 if (payload.empty())
151 {
152 payload = {0xAA, 0xBB, 0xCC, 0xDD, 0x80};
153
154 payload.resize(40U, 0x00);
155 }
156
158
159 rt.frameId = 0xC001;
160 rt.cycleCounter = 1;
162 rt.transferStatus = transferStatus;
163 rt.payload = std::move(payload);
164
166
167 frame.insert(frame.end(), kControllerMac.begin(), kControllerMac.end());
168
169 frame.insert(frame.end(), kDeviceMac.begin(), kDeviceMac.end());
170
171 if (vlan)
172 {
173 frame.insert(frame.end(), kVlanTag.begin(), kVlanTag.end());
174 }
175
176 frame.push_back(static_cast<std::uint8_t>((kProfinetEtherType >> 8U) & 0xFFU));
177
178 frame.push_back(static_cast<std::uint8_t>(kProfinetEtherType & 0xFFU));
179
180 const profinet::Bytes rtBytes = rt.ToBytes();
181
182 frame.insert(frame.end(), rtBytes.begin(), rtBytes.end());
183
184 return frame;
185 }
186};
187
189TEST_F(CyclicControllerConformanceTest, UntaggedInputFrameIsAccepted)
190{
192 InvokeProcessInputFrame(BuildInputFrame());
193
194 EXPECT_EQ(controller->StatsSnapshot().framesReceived, 1U);
195
196 const auto data = controller->GetInputData(1, 1);
197
198 ASSERT_TRUE(data.has_value());
199
200 EXPECT_EQ(*data, profinet::Bytes({0xAA, 0xBB, 0xCC, 0xDD}));
201}
202
204TEST_F(CyclicControllerConformanceTest, VlanTaggedInputFrameIsAccepted)
205{
207 InvokeProcessInputFrame(BuildInputFrame(true));
208
209 EXPECT_EQ(controller->StatsSnapshot().framesReceived, 1U);
210
211 const auto data = controller->GetInputData(1, 1);
212
213 ASSERT_TRUE(data.has_value());
214
215 EXPECT_EQ(*data, profinet::Bytes({0xAA, 0xBB, 0xCC, 0xDD}));
216}
217
223TEST_F(CyclicControllerConformanceTest, NonZeroTransferStatusIsRejected)
224{
226 InvokeProcessInputFrame(BuildInputFrame(false, 1));
227
228 EXPECT_EQ(controller->StatsSnapshot().framesInvalid, 1U);
229 EXPECT_FALSE(controller->GetInputData(1, 1).has_value());
230}
231
241TEST_F(CyclicControllerConformanceTest, TxContinuesWhileControllerIsInFault)
242{
243 InvokeTransition(profinet::cyclic::CyclicState::Fault);
244 InvokeTxCycle();
245
246 EXPECT_EQ(controller->StatsSnapshot().framesSent, 1U);
247}
248} // namespace profinet::cyclic
Test fixture for cyclic controller protocol conformance.
std::unique_ptr< CyclicController > controller
The controller under test used to verify the private functionality.
static profinet::Bytes BuildInputFrame(bool vlan=false, std::uint8_t transferStatus=0, profinet::Bytes payload={})
Build a device-to-controller cyclic RT Ethernet frame.
static std::unique_ptr< profinet::cyclic::CyclicController > MakeController()
Construct the cyclic controller under test.
static profinet::rt::IOCRConfig MakeInputIocr()
Construct the input IOCR used by the regression tests.
static profinet::rt::IOCRConfig MakeOutputIocr()
Construct the output IOCR used by the regression tests.
Declares the PROFINET RTC1 cyclic IO controller and process-data exchange API.
TEST_F(CyclicTest, CyclicStateToStringCoversAllStates)
CyclicState
Lifecycle state of a CyclicController.
Definition cyclic.h:41
@ Fault
Communication failure (e.g. consecutive watchdog timeouts).
@ Running
Active cyclic data exchange.
constexpr std::uint16_t IOCR_TYPE_INPUT
IOCR direction: Device -> Controller.
Definition rt.h:66
constexpr std::uint16_t IOCR_TYPE_OUTPUT
IOCR direction: Controller -> Device.
Definition rt.h:68
constexpr MacAddress kControllerMac
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
Declares PROFINET RT_CLASS_1 cyclic frame and IOCR data structures.
IOCR configuration derived from AR setup: timing parameters and IO object mappings needed for cyclic ...
Definition rt.h:137
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 referenceIOCR
IOCR reference assigned during AR establishment.
Definition rt.h:142
std::uint16_t sendClockFactor
Send clock base factor (31.25us units).
Definition rt.h:148
std::uint16_t frameId
Frame ID assigned to this IOCR.
Definition rt.h:145
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
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
std::uint8_t dataStatus
Data status bitmask (see DATA_STATUS_* constants).
Definition rt.h:216
std::uint8_t transferStatus
Transfer status (0 = OK).
Definition rt.h:219
Bytes ToBytes() const
Serialize this frame back to raw bytes.
Definition rt.cpp:37
std::uint16_t frameId
Frame ID identifying which IOCR this frame belongs to.
Definition rt.h:210
std::uint16_t cycleCounter
Cycle counter, incremented each transmission.
Definition rt.h:213
static constexpr std::uint8_t DATA_RUN
Alias for DATA_STATUS_PROVIDER_RUN, for readability at call sites.
Definition rt.h:232
static constexpr std::uint8_t DATA_VALID
Alias for DATA_STATUS_VALID, for readability at call sites.
Definition rt.h:226
Bytes payload
C_SDU payload: process data plus IOPS/IOCS trailers.
Definition rt.h:222
std::unique_ptr< cyclic::CyclicController > controller
Bytes frame
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
Bytes payload
Definition test_dcp.cpp:392