4#include <gtest/gtest.h>
32void PopulateLab(VirtualProfinetLab& lab,
33 const std::size_t count,
34 const gsdml::GSDMLDevice& auma,
35 const gsdml::GSDMLDevice& phoenix)
37 for (std::size_t index = 0; index < count; ++index)
39 const std::string valveName =
"valve" + std::to_string(index + 1U);
40 const std::string plcName =
"plc" + std::to_string(index + 1U);
42 lab.AddAumaValve(valveName,
45 std::move(iocrs.input),
46 std::move(iocrs.output));
47 lab.AddPhoenixPlc(plcName, phoenix,
MakeIndexedMac(0x10U, index + 1U));
55 std::move(controllerIocrs.input),
56 std::move(controllerIocrs.output));
63TEST(VirtualProfinetLabTests, RunsMultipleIndependentValvePlcPairs)
65 constexpr std::size_t kInstanceCount = 4;
66 const auto auma = MakeAumaGsdml();
67 const auto phoenix = MakePhoenixGsdml();
69 PopulateLab(lab, kInstanceCount, auma, phoenix);
71 EXPECT_EQ(lab.
ValveNames().size(), kInstanceCount);
72 EXPECT_EQ(lab.
PlcNames().size(), kInstanceCount);
73 EXPECT_EQ(lab.
Bus().
EndpointCount(), kInstanceCount + (2U * kInstanceCount));
75 for (std::size_t index = 0; index < kInstanceCount; ++index)
77 const std::string plc =
"plc" + std::to_string(index + 1U);
78 const std::string valve =
"valve" + std::to_string(index + 1U);
82 ASSERT_TRUE(WaitUntil([&lab]
84 for (std::size_t index = 0; index < kInstanceCount; ++index)
86 if (lab.
Valve(
"valve" + std::to_string(index + 1U)).
Position() == 0U)
92 }, std::chrono::milliseconds(500)));
94 for (std::size_t index = 0; index < kInstanceCount; ++index)
96 const std::string plc =
"plc" + std::to_string(index + 1U);
97 const std::string valve =
"valve" + std::to_string(index + 1U);
98 const auto value = lab.
Plc(plc).
ReadInput(valve,
"T_ID_ACTPOS_E2");
99 ASSERT_TRUE(value.has_value());
100 EXPECT_GT(*value, 0.0);
108TEST(VirtualProfinetLabTests, DuplicateNamesAreRejected)
110 const auto auma = MakeAumaGsdml();
111 const auto phoenix = MakePhoenixGsdml();
113 auto first = MakeTestAumaIocrs(auma);
114 lab.
AddAumaValve(
"valve", auma, MakeIndexedMac(0x20U, 1U),
115 std::move(first.input), std::move(first.output));
117 auto second = MakeTestAumaIocrs(auma);
118 EXPECT_THROW(lab.
AddAumaValve(
"valve", auma, MakeIndexedMac(0x20U, 2U),
119 std::move(second.input), std::move(second.output)),
120 std::invalid_argument);
122 lab.
AddPhoenixPlc(
"plc", phoenix, MakeIndexedMac(0x10U, 1U));
123 EXPECT_THROW(lab.
AddPhoenixPlc(
"plc", phoenix, MakeIndexedMac(0x10U, 2U)),
124 std::invalid_argument);
128TEST(VirtualProfinetLabTests, DuplicateMacAddressesAreRejectedAcrossNodeTypes)
130 const auto auma = MakeAumaGsdml();
131 const auto phoenix = MakePhoenixGsdml();
133 auto iocrs = MakeTestAumaIocrs(auma);
134 const MacAddress mac = MakeIndexedMac(0x20U, 1U);
136 std::move(iocrs.input), std::move(iocrs.output));
138 EXPECT_THROW(lab.
AddPhoenixPlc(
"plc1", phoenix, mac), std::invalid_argument);
140 EXPECT_TRUE(lab.
PlcNames().empty());
144TEST(VirtualProfinetLabTests, ScenarioChangeUpdatesDashboard)
146 const auto auma = MakeAumaGsdml();
147 const auto phoenix = MakePhoenixGsdml();
149 auto iocrs = MakeTestAumaIocrs(auma);
150 lab.
AddAumaValve(
"valve", auma, MakeIndexedMac(0x20U, 1U),
151 std::move(iocrs.input), std::move(iocrs.output));
152 lab.
AddPhoenixPlc(
"plc", phoenix, MakeIndexedMac(0x10U, 1U));
153 auto controllerIocrs = MakeTestAumaIocrs(auma);
158 std::move(controllerIocrs.input),
159 std::move(controllerIocrs.output));
165 EXPECT_NE(report.find(
"VIRTUAL PROFINET LAB"), std::string::npos);
166 EXPECT_NE(report.find(
"scenario=jammed"), std::string::npos);
Keeps an Asio I/O context running on a dedicated worker thread.
std::size_t EndpointCount() const
Return the number of currently connected socket endpoints.
VirtualAumaScenario Scenario() const noexcept
Return the current scenario.
std::uint16_t Position() const noexcept
Return the current simulated position.
std::optional< double > ReadInput(const std::string &valveName, std::string_view signalTextId) const
Read a typed AUMA input signal using the production controller API.
void SetOutput(const std::string &valveName, std::string_view signalTextId, double value)
Set a typed AUMA actuator signal.
Owns one shared Ethernet bus plus any number of virtual PLCs and AUMA valves.
void SetAumaScenario(const std::string &valveName, VirtualAumaScenario scenario)
Set one valve scenario.
std::vector< std::string > PlcNames() const
Return all PLC names.
VirtualPhoenixPlc & AddPhoenixPlc(std::string name, const gsdml::GSDMLDevice &gsdml, MacAddress controllerMac)
Add one Phoenix AXC F 1252 controller instance.
std::string StatusReport() const
Render a compact multi-device status dashboard.
InMemoryEthernetBus & Bus() noexcept
Return the shared virtual bus.
VirtualPhoenixPlc & Plc(const std::string &name)
Find a PLC by name.
void Stop()
Stop all devices and controllers.
VirtualAumaValve & Valve(const std::string &name)
Find a valve by name.
void Connect(asio::io_context &ioContext, const std::string &plcName, const std::string &valveName, rt::IOCRConfig inputIocr, rt::IOCRConfig outputIocr, int maxConsecutiveTimeouts=cyclic::DEFAULT_MAX_CONSECUTIVE_TIMEOUTS)
Connect a PLC to an AUMA valve.
VirtualAumaValve & AddAumaValve(std::string name, const gsdml::GSDMLDevice &gsdml, MacAddress deviceMac, rt::IOCRConfig inputIocr, rt::IOCRConfig outputIocr, VirtualAumaValveConfiguration config={})
Add one AUMA valve instance.
std::vector< std::string > ValveNames() const
Return all valve names.
MacAddress MakeIndexedMac(const std::uint8_t prefix, const std::size_t index)
Allocate a deterministic locally administered MAC address for an instance index.
gsdml::GSDMLDevice MakePhoenixGsdml()
Build a compact Phoenix GSDML model with four-byte DI/DO process data.
VirtualAumaIocrConfiguration MakeTestAumaIocrs(const gsdml::GSDMLDevice &device)
Build matching compact RTC1 IOCR configurations for the AUMA test model.
gsdml::GSDMLDevice MakeAumaGsdml()
Build a compact GSDML model using the real AUMA signal identifiers.
bool WaitUntil(const std::function< bool()> &predicate, std::chrono::milliseconds timeout=std::chrono::milliseconds(500))
Wait until a predicate becomes true or the timeout expires.
@ Jammed
Valve accepts commands but the mechanical position does not change.
TEST(InMemoryEthernetRoutingTests, UnicastReachesOnlyMatchingEndpoint)
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
Multi-instance in-memory PROFINET laboratory.
Shared synthetic GSDML and timing helpers for virtual PROFINET tests.