15#include <gtest/gtest.h>
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;
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));
94 frame.insert(
frame.end(), rtBytes.begin(), rtBytes.end());
141 return std::make_unique<CyclicController>(
154 const Bytes data{0xAA, 0xBB, 0xCC, 0xDD};
156 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, kIopsGood)));
158 const auto inputData =
controller->GetInputData(1, 1);
160 ASSERT_TRUE(inputData.has_value());
162 EXPECT_EQ(*inputData, data);
166 const auto status =
controller->GetInputStatus(1, 1);
168 ASSERT_TRUE(status.has_value());
170 EXPECT_EQ(*status, kIopsGood);
181 const Bytes data{0xAA, 0xBB, 0xCC, 0xDD};
183 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, kIopsGood)));
185 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, 0x00)));
187 EXPECT_FALSE(
controller->GetInputData(1, 1).has_value());
191 const auto rawData =
controller->GetInputData(1, 1,
true);
193 ASSERT_TRUE(rawData.has_value());
195 EXPECT_EQ(*rawData, data);
208 std::uint16_t subslot;
211 bool operator==(
const Event&)
const =
default;
214 std::vector<Event> events;
216 controller->OnInputStatus([&events](std::uint16_t slot, std::uint16_t subslot, std::uint8_t iops)
218 events.push_back({.slot = slot, .subslot = subslot, .iops = iops});
221 const Bytes data{0x01, 0x02, 0x03, 0x04};
223 const Bytes good = BuildInputFrame(MakePayload(data, 0x80));
225 const Bytes bad = BuildInputFrame(MakePayload(data, 0x00));
227 InvokeProcessInputFrame(good);
228 InvokeProcessInputFrame(good);
230 InvokeProcessInputFrame(bad);
231 InvokeProcessInputFrame(bad);
233 ASSERT_EQ(events.size(), 2U);
235 EXPECT_EQ(events[0].slot, 1U);
236 EXPECT_EQ(events[0].subslot, 1U);
237 EXPECT_EQ(events[0].iops, 0x80U);
239 EXPECT_EQ(events[1].slot, 1U);
240 EXPECT_EQ(events[1].subslot, 1U);
241 EXPECT_EQ(events[1].iops, 0x00U);
247 std::vector<Bytes> delivered;
249 controller->OnInput([&delivered](std::uint16_t, std::uint16_t,
const Bytes& data)
251 delivered.push_back(data);
254 InvokeProcessInputFrame(BuildInputFrame(MakePayload(
Bytes{0x01, 0x02, 0x03, 0x04}, 0x80)));
255 InvokeProcessInputFrame(BuildInputFrame(MakePayload(
Bytes{0x09, 0x09, 0x09, 0x09}, 0x00)));
257 ASSERT_EQ(delivered.size(), 1U);
259 EXPECT_EQ(delivered[0],
Bytes({0x01, 0x02, 0x03, 0x04}));
271 const Bytes data{0x01, 0x02, 0x03, 0x04};
273 InvokeProcessInputFrame(BuildInputFrame(MakePayload(data, 0x81)));
277 const auto inputData =
controller->GetInputData(1, 1);
279 ASSERT_TRUE(inputData.has_value());
281 EXPECT_EQ(*inputData, data);
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.
constexpr std::uint16_t IOCR_TYPE_OUTPUT
IOCR direction: Controller -> Device.
constexpr MacAddress kControllerMac
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
constexpr bool operator==(BlockType lhs, std::uint16_t rhs) noexcept
Checks equality between a BlockType enum and a raw uint16_t value.
IOCR configuration derived from AR setup: timing parameters and IO object mappings needed for cyclic ...
std::vector< IODataObject > objects
IO data objects carried in this IOCR's cyclic frame.
std::uint16_t dataLength
Total cyclic data length in bytes.
std::uint16_t typeIOCR
IOCR direction: 1=Input, 2=Output.
std::uint16_t referenceIOCR
IOCR reference assigned during AR establishment.
std::uint16_t sendClockFactor
Send clock base factor (31.25us units).
std::uint16_t frameId
Frame ID assigned to this IOCR.
std::uint16_t reductionRatio
Reduction ratio relative to the send clock.
std::uint16_t watchdogFactor
Watchdog factor (missed-frame tolerance before a timeout fault).
Single IO data object within the C_SDU: one piece of process data mapped to a slot/subslot,...
std::uint16_t slot
Slot number.
A single PROFINET Real-Time cyclic frame: Frame ID + C_SDU payload + cycle counter/status trailer.
std::uint8_t dataStatus
Data status bitmask (see DATA_STATUS_* constants).
std::uint8_t transferStatus
Transfer status (0 = OK).
Bytes ToBytes() const
Serialize this frame back to raw bytes.
std::uint16_t frameId
Frame ID identifying which IOCR this frame belongs to.
std::uint16_t cycleCounter
Cycle counter, incremented each transmission.
static constexpr std::uint8_t DATA_RUN
Alias for DATA_STATUS_PROVIDER_RUN, for readability at call sites.
static constexpr std::uint8_t DATA_VALID
Alias for DATA_STATUS_VALID, for readability at call sites.
Bytes payload
C_SDU payload: process data plus IOPS/IOCS trailers.
std::unique_ptr< cyclic::CyclicController > controller
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.