PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
test_receivedIops.cpp
Go to the documentation of this file.
1
14
15#include <gtest/gtest.h>
16
17#include <array>
18#include <cstdint>
19#include <vector>
20
21#include "profinet/cyclic.h"
22
23namespace
24{
25constexpr std::uint8_t kIopsGood = 0x80;
26constexpr std::array<std::uint8_t, 6> kControllerMac{0x00, 0x11, 0x22, 0x33, 0x44, 0x55};
27constexpr std::array<std::uint8_t, 6> kDeviceMac{0x02, 0x00, 0x00, 0x00, 0x00, 0x01};
28constexpr std::uint16_t kProfinetEtherType = 0x8892;
29} // namespace
30
31namespace profinet::cyclic
32{
40class ReceivedIopsTest : public ::testing::Test
41{
42 protected:
43 void SetUp() override
44 {
46 }
47
49 {
50 controller->ProcessInputFrame(data);
51 }
52
54 std::unique_ptr<CyclicController> controller;
55
63 [[nodiscard]] Bytes MakePayload(const Bytes& data, std::uint8_t iops) const
64 {
65 Bytes payload = data;
66 payload.push_back(iops);
67 payload.resize(40U, 0x00);
68 return payload;
69 }
70
76 [[nodiscard]] Bytes BuildInputFrame(const Bytes& payload) const
77 {
78 rt::RTFrame rt;
79
80 rt.frameId = 0xC001;
81 rt.cycleCounter = 1;
83 rt.transferStatus = 0;
84 rt.payload = payload;
85
87
88 frame.insert(frame.end(), kControllerMac.begin(), kControllerMac.end());
89 frame.insert(frame.end(), kDeviceMac.begin(), kDeviceMac.end());
90 frame.push_back(static_cast<std::uint8_t>(kProfinetEtherType >> 8U));
91 frame.push_back(static_cast<std::uint8_t>(kProfinetEtherType & 0xFFU));
92
93 const Bytes rtBytes = rt.ToBytes();
94 frame.insert(frame.end(), rtBytes.begin(), rtBytes.end());
95
96 return frame;
97 }
98
102 static std::unique_ptr<profinet::cyclic::CyclicController> MakeController()
103 {
104 rt::IOCRConfig input;
105
107 input.referenceIOCR = 1;
108 input.frameId = 0xC001;
109 input.sendClockFactor = 1;
110 input.reductionRatio = 1;
111 input.watchdogFactor = 3;
112 input.dataLength = 40;
113
114 input.objects.push_back(
116 .slot = 1,
117 .subslot = 1,
118 .frameOffset = 0,
119 .dataLength = 4,
120 .offsetIOPS = 4});
121
122 rt::IOCRConfig output;
123
125 output.referenceIOCR = 2;
126 output.frameId = 0xC000;
127 output.sendClockFactor = 32;
128 output.reductionRatio = 32;
129 output.watchdogFactor = 3;
130 output.dataLength = 40;
131
132 output.objects.push_back(
134 .slot = 1,
135 .subslot = 1,
136 .frameOffset = 0,
137 .dataLength = 4,
138 .offsetIOPS = 4});
139 asio::io_context ioContext;
140
141 return std::make_unique<CyclicController>(
142 ioContext,
143 "eth0",
144 kControllerMac,
145 kDeviceMac,
146 input,
147 output);
148 }
149};
150
152TEST_F(ReceivedIopsTest, GoodIopsDeliversInputData)
153{
154 const Bytes data{0xAA, 0xBB, 0xCC, 0xDD};
155
156 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, kIopsGood)));
157
158 const auto inputData = controller->GetInputData(1, 1);
159
160 ASSERT_TRUE(inputData.has_value());
161
162 EXPECT_EQ(*inputData, data);
163
164 EXPECT_TRUE(controller->IsInputGood(1, 1));
165
166 const auto status = controller->GetInputStatus(1, 1);
167
168 ASSERT_TRUE(status.has_value());
169
170 EXPECT_EQ(*status, kIopsGood);
171}
172
179TEST_F(ReceivedIopsTest, DISABLED_BadIopsWithholdsStaleInputData)
180{
181 const Bytes data{0xAA, 0xBB, 0xCC, 0xDD};
182
183 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, kIopsGood)));
184
185 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, 0x00)));
186
187 EXPECT_FALSE(controller->GetInputData(1, 1).has_value());
188
189 EXPECT_FALSE(controller->IsInputGood(1, 1));
190
191 const auto rawData = controller->GetInputData(1, 1, true);
192
193 ASSERT_TRUE(rawData.has_value());
194
195 EXPECT_EQ(*rawData, data);
196}
197
203TEST_F(ReceivedIopsTest, StatusCallbackFiresOnlyOnTransitions)
204{
205 struct Event
206 {
207 std::uint16_t slot;
208 std::uint16_t subslot;
209 std::uint8_t iops;
210
211 bool operator==(const Event&) const = default;
212 };
213
214 std::vector<Event> events;
215
216 controller->OnInputStatus([&events](std::uint16_t slot, std::uint16_t subslot, std::uint8_t iops)
217 {
218 events.push_back({.slot = slot, .subslot = subslot, .iops = iops});
219 });
220
221 const Bytes data{0x01, 0x02, 0x03, 0x04};
222
223 const Bytes good = BuildInputFrame(MakePayload(data, 0x80));
224
225 const Bytes bad = BuildInputFrame(MakePayload(data, 0x00));
226
227 InvokeProcessInputFrame(good);
228 InvokeProcessInputFrame(good);
229
230 InvokeProcessInputFrame(bad);
231 InvokeProcessInputFrame(bad);
232
233 ASSERT_EQ(events.size(), 2U);
234
235 EXPECT_EQ(events[0].slot, 1U);
236 EXPECT_EQ(events[0].subslot, 1U);
237 EXPECT_EQ(events[0].iops, 0x80U);
238
239 EXPECT_EQ(events[1].slot, 1U);
240 EXPECT_EQ(events[1].subslot, 1U);
241 EXPECT_EQ(events[1].iops, 0x00U);
242}
243
245TEST_F(ReceivedIopsTest, InputCallbackSkipsBadData)
246{
247 std::vector<Bytes> delivered;
248
249 controller->OnInput([&delivered](std::uint16_t, std::uint16_t, const Bytes& data)
250 {
251 delivered.push_back(data);
252 });
253
254 InvokeProcessInputFrame(BuildInputFrame(MakePayload(Bytes{0x01, 0x02, 0x03, 0x04}, 0x80)));
255 InvokeProcessInputFrame(BuildInputFrame(MakePayload(Bytes{0x09, 0x09, 0x09, 0x09}, 0x00)));
256
257 ASSERT_EQ(delivered.size(), 1U);
258
259 EXPECT_EQ(delivered[0], Bytes({0x01, 0x02, 0x03, 0x04}));
260}
261
267TEST_F(ReceivedIopsTest, DISABLED_LowerIopsBitsDoNotAffectGoodState)
268{
269 auto controller = MakeController();
270
271 const Bytes data{0x01, 0x02, 0x03, 0x04};
272
273 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, 0x81)));
274
275 EXPECT_TRUE(controller->IsInputGood(1, 1));
276
277 const auto inputData = controller->GetInputData(1, 1);
278
279 ASSERT_TRUE(inputData.has_value());
280
281 EXPECT_EQ(*inputData, data);
282}
283} // namespace profinet::cyclic
Test fixture for received cyclic input IOPS behavior.
std::unique_ptr< CyclicController > controller
The controller under test used to verify the private functionality.
Bytes MakePayload(const Bytes &data, std::uint8_t iops) const
Build a cyclic payload containing input data and IOPS.
void InvokeProcessInputFrame(const Bytes &data)
static std::unique_ptr< profinet::cyclic::CyclicController > MakeController()
Construct the cyclic controller used by the tests.
Bytes BuildInputFrame(const Bytes &payload) const
Build an untagged input RT Ethernet frame.
Declares the PROFINET RTC1 cyclic IO controller and process-data exchange API.
TEST_F(CyclicTest, CyclicStateToStringCoversAllStates)
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
constexpr bool operator==(BlockType lhs, std::uint16_t rhs) noexcept
Checks equality between a BlockType enum and a raw uint16_t value.
Definition indices.h:326
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