10#include <gtest/gtest.h>
23using namespace std::chrono_literals;
35 return MacAddress{0x02, 0x00, 0x00, 0x00, 0x00, 0x01};
38DCPDeviceDescription MakeDeviceInfo()
40 DCPDeviceDescription info(
43 info.ip =
"127.0.0.1";
44 info.vendorHigh = 0x01;
45 info.vendorLow = 0x3F;
46 info.deviceHigh = 0x00;
47 info.deviceLow = 0x02;
51IOCRSetup MakeAumaLikeSetup()
54 setup.sendClockFactor = 128;
55 setup.reductionRatio = 8;
56 setup.watchdogFactor = 3;
57 setup.dataHoldFactor = 3;
59 setup.slots.push_back({
62 .moduleIdent = 0x80010000U,
63 .submoduleIdent = 0x00000000U,
67 setup.slots.push_back({
70 .moduleIdent = 0x80000000U,
71 .submoduleIdent = 0x00002200U,
75 setup.slots.push_back({
78 .moduleIdent = 0x80010000U,
79 .submoduleIdent = 0x10001000U,
86class VirtualProfinetRpcDeviceTest :
public ::testing::Test
91 configuration.rpcPort =
static_cast<std::uint16_t
>(
92 35000U +
static_cast<std::uint16_t
>(::testing::UnitTest::GetInstance()
95 configuration.applicationReadyDelay = 5ms;
96 device = std::make_unique<VirtualProfinetRpcDevice>(configuration);
100 void TearDown()
override
105 VirtualProfinetDeviceConfiguration configuration{};
106 std::unique_ptr<VirtualProfinetRpcDevice> device;
109TEST_F(VirtualProfinetRpcDeviceTest, CompleteAumaStartupReachesApplicationReady)
111 RPCConOptions options;
112 options.timeoutSec = 1.0;
113 options.rpcPort = configuration.rpcPort;
115 RPCCon connection(MakeDeviceInfo(), options);
118 connection.Connect(ControllerMac(),
true, MakeAumaLikeSetup()));
120 EXPECT_TRUE(connection.IsConnected());
121 EXPECT_TRUE(device->WaitForState(
122 VirtualProfinetDeviceState::Connected, 500ms));
124 ASSERT_NO_THROW(connection.PrmBegin());
125 EXPECT_TRUE(device->WaitForState(
126 VirtualProfinetDeviceState::Parameterizing, 500ms));
128 ASSERT_NO_THROW(connection.PrmEnd());
129 EXPECT_TRUE(device->WaitForState(
130 VirtualProfinetDeviceState::Parameterized, 500ms));
132 ASSERT_NO_THROW(connection.ApplicationReady(2.0));
134 EXPECT_TRUE(device->WaitForState(
135 VirtualProfinetDeviceState::ApplicationReady, 500ms));
137 EXPECT_EQ(device->ConnectCount(), 1U);
138 EXPECT_EQ(device->PrmBeginCount(), 1U);
139 EXPECT_EQ(device->PrmEndCount(), 1U);
140 EXPECT_EQ(device->ApplicationReadyRequestCount(), 1U);
141 EXPECT_EQ(device->ApplicationReadyResponseCount(), 1U);
143 EXPECT_TRUE(device->SawInputIocr());
144 EXPECT_TRUE(device->SawOutputIocr());
145 EXPECT_TRUE(device->SawExpectedSubmodules());
146 EXPECT_TRUE(device->SawAlarmCr());
148 connection.Disconnect();
150 EXPECT_TRUE(device->WaitForState(
151 VirtualProfinetDeviceState::Released, 500ms));
152 EXPECT_EQ(device->ReleaseCount(), 1U);
155TEST_F(VirtualProfinetRpcDeviceTest, ConnectWithoutCyclicDoesNotRequireIocrs)
157 RPCConOptions options;
158 options.timeoutSec = 1.0;
159 options.rpcPort = configuration.rpcPort;
161 RPCCon connection(MakeDeviceInfo(), options);
163 ASSERT_NO_THROW(connection.Connect(ControllerMac(),
false));
165 EXPECT_TRUE(device->WaitForState(
166 VirtualProfinetDeviceState::Connected, 500ms));
167 EXPECT_FALSE(device->SawInputIocr());
168 EXPECT_FALSE(device->SawOutputIocr());
171TEST_F(VirtualProfinetRpcDeviceTest, PrmBeginBeforeConnectIsRejectedByDevice)
173 RPCConOptions options;
174 options.timeoutSec = 0.2;
175 options.rpcPort = configuration.rpcPort;
180 EXPECT_EQ(device->PrmBeginCount(), 0U);
181 EXPECT_EQ(device->State(), VirtualProfinetDeviceState::Idle);
184TEST_F(VirtualProfinetRpcDeviceTest, MissingExpectedSubmodulesRejectsCyclicConnect)
186 configuration.requireExpectedSubmodules =
true;
188 device = std::make_unique<VirtualProfinetRpcDevice>(configuration);
194 EXPECT_EQ(device->State(), VirtualProfinetDeviceState::Idle);
197TEST_F(VirtualProfinetRpcDeviceTest, ApplicationReadyIsNotAcceptedBeforePrmEnd)
199 RPCConOptions options;
200 options.timeoutSec = 1.0;
201 options.rpcPort = configuration.rpcPort;
203 RPCCon connection(MakeDeviceInfo(), options);
206 connection.Connect(ControllerMac(),
true, MakeAumaLikeSetup()));
208 EXPECT_TRUE(device->WaitForState(
209 VirtualProfinetDeviceState::Connected, 500ms));
213 std::this_thread::sleep_for(50ms);
215 EXPECT_EQ(device->ApplicationReadyRequestCount(), 0U);
216 EXPECT_EQ(device->State(), VirtualProfinetDeviceState::Connected);
219TEST_F(VirtualProfinetRpcDeviceTest, DeviceReportsItsLifecycleClearly)
225 EXPECT_EQ(device->State(), VirtualProfinetDeviceState::Idle);
226 EXPECT_EQ(device->ConnectCount(), 0U);
227 EXPECT_EQ(device->PrmBeginCount(), 0U);
228 EXPECT_EQ(device->PrmEndCount(), 0U);
231TEST_F(VirtualProfinetRpcDeviceTest, StopUnblocksWorkerWaitingForRpc)
233 ASSERT_EQ(device->State(), VirtualProfinetDeviceState::Idle);
235 ASSERT_NO_THROW(device->Stop());
237 EXPECT_EQ(device->State(), VirtualProfinetDeviceState::Idle);
240TEST_F(VirtualProfinetRpcDeviceTest, StopCancelsPendingApplicationReady)
242 configuration.applicationReadyDelay = 5s;
Parsed PROFINET device information from a DCP response.
Connection to a PROFINET IO-Device: establishes an AR (Application Relationship) and performs acyclic...
Stateful UDP PROFINET IO-Device protocol simulator.
VirtualProfinetDeviceState
Observable Application Relation state of the virtual device.
const char * ToString(VirtualAumaScenario scenario) noexcept
Convert a virtual AUMA scenario into its stable CLI spelling.
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
MacAddress String2Mac(const std::string &macStr)
Parse a colon-separated MAC address string.
PROFINET DCE/RPC protocol structures and IO-Device connection API.
Configuration for establishing cyclic IO alongside an AR.
Configuration options for an RPC connection.
Configuration for a controller-facing virtual PROFINET device.
TEST_F(AlarmItemTest, ParseChannelDiagnosis)
Stateful PROFINET IO-Device DCE/RPC lifecycle simulator.