27#include <gtest/gtest.h>
44 transport = std::make_unique<TestRpcTransport>();
46 ctransport = std::make_unique<TestRpcTransport>();
62 info.
name =
"test-device";
63 info.deviceHigh = 0x12;
64 info.deviceLow = 0x34;
65 info.vendorHigh = 0x56;
66 info.vendorLow = 0x78;
77 return std::ranges::all_of(uuid, [](
const std::uint8_t
byte)
139 for (
const auto& floor : floors)
188 std::uint16_t port,
const std::array<std::uint8_t, 4>& ipAddress)
205 return MakeEpmTower({interfaceFloor, udpFloor, portFloor, ipFloor});
213 bool IsZeroUuid(
const std::array<std::uint8_t, uuidLenght>& uuid)
215 return std::ranges::all_of(uuid, [](std::uint8_t value)
233 wire::PutLittleEndian<std::uint32_t>(body, 0);
234 wire::PutLittleEndian<std::uint32_t>(body, 1);
236 wire::PutLittleEndian<std::uint32_t>(body, 1);
237 wire::PutLittleEndian<std::uint32_t>(body, 0);
238 wire::PutLittleEndian<std::uint32_t>(body, 1);
242 wire::PutLittleEndian<std::uint32_t>(body, 0);
250 while ((body.size() & 3U) != 0U)
257 body.insert(body.end(), tower.begin(), tower.end());
259 while ((body.size() & 3U) != 0U)
277 response.push_back(4);
279 response.push_back(0x00);
280 response.push_back(0x00);
282 response.push_back(0x10);
283 response.push_back(0x00);
284 response.push_back(0x00);
285 response.push_back(0x00);
287 response.insert(response.end(), 16, 0x00);
288 response.insert(response.end(), 16, 0x00);
289 response.insert(response.end(), 16, 0x00);
291 wire::PutLittleEndian<std::uint32_t>(response, 0);
292 wire::PutLittleEndian<std::uint32_t>(response, 3);
293 wire::PutLittleEndian<std::uint32_t>(response, 0);
295 wire::PutLittleEndian<std::uint16_t>(response,
EPM_LOOKUP);
299 wire::PutLittleEndian<std::uint16_t>(response, 0);
300 response.push_back(0);
301 response.push_back(0);
314 std::array<std::uint8_t, 4096>
buffer{};
315 asio::ip::udp::endpoint sender;
317 const std::size_t received = server.receive_from(asio::buffer(
buffer), sender);
319 request.assign(
buffer.begin(),
buffer.begin() + received);
321 server.send_to(asio::buffer(response), sender);
341 if ((
payload.size() % 2U) != 0U)
404 payload.insert(
payload.end(), additionalBlocks.begin(), additionalBlocks.end());
414 std::map<dcp::BlockKey, std::vector<std::uint8_t>>
MakeStationBlocks(
const std::string& stationName)
419 std::vector<std::uint8_t>(stationName.begin(), stationName.end()),
452 return conn->ParseAlarmCrResponse(responseData);
479 std::ranges::copy(headerBytes, response.
blockHeader.begin());
564 void Open(
const std::string& address, std::uint16_t port,
bool isListener =
false)
override
606 void SendTo(
const Bytes& data,
const asio::ip::udp::endpoint& targetEndpoint)
override
624 throw RPCError(
"TestRpcTransport: no response configured");
745 std::ranges::copy(blockBytes, iod.
blockHeader.begin());
764 std::size_t size = 6;
797 return Bytes(size, 0);
857 data.insert(data.end(), body.begin(), body.end());
927 return conn->SendReceive(request);
935 conn->arUuid.fill(
byte);
940 conn->arUuid = bytes;
945 conn->sessionKey = byte;
948 std::uint8_t vendorHigh, std::uint8_t vendorLow)
950 conn->info.deviceHigh = deviceHigh;
951 conn->info.deviceLow = deviceLow;
952 conn->info.vendorHigh = vendorHigh;
953 conn->info.vendorLow = vendorLow;
964 conn->liveMonotonic = time;
968 conn->CheckTimeout();
980 bool waitResponse =
true,
const Bytes& subBlocks = {})
982 return conn->SendControl(blockType, controlCommand, waitResponse, subBlocks);
1002 std::uint32_t sequenceNumber, std::uint16_t operationNumber,
const Bytes&
payload)
1018 std::array<std::uint8_t, uuidLenght> objectUuid{};
1019 std::array<std::uint8_t, uuidLenght> interfaceUuid{};
1020 std::array<std::uint8_t, uuidLenght> activityUuid{};
1022 for (std::size_t i = 0U; i <
uuidLenght; ++i)
1024 objectUuid[i] =
static_cast<std::uint8_t
>(0x10U + i);
1025 interfaceUuid[i] =
static_cast<std::uint8_t
>(0x20U + i);
1026 activityUuid[i] =
static_cast<std::uint8_t
>(0x30U + i);
1034 data.insert(data.end(), 4U, 0U);
1074 std::ranges::copy(blockBytes, control.
blockHeader.begin());
1095 const auto blockLength =
static_cast<std::uint32_t
>(block.size());
1099 wire::PutLittleEndian<std::uint32_t>(nrd, 0);
1102 wire::PutLittleEndian<std::uint32_t>(nrd, 0);
1114 nrd.insert(nrd.end(), block.begin(), block.end());
1150 EXPECT_EQ(back,
"dea00001-6c97-11d1-8271-00a02442df7d");
1187 EXPECT_EQ(ep.
InterfaceName(),
"Unknown(11111111-2222-3333-4444-555555555555)");
1210 EXPECT_EQ(setup.
CycleTimeUs(),
static_cast<std::uint32_t
>(32000));
1224 EXPECT_FALSE(warnings.empty());
1234 const auto warnings = setup.
Validate();
1236 EXPECT_NE(std::ranges::find(warnings,
"No IO slots configured"), warnings.end());
1246 const auto warnings = setup.
Validate();
1248 EXPECT_NE(std::ranges::find(warnings,
"Watchdog factor too low (use 6+)"), warnings.end());
1260 setup.
slots.push_back({});
1262 const auto warnings = setup.
Validate();
1264 EXPECT_TRUE(warnings.empty());
1275 const auto rpc = CreateRpc(
payload);
1277 EXPECT_EQ(rpc.version, 4);
1279 EXPECT_EQ(rpc.flags1, 0x20);
1280 EXPECT_EQ(rpc.interfaceVersion, 1);
1282 EXPECT_EQ(rpc.interfaceHint, 0xFFFF);
1283 EXPECT_EQ(rpc.activityHint, 0xFFFF);
1284 EXPECT_EQ(rpc.lengthOfBody,
payload.size());
1285 EXPECT_EQ(rpc.payload,
payload);
1295 EXPECT_EQ(
ToHex(deviceUuid.data(), deviceUuid.size()),
"dea000016c9711d1827100a02442df7d");
1310 .moduleIdent = 0x80010000,
1311 .submoduleIdent = 0x00000001,
1318 .moduleIdent = 0x80010000,
1319 .submoduleIdent = 0x00000010,
1326 .moduleIdent = 0x80010000,
1327 .submoduleIdent = 0x00000010,
1334 .moduleIdent = 0x80010000,
1335 .submoduleIdent = 0x00000010,
1342 .moduleIdent = 0x80010000,
1343 .submoduleIdent = 0x00000002,
1350 .moduleIdent = 0x80010000,
1351 .submoduleIdent = 0x00000003,
1358 .moduleIdent = 0x80010000,
1359 .submoduleIdent = 0x00000003,
1366 .moduleIdent = 0x00008000,
1367 .submoduleIdent = 0x00002200,
1374 .moduleIdent = 0x00008100,
1375 .submoduleIdent = 0x10001000,
1388 EXPECT_EQ(inputHeader.iocrType, 1);
1389 EXPECT_EQ(outputHeader.iocrType, 2);
1391 EXPECT_EQ(inputHeader.dataLength, 43);
1392 EXPECT_EQ(outputHeader.dataLength, 40);
1394 EXPECT_EQ(inputHeader.numberOfApis, 1);
1395 EXPECT_EQ(outputHeader.numberOfApis, 1);
1401 const auto info = MakeRpcTestDeviceInfo();
1405 const auto& arUuid = conn.
ArUuid();
1407 EXPECT_FALSE(IsAllZeroUuid(arUuid));
1415 const auto info = MakeRpcTestDeviceInfo();
1420 EXPECT_FALSE(IsAllZeroUuid(first.
ArUuid()));
1421 EXPECT_FALSE(IsAllZeroUuid(second.
ArUuid()));
1431 const auto rpc = CreateRpc({});
1433 EXPECT_FALSE(IsAllZeroUuid(conn->ArUuid()));
1434 EXPECT_FALSE(IsAllZeroUuid(rpc.activityUuid));
1435 EXPECT_NE(conn->ArUuid(), rpc.activityUuid);
1442 const auto info = MakeRpcTestDeviceInfo();
1446 EXPECT_NE(conn.
SessionKey(),
static_cast<std::uint16_t
>(0));
1453 const auto info = MakeRpcTestDeviceInfo();
1455 constexpr int connectionCount = 32;
1457 for (
int i = 0; i < connectionCount; ++i)
1461 EXPECT_NE(conn.
SessionKey(),
static_cast<std::uint16_t
>(0));
1468 const auto rpc = CreateRpc({});
1470 EXPECT_EQ(
ToHex(rpc.objectUuid.data(), rpc.objectUuid.size()),
"dea000006c9711d18271000112345678");
1477 InvokeSetRPCConInfo(0xA1, 0xB2, 0xC3, 0xD4);
1479 const auto rpc = CreateRpc({});
1481 EXPECT_EQ(
ToHex(rpc.objectUuid.data(), rpc.objectUuid.size()),
"dea000006c9711d182710001a1b2c3d4");
1488 InvokeSetRPCConInfo(0xAA, 0xBB, 0xCC, 0xDD);
1489 const auto rpc = CreateRpc({});
1491 const std::string uuidHex =
ToHex(rpc.objectUuid.data(), rpc.objectUuid.size());
1493 EXPECT_EQ(uuidHex.substr(0, 20),
"dea000006c9711d18271");
1494 EXPECT_EQ(uuidHex.substr(20),
"0001aabbccdd");
1501 const auto rpc = CreateRpc({});
1503 EXPECT_EQ(rpc.objectUuid.size(),
uuidLenght);
1528 endpoint.
interfaceUuid =
"dea00003-6c97-11d1-8271-00a02442df7d";
1537 endpoint.
interfaceUuid =
"dea00004-6c97-11d1-8271-00a02442df7d";
1539 EXPECT_EQ(endpoint.
InterfaceName(),
"PNIO-ParameterServer");
1555 endpoint.
interfaceUuid =
"11111111-2222-3333-4444-555555555555";
1557 EXPECT_EQ(endpoint.
InterfaceName(),
"Unknown(11111111-2222-3333-4444-555555555555)");
1563 const std::array<std::pair<std::string_view, std::string_view>, 6> cases = {{{
UUID_PNIO_DEVICE,
"PNIO-Device"}, {
UUID_PNIO_CONTROLLER,
"PNIO-Controller"}, {
UUID_PNIO_SUPERVISOR,
"PNIO-Supervisor"}, {
UUID_PNIO_PARAMETER_SERVER,
"PNIO-ParameterServer"}, {
UUID_EPM_V4,
"EPM"}, {
"DEA00006-6C97-11D1-8271-00A02442DF7D",
"Unknown(DEA00006-6C97-11D1-8271-00A02442DF7D)"}}};
1565 for (
const auto& [uuid, expected] : cases)
1578 EXPECT_FALSE(
ParseEpmTower({0x01, 0x00, 0x00}).has_value());
1584 const Bytes tower = {0x01, 0x00, 0x01};
1592 const Bytes tower = {0x01, 0x00, 0x05, 0x00, 0x0D, 0x01};
1600 const Bytes tower = {0x01, 0x00, 0x01, 0x00, 0x0A};
1608 const Bytes tower = {0x01, 0x00, 0x01, 0x00, 0x0A, 0x04, 0x00, 0x01};
1616 const Bytes interfaceFloor = MakeEpmTowerFloor(
1618 MakeInterfaceTowerRhs(7));
1620 const Bytes emptyFloor = MakeEpmTowerFloor({}, {});
1622 const Bytes tower = MakeEpmTower({
1629 ASSERT_TRUE(result.has_value());
1632 EXPECT_EQ(result->interfaceVersionMajor, 3);
1633 EXPECT_EQ(result->interfaceVersionMinor, 7);
1642 ASSERT_EQ(lhs.size(), 19U);
1644 EXPECT_EQ(lhs[0], 0x0D);
1648 EXPECT_TRUE(std::equal(uuidBytes.begin(), uuidBytes.end(), lhs.begin() + 1));
1650 EXPECT_EQ(lhs[17], 0x03);
1651 EXPECT_EQ(lhs[18], 0x00);
1658 const Bytes interfaceFloor =
1661 MakeInterfaceTowerRhs(7));
1664 MakeEpmTower({interfaceFloor});
1669 ASSERT_TRUE(result.has_value());
1672 EXPECT_EQ(result->interfaceVersionMajor, 3);
1673 EXPECT_EQ(result->interfaceVersionMinor, 7);
1679 const Bytes emptyFloor = MakeEpmTowerFloor({}, {});
1681 ASSERT_EQ(emptyFloor, (
Bytes{0x00, 0x00, 0x00, 0x00}));
1688 const Bytes emptyFloor = MakeEpmTowerFloor({}, {});
1690 const Bytes interfaceFloor = MakeEpmTowerFloor(
1692 MakeInterfaceTowerRhs(7));
1694 const Bytes tower = MakeEpmTower({emptyFloor, interfaceFloor});
1696 ASSERT_GE(tower.size(), 6U);
1698 EXPECT_EQ(tower[0], 0x02);
1699 EXPECT_EQ(tower[1], 0x00);
1701 EXPECT_EQ(tower[2], 0x00);
1702 EXPECT_EQ(tower[3], 0x00);
1703 EXPECT_EQ(tower[4], 0x00);
1704 EXPECT_EQ(tower[5], 0x00);
1706 EXPECT_EQ(tower[6], 0x13);
1707 EXPECT_EQ(tower[7], 0x00);
1713 const Bytes tower = MakeEpmTower({
1714 MakeEpmTowerFloor(MakeInterfaceTowerLhs(
UUID_PNIO_DEVICE, 0x1234), MakeInterfaceTowerRhs(0x5678)),
1719 ASSERT_TRUE(result.has_value());
1721 EXPECT_EQ(result->interfaceVersionMajor, 0x1234);
1722 EXPECT_EQ(result->interfaceVersionMinor, 0x5678);
1729 const Bytes tower = MakeEpmTower({
1735 ASSERT_TRUE(result.has_value());
1737 EXPECT_EQ(result->interfaceVersionMajor, 5);
1738 EXPECT_EQ(result->interfaceVersionMinor, 0);
1745 const Bytes tower = MakeEpmTower({
1746 MakeEpmTowerFloor({0x0D, 0x01, 0x02}, {}),
1757 const Bytes udpFloor = MakeEpmTowerFloor({0x0A}, {});
1759 const Bytes tower = MakeEpmTower({interfaceFloor, udpFloor});
1763 ASSERT_TRUE(result.has_value());
1764 EXPECT_EQ(result->protocol,
"ncadg_ip_udp");
1772 const Bytes portFloor = MakeEpmTowerFloor({0x08}, {0x12, 0x34});
1774 const Bytes tower = MakeEpmTower({interfaceFloor, portFloor});
1778 ASSERT_TRUE(result.has_value());
1779 EXPECT_EQ(result->port, 0x1234);
1787 const Bytes portFloor = MakeEpmTowerFloor({0x08}, {0x12});
1789 const Bytes tower = MakeEpmTower({interfaceFloor, portFloor});
1793 ASSERT_TRUE(result.has_value());
1794 EXPECT_EQ(result->port, 0);
1802 const Bytes ipFloor = MakeEpmTowerFloor({0x09}, {192, 168, 10, 25});
1804 const Bytes tower = MakeEpmTower({interfaceFloor, ipFloor});
1808 ASSERT_TRUE(result.has_value());
1809 EXPECT_EQ(result->ipAddress,
"192.168.10.25");
1817 const Bytes ipFloor = MakeEpmTowerFloor({0x09}, {192, 168, 10});
1819 const Bytes tower = MakeEpmTower({interfaceFloor, ipFloor});
1823 ASSERT_TRUE(result.has_value());
1824 EXPECT_TRUE(result->ipAddress.empty());
1834 ASSERT_TRUE(result.has_value());
1836 EXPECT_EQ(result->interfaceVersionMajor, 3);
1837 EXPECT_EQ(result->interfaceVersionMinor, 7);
1838 EXPECT_EQ(result->protocol,
"ncadg_ip_udp");
1839 EXPECT_EQ(result->port, 0x8894);
1840 EXPECT_EQ(result->ipAddress,
"10.250.21.110");
1847 const Bytes firstInterface = MakeEpmTowerFloor(MakeInterfaceTowerLhs(
UUID_PNIO_DEVICE, 1), MakeInterfaceTowerRhs(2));
1849 const Bytes secondInterface = MakeEpmTowerFloor(MakeInterfaceTowerLhs(
UUID_PNIO_CONTROLLER, 3), MakeInterfaceTowerRhs(4));
1851 const Bytes tower = MakeEpmTower({firstInterface, secondInterface});
1855 ASSERT_TRUE(result.has_value());
1857 EXPECT_EQ(result->interfaceVersionMajor, 1);
1858 EXPECT_EQ(result->interfaceVersionMinor, 2);
1864 const Bytes firstFloor = MakeEpmTowerFloor({0x0A}, {});
1865 const Bytes secondFloor = MakeEpmTowerFloor(MakeInterfaceTowerLhs(
UUID_PNIO_DEVICE, 1), MakeInterfaceTowerRhs(2));
1867 const Bytes tower = MakeEpmTower({firstFloor, secondFloor});
1871 EXPECT_FALSE(result.has_value());
1879 const Bytes unknownFloor = MakeEpmTowerFloor({0xFE}, {0x01, 0x02, 0x03});
1881 const Bytes tower = MakeEpmTower({interfaceFloor, unknownFloor});
1885 ASSERT_TRUE(result.has_value());
1887 EXPECT_TRUE(result->protocol.empty());
1888 EXPECT_EQ(result->port, 0);
1889 EXPECT_TRUE(result->ipAddress.empty());
1896 MakeCompleteEpmTower(
1901 {192, 168, 1, 20}));
1903 ASSERT_TRUE(result.has_value());
1905 EXPECT_EQ(result->protocol,
"ncadg_ip_udp");
1906 EXPECT_EQ(result->port, 0x8894);
1907 EXPECT_EQ(result->ipAddress,
"192.168.1.20");
1914 asio::io_context serverIo;
1915 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
1917 const std::uint16_t serverPort = server.local_endpoint().port();
1919 const auto objectUuid =
StringToUuidBytes(
"01234567-89ab-cdef-0123-456789abcdef");
1923 const Bytes body = MakeEpmLookupBody(objectUuid,
"AUMA-SGx", tower);
1925 const Bytes response = MakeEpmResponse(body);
1946 std::thread serverThread([&server, &response,
this]()
1949 ReplyToEpmLookup(server, response, request);
1952 EXPECT_EQ(request[0], 4);
1954 EXPECT_EQ(request[2], 0x20);
1971 const auto results = conn->EpmLookup(
"127.0.0.1", serverPort, 1.0);
1973 serverThread.join();
1975 ASSERT_EQ(results.size(), 1U);
1977 EXPECT_EQ(results[0].objectUuid,
"01234567-89ab-cdef-0123-456789abcdef");
1979 EXPECT_EQ(results[0].interfaceVersionMajor, 3);
1980 EXPECT_EQ(results[0].interfaceVersionMinor, 7);
1981 EXPECT_EQ(results[0].protocol,
"ncadg_ip_udp");
1982 EXPECT_EQ(results[0].port, 0x8894);
1983 EXPECT_EQ(results[0].ipAddress,
"10.250.21.110");
1984 EXPECT_EQ(results[0].annotation,
"AUMA-SGx");
1991 asio::io_context serverIo;
1992 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
1994 const std::uint16_t serverPort = server.local_endpoint().port();
1996 const Bytes responseBody = {
2011 const Bytes response = MakeEpmResponse(responseBody);
2015 std::jthread serverThread([&server, &response, &request,
this]()
2017 ReplyToEpmLookup(server, response, request);
2020 EXPECT_NO_THROW(conn->EpmLookup(
"127.0.0.1", serverPort, 1.0,
UUID_PNIO_DEVICE));
2022 serverThread.join();
2034 EXPECT_TRUE(std::equal(expectedUuid.begin(), expectedUuid.end(), request.begin() + bodyOffset + 4));
2035 r.
SkipTo(bodyOffset + 20);
2037 r.
SkipTo(bodyOffset + 22);
2044 asio::io_context serverIo;
2045 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2047 const Bytes response = {0x04, 0x02};
2051 std::jthread serverThread([&server, &response, &request,
this]()
2053 ReplyToEpmLookup(server, response, request);
2056 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2058 serverThread.join();
2060 EXPECT_TRUE(results.empty());
2066 asio::io_context serverIo;
2067 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2075 std::jthread serverThread([&server, &response, &request,
this]()
2077 ReplyToEpmLookup(server, response, request);
2080 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2082 serverThread.join();
2084 EXPECT_TRUE(results.empty());
2091 asio::io_context serverIo;
2092 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2100 std::jthread serverThread([&server, &response, &request,
this]()
2102 ReplyToEpmLookup(server, response, request);
2105 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2107 serverThread.join();
2109 EXPECT_TRUE(results.empty());
2115 asio::io_context serverIo;
2116 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2118 const Bytes response = MakeEpmResponse({0x00, 0x00, 0x00, 0x00});
2122 std::jthread serverThread([&server, &response, &request,
this]()
2124 ReplyToEpmLookup(server, response, request);
2127 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2129 serverThread.join();
2131 EXPECT_TRUE(results.empty());
2138 asio::io_context serverIo;
2139 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2141 Bytes response = MakeEpmResponse({
2157 wire::PutLittleEndian<std::uint16_t>(response, 0xFFFF);
2161 response[74] = 0xFF;
2162 response[75] = 0xFF;
2166 std::jthread serverThread([&server, &response, &request,
this]()
2168 ReplyToEpmLookup(server, response, request);
2171 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2173 serverThread.join();
2175 EXPECT_TRUE(results.empty());
2181 asio::io_context serverIo;
2182 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2185 wire::PutLittleEndian<std::uint32_t>(body, 0);
2186 wire::PutLittleEndian<std::uint32_t>(body, 1);
2187 wire::PutLittleEndian<std::uint32_t>(body, 1);
2188 wire::PutLittleEndian<std::uint32_t>(body, 0);
2189 wire::PutLittleEndian<std::uint32_t>(body, 1);
2190 body.insert(body.end(), 5, 0x00);
2192 const Bytes response = MakeEpmResponse(body);
2195 std::jthread serverThread([&server, &response, &request,
this]()
2197 ReplyToEpmLookup(server, response, request);
2200 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2202 serverThread.join();
2204 EXPECT_TRUE(results.empty());
2210 asio::io_context serverIo;
2211 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2214 wire::PutLittleEndian<std::uint32_t>(body, 0);
2215 wire::PutLittleEndian<std::uint32_t>(body, 1);
2216 wire::PutLittleEndian<std::uint32_t>(body, 1);
2217 wire::PutLittleEndian<std::uint32_t>(body, 0);
2218 wire::PutLittleEndian<std::uint32_t>(body, 1);
2220 body.push_back(0x00);
2222 const Bytes response = MakeEpmResponse(body);
2225 std::jthread serverThread([&server, &response, &request,
this]()
2227 ReplyToEpmLookup(server, response, request);
2230 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2232 serverThread.join();
2234 EXPECT_TRUE(results.empty());
2240 asio::io_context serverIo;
2241 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2244 wire::PutLittleEndian<std::uint32_t>(body, 0);
2245 wire::PutLittleEndian<std::uint32_t>(body, 1);
2246 wire::PutLittleEndian<std::uint32_t>(body, 1);
2247 wire::PutLittleEndian<std::uint32_t>(body, 0);
2248 wire::PutLittleEndian<std::uint32_t>(body, 1);
2250 wire::PutLittleEndian<std::uint32_t>(body, 0);
2251 body.push_back(0x00);
2253 const Bytes response = MakeEpmResponse(body);
2256 std::jthread serverThread([&server, &response, &request,
this]()
2258 ReplyToEpmLookup(server, response, request);
2261 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2263 serverThread.join();
2265 EXPECT_TRUE(results.empty());
2272 asio::io_context serverIo;
2273 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2275 const auto objectUuid =
StringToUuidBytes(
"01234567-89ab-cdef-0123-456789abcdef");
2280 wire::PutLittleEndian<std::uint32_t>(body, 0);
2281 wire::PutLittleEndian<std::uint32_t>(body, 1);
2282 wire::PutLittleEndian<std::uint32_t>(body, 1);
2283 wire::PutLittleEndian<std::uint32_t>(body, 0);
2284 wire::PutLittleEndian<std::uint32_t>(body, 1);
2286 body.insert(body.end(), objectUuid.begin(), objectUuid.end());
2288 wire::PutLittleEndian<std::uint32_t>(body, 0);
2289 wire::PutLittleEndian<std::uint32_t>(body, 0);
2291 while ((body.size() & 3U) != 0U)
2296 wire::PutLittleEndian<std::uint32_t>(body,
static_cast<std::uint32_t
>(tower.size()));
2297 body.insert(body.end(), tower.begin(), tower.end());
2299 while ((body.size() & 3U) != 0U)
2304 const Bytes response = MakeEpmResponse(body);
2307 std::jthread serverThread([&server, &response, &request,
this]()
2309 ReplyToEpmLookup(server, response, request);
2312 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2314 serverThread.join();
2316 ASSERT_EQ(results.size(), 1U);
2317 EXPECT_TRUE(results[0].annotation.empty());
2323 asio::io_context serverIo;
2324 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2326 const auto objectUuid =
StringToUuidBytes(
"11111111-2222-3333-4444-555555555555");
2331 wire::PutLittleEndian<std::uint32_t>(body, 0);
2332 wire::PutLittleEndian<std::uint32_t>(body, 1);
2333 wire::PutLittleEndian<std::uint32_t>(body, 1);
2334 wire::PutLittleEndian<std::uint32_t>(body, 0);
2335 wire::PutLittleEndian<std::uint32_t>(body, 1);
2337 body.insert(body.end(), objectUuid.begin(), objectUuid.end());
2339 wire::PutLittleEndian<std::uint32_t>(body, 0);
2341 const Bytes annotation = {
'A',
'B',
'C', 0, 0, 0};
2343 wire::PutLittleEndian<std::uint32_t>(body,
static_cast<std::uint32_t
>(annotation.size()));
2345 body.insert(body.end(), annotation.begin(), annotation.end());
2347 while ((body.size() & 3U) != 0U)
2352 wire::PutLittleEndian<std::uint32_t>(body,
static_cast<std::uint32_t
>(tower.size()));
2354 body.insert(body.end(), tower.begin(), tower.end());
2356 while ((body.size() & 3U) != 0U)
2361 const Bytes response = MakeEpmResponse(body);
2364 std::jthread serverThread([&server, &response, &request,
this]()
2366 ReplyToEpmLookup(server, response, request);
2369 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2371 serverThread.join();
2373 ASSERT_EQ(results.size(), 1U);
2374 EXPECT_EQ(results[0].annotation,
"ABC");
2380 asio::io_context serverIo;
2381 asio::ip::udp::socket server(serverIo, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
2383 const auto objectUuid =
StringToUuidBytes(
"01234567-89ab-cdef-0123-456789abcdef");
2385 const Bytes invalidTower = MakeEpmTower({
2386 MakeEpmTowerFloor({0x0A}, {}),
2389 const Bytes body = MakeEpmLookupBody(objectUuid,
"invalid", invalidTower);
2391 const Bytes response = MakeEpmResponse(body);
2394 std::jthread serverThread([&server, &response, &request,
this]()
2396 ReplyToEpmLookup(server, response, request);
2399 const auto results = conn->EpmLookup(
"127.0.0.1", server.local_endpoint().port(), 1.0);
2401 serverThread.join();
2403 EXPECT_TRUE(results.empty());
2411 using testing::InSequence;
2412 using testing::Return;
2413 using testing::StrictMock;
2415 StrictMock<MockRawEthernetSocket> sock;
2421 const std::string stationName =
"valve2";
2423 const Bytes response = MakeStationNameResponse(deviceMac, src, stationName);
2426 InSequence sequence;
2428 EXPECT_CALL(sock, Send(_))
2431 EXPECT_CALL(sock, SetTimeout(_))
2434 EXPECT_CALL(sock, Recv())
2435 .WillOnce(Return(response))
2436 .WillRepeatedly(Return(
Bytes{}));
2441 EXPECT_EQ(result.mac,
"a0:d3:85:00:f6:13");
2442 EXPECT_EQ(result.name, stationName);
2449 using testing::Return;
2450 using testing::StrictMock;
2452 StrictMock<MockRawEthernetSocket> sock;
2458 const std::string stationName =
"valve2";
2460 const Bytes response = MakeStationNameResponse(deviceMac, src, stationName);
2464 EXPECT_CALL(sock, Send(_))
2465 .WillOnce([&transmitted](
const std::vector<std::uint8_t>&
frame)
2467 transmitted =
frame;
2470 EXPECT_CALL(sock, SetTimeout(_))
2473 EXPECT_CALL(sock, Recv())
2474 .WillOnce(Return(response))
2475 .WillRepeatedly(Return(
Bytes{}));
2479 ASSERT_FALSE(transmitted.empty());
2481 EXPECT_EQ(result.name, stationName);
2487 EXPECT_EQ(eth.
src, src);
2500 using testing::InSequence;
2501 using testing::Return;
2502 using testing::StrictMock;
2504 StrictMock<MockRawEthernetSocket> sock;
2510 const std::string stationName =
"valve2";
2512 const Bytes response = MakeStationNameResponse(deviceMac, src, stationName);
2514 EXPECT_CALL(sock, Send(_))
2517 EXPECT_CALL(sock, SetTimeout(_))
2520 EXPECT_CALL(sock, Recv())
2521 .WillOnce(Return(
Bytes{}))
2522 .WillOnce(Return(response))
2523 .WillRepeatedly(Return(
Bytes{}));
2525 const auto result =
GetStationInfo(sock, src, stationName, 0.01);
2527 EXPECT_EQ(result.mac,
"a0:d3:85:00:f6:13");
2528 EXPECT_EQ(result.name, stationName);
2534 using testing::InSequence;
2535 using testing::Return;
2536 using testing::StrictMock;
2538 StrictMock<MockRawEthernetSocket> sock;
2544 const Bytes response = MakeStationNameResponse(deviceMac, src,
"VaLvE2");
2546 EXPECT_CALL(sock, Send(_))
2549 EXPECT_CALL(sock, SetTimeout(_))
2552 EXPECT_CALL(sock, Recv())
2553 .WillOnce(Return(
Bytes{}))
2554 .WillOnce(Return(response))
2555 .WillRepeatedly(Return(
Bytes{}));
2559 EXPECT_EQ(result.mac,
"a0:d3:85:00:f6:13");
2560 EXPECT_EQ(result.name,
"VaLvE2");
2567 using testing::InSequence;
2568 using testing::Return;
2569 using testing::StrictMock;
2571 StrictMock<MockRawEthernetSocket> sock;
2582 const Bytes discoveryResponse = MakeStationNameResponse(secondMac, src,
"valve2");
2584 EXPECT_CALL(sock, Send(_))
2587 EXPECT_CALL(sock, SetTimeout(_))
2590 EXPECT_CALL(sock, Recv())
2591 .WillOnce(Return(
Bytes{}))
2592 .WillOnce(Return(discoveryResponse))
2593 .WillRepeatedly(Return(
Bytes{}));
2597 EXPECT_EQ(result.mac,
"a0:d3:85:00:f6:14");
2604 using testing::InSequence;
2605 using testing::Return;
2606 using testing::StrictMock;
2608 StrictMock<MockRawEthernetSocket> sock;
2614 const Bytes response = MakeStationNameResponse(deviceMac, src,
"different-device");
2616 EXPECT_CALL(sock, Send(_))
2619 EXPECT_CALL(sock, SetTimeout(_))
2622 EXPECT_CALL(sock, Recv())
2623 .WillOnce(Return(
Bytes{}))
2624 .WillOnce(Return(response))
2625 .WillRepeatedly(Return(
Bytes{}));
2634 using testing::InSequence;
2635 using testing::Return;
2636 using testing::StrictMock;
2638 StrictMock<MockRawEthernetSocket> sock;
2644 const Bytes response = MakeStationNameResponse(deviceMac, src,
"different-device");
2646 EXPECT_CALL(sock, Send(_))
2649 EXPECT_CALL(sock, SetTimeout(_))
2652 EXPECT_CALL(sock, Recv())
2653 .WillOnce(Return(
Bytes{}))
2654 .WillOnce(Return(response))
2655 .WillRepeatedly(Return(
Bytes{}));
2663 using testing::InSequence;
2664 using testing::Return;
2665 using testing::StrictMock;
2667 StrictMock<MockRawEthernetSocket> sock;
2671 EXPECT_CALL(sock, Send(_))
2674 EXPECT_CALL(sock, SetTimeout(_))
2677 EXPECT_CALL(sock, Recv())
2678 .WillOnce(Return(
Bytes{}))
2679 .WillOnce(Return(
Bytes{}))
2680 .WillRepeatedly(Return(
Bytes{}));
2689 using testing::InSequence;
2690 using testing::Return;
2691 using testing::StrictMock;
2693 StrictMock<MockRawEthernetSocket> sock;
2699 const Bytes response = MakeStationNameResponse(deviceMac, src,
"some-other-device");
2702 InSequence sequence;
2704 EXPECT_CALL(sock, Send(_))
2707 EXPECT_CALL(sock, SetTimeout(_))
2710 EXPECT_CALL(sock, Recv())
2711 .WillOnce(Return(response))
2712 .WillRepeatedly(Return(
Bytes{}));
2717 EXPECT_EQ(result.name,
"some-other-device");
2736 EXPECT_EQ(result.
offset, 0U);
2744 const PNNRDData result = InvokeCreateNrd({});
2749 EXPECT_EQ(result.
offset, 0U);
2751 EXPECT_TRUE(result.
payload.empty());
2780 const Bytes bytes = InvokeBuildAlarmCrBlock(0, 0);
2801 const Bytes bytes = InvokeBuildAlarmCrBlock(1, 0);
2812 const Bytes bytes = InvokeBuildAlarmCrBlock(0, 1);
2823 const Bytes bytes = InvokeBuildAlarmCrBlock(1, 1);
2834 const Bytes bytes = InvokeBuildAlarmCrBlock(0, 0);
2846 EXPECT_EQ(InvokeParseAlarmCrResponse({}), -1);
2852 const Bytes data = {
2858 EXPECT_EQ(InvokeParseAlarmCrResponse(data), -1);
2864 const Bytes data = MakeBlockHeader(
static_cast<BlockType>(0x9999), 0);
2866 EXPECT_EQ(InvokeParseAlarmCrResponse(data), -1);
2872 constexpr std::uint16_t expectedAlarmReference = 0x1234;
2874 const Bytes data = MakeAlarmCrResponseBlock(expectedAlarmReference);
2876 EXPECT_EQ(InvokeParseAlarmCrResponse(data),
static_cast<int>(expectedAlarmReference));
2882 constexpr std::uint16_t expectedAlarmReference = 0x4321;
2886 const Bytes alarmBlock = MakeAlarmCrResponseBlock(expectedAlarmReference);
2888 data.insert(data.end(), alarmBlock.begin(), alarmBlock.end());
2890 EXPECT_EQ(InvokeParseAlarmCrResponse(data),
static_cast<int>(expectedAlarmReference));
2895 Bytes malformed = MakeAlarmCrResponseBlock(0x1234);
2901 const int result = InvokeParseAlarmCrResponse(malformed);
2903 EXPECT_EQ(result, -1);
2909 Bytes malformed = MakeAlarmCrResponseBlock(0x1234);
2911 malformed[2] = 0xFF;
2912 malformed[3] = 0xFF;
2914 EXPECT_EQ(InvokeParseAlarmCrResponse(malformed), -1);
2920 Bytes malformed = MakeAlarmCrResponseBlock(0x1111);
2922 malformed[2] = 0xFF;
2923 malformed[3] = 0xFF;
2935 const Bytes malformedHeaderBytes = malformedHeader.
ToBytes();
2937 const Bytes validBlock = MakeAlarmCrResponseBlock(0x5678);
2939 Bytes data = malformedHeaderBytes;
2940 data.insert(data.end(), validBlock.begin(), validBlock.end());
2942 const int result = InvokeParseAlarmCrResponse(data);
2946 EXPECT_EQ(result, -1);
2954 const Bytes second = MakeBlockHeader(
static_cast<BlockType>(0x2000), 0);
2956 const Bytes third = MakeBlockHeader(
static_cast<BlockType>(0x3000), 0);
2958 data.insert(data.end(), second.begin(), second.end());
2960 data.insert(data.end(), third.begin(), third.end());
2962 EXPECT_EQ(InvokeParseAlarmCrResponse(data), -1);
2968 EXPECT_FALSE(conn->IsConnected());
2974 const auto& info = conn->Info();
2976 EXPECT_EQ(info.name,
"test-device");
2982 const auto& uuid = conn->ArUuid();
2990 EXPECT_NE(conn->SessionKey(), 0U);
2996 EXPECT_EQ(conn->LocalAlarmRef(), conn->LocalAlarmRef());
3002 EXPECT_EQ(conn->DeviceAlarmRef(), 0);
3008 EXPECT_FALSE(conn->HasAlarmCr());
3014 EXPECT_EQ(InvokeParseIocrResponse({}, 1), 0);
3020 const Bytes data = {0x81, 0x02, 0x00};
3022 EXPECT_EQ(InvokeParseIocrResponse(data, 1), 0);
3030 EXPECT_EQ(InvokeParseIocrResponse(data, 1), 0);
3036 const Bytes data = MakeIocrResponseBlock(2, 7, 0x1234);
3038 EXPECT_EQ(InvokeParseIocrResponse(data, 1), 0);
3044 const Bytes data = MakeIocrResponseBlock(1, 7, 0x8001);
3046 EXPECT_EQ(InvokeParseIocrResponse(data, 1), 0x8001);
3056 EXPECT_EQ(InvokeParseIocrResponse(malformed, 1), 0);
3064 const Bytes iocr = MakeIocrResponseBlock(2, 9, 0x8123);
3065 data.insert(data.end(), iocr.begin(), iocr.end());
3067 EXPECT_EQ(InvokeParseIocrResponse(data, 2), 0x8123);
3072 transport = std::make_unique<TestRpcTransport>();
3073 rawTransport = transport.get();
3078 conn = MakeTestTransportRpcConnection(std::move(transport));
3084 const PNRPCHeader response = InvokeSendReceive(request);
3089 EXPECT_TRUE(conn->IsConnected());
3096 transport = std::make_unique<TestRpcTransport>();
3097 rawTransport = transport.get();
3103 conn = MakeTestTransportRpcConnection(std::move(transport));
3108 const PNRPCHeader response = InvokeSendReceive(request);
3117 transport = std::make_unique<TestRpcTransport>();
3118 transport->responses.emplace_back(10, 0);
3120 conn = MakeTestTransportRpcConnection(std::move(transport));
3124 EXPECT_THROW(InvokeSendReceive(request),
RPCError);
3130 transport = std::make_unique<TestRpcTransport>();
3133 conn = MakeTestTransportRpcConnection(std::move(transport));
3144 transport = std::make_unique<TestRpcTransport>();
3147 conn = MakeTestTransportRpcConnection(std::move(transport));
3151 EXPECT_THROW(InvokeSendReceive(request),
RPCError);
3157 transport = std::make_unique<TestRpcTransport>();
3160 conn = MakeTestTransportRpcConnection(std::move(transport));
3166 InvokeSendReceive(request);
3167 FAIL() <<
"Expected RPCError";
3171 EXPECT_NE(std::string(e.what()).find(
"Unexpected RPC packet type"), std::string::npos);
3177 InvokeSetRPCConLive(
false);
3179 EXPECT_FALSE(conn->IsConnected());
3185 InvokeSetRPCConLive(
true);
3186 InvokeSetRPCConSrcMac(
"02:00:00:00:00:01");
3187 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3188 InvokeRPCConCheckTimeout();
3190 EXPECT_TRUE(InvokeGetRPCConLive());
3197 transport = std::make_unique<TestRpcTransport>();
3198 rawTransport = transport.get();
3202 conn = MakeTestTransportRpcConnection(std::move(transport));
3204 InvokeSetRPCConLive(
true);
3205 InvokeSetRPCConSrcMac(
"02:00:00:00:00:01");
3206 auto timePoint = std::chrono::steady_clock::now() -
3207 std::chrono::duration_cast<std::chrono::steady_clock::duration>(std::chrono::duration<double>(
CONNECTION_TIMEOUT + 1.0));
3209 InvokeSetRPCConLiveMonotonic(timePoint);
3210 InvokeRPCConCheckTimeout();
3212 EXPECT_TRUE(InvokeGetRPCConLive());
3219 EXPECT_THROW(conn->Connect(), std::invalid_argument);
3225 transport = std::make_unique<TestRpcTransport>();
3226 rawTransport = transport.get();
3230 conn = MakeTestTransportRpcConnection(std::move(transport));
3234 const auto result = conn->Connect(mac);
3236 EXPECT_FALSE(result.has_value());
3237 EXPECT_TRUE(conn->IsConnected());
3238 EXPECT_EQ(conn->LocalAlarmRef(), 0x8001);
3239 EXPECT_FALSE(conn->HasAlarmCr());
3246 transport = std::make_unique<TestRpcTransport>();
3248 const Bytes alarm = MakeAlarmCrResponseBlock(0x4321);
3250 Bytes responsePayload = alarm;
3252 transport->responses.push_back(MakeNrdRpcResponseBytes(
PNRPCHeader::CONNECT, responsePayload));
3254 conn = MakeTestTransportRpcConnection(std::move(transport));
3256 const auto result = conn->Connect(
String2Mac(
"02:00:00:00:00:01"),
true);
3258 EXPECT_FALSE(result.has_value());
3259 EXPECT_TRUE(conn->IsConnected());
3260 EXPECT_TRUE(conn->HasAlarmCr());
3261 EXPECT_EQ(conn->DeviceAlarmRef(), 0x4321);
3267 transport = std::make_unique<TestRpcTransport>();
3270 conn = MakeTestTransportRpcConnection(std::move(transport));
3272 const auto result = conn->Connect(
String2Mac(
"02:00:00:00:00:01"),
true);
3274 EXPECT_FALSE(result.has_value());
3275 EXPECT_FALSE(conn->HasAlarmCr());
3276 EXPECT_EQ(conn->DeviceAlarmRef(), -1);
3282 transport = std::make_unique<TestRpcTransport>();
3286 conn = MakeTestTransportRpcConnection(std::move(transport));
3294 transport = std::make_unique<TestRpcTransport>();
3296 transport->responseHandler = [](
const Bytes&) ->
Bytes
3298 throw RPCError(
"transport failure");
3301 conn = MakeTestTransportRpcConnection(std::move(transport));
3309 transport = std::make_unique<TestRpcTransport>();
3313 conn = MakeTestTransportRpcConnection(std::move(transport));
3317 ASSERT_NO_THROW(conn->Connect(mac));
3319 InvokeSetRPCConLive(
true);
3321 EXPECT_NO_THROW(conn->Connect());
3327 transport = std::make_unique<TestRpcTransport>();
3331 conn = MakeTestTransportRpcConnection(std::move(transport));
3337 ASSERT_NO_THROW(conn->Connect(initialMac));
3339 auto rpcConMac = InvokeGetRPCConSrcMac();
3340 ASSERT_TRUE(rpcConMac.has_value());
3342 ASSERT_NO_THROW(conn->Connect(replacementMac));
3344 rpcConMac = InvokeGetRPCConSrcMac();
3345 EXPECT_EQ(rpcConMac.value(), replacementMac);
3351 conn = MakeTestTransportRpcConnection(std::make_unique<TestRpcTransport>());
3353 InvokeSetRPCConLive(
true);
3354 InvokeGetRPCConSrcMac().reset();
3356 EXPECT_THROW(conn->Connect(), std::invalid_argument);
3361 transport = std::make_unique<TestRpcTransport>();
3363 Bytes responsePayload = MakeIocrResponseBlock(1, 1, 0x8001);
3365 const Bytes outputIocr = MakeIocrResponseBlock(2, 2, 0x8002);
3367 responsePayload.insert(responsePayload.end(), outputIocr.begin(), outputIocr.end());
3369 const Bytes alarm = MakeAlarmCrResponseBlock(0x4321);
3371 responsePayload.insert(responsePayload.end(), alarm.begin(), alarm.end());
3373 transport->responses.push_back(MakeNrdRpcResponseBytes(
PNRPCHeader::CONNECT, responsePayload));
3375 conn = MakeTestTransportRpcConnection(std::move(transport));
3378 setup.
slots.push_back({
3383 .moduleIdent = 0x42,
3384 .submoduleIdent = 0x99,
3387 const auto result = conn->Connect(
String2Mac(
"02:00:00:00:00:01"),
false, setup);
3389 ASSERT_TRUE(result.has_value());
3390 EXPECT_EQ(result->inputFrameId, 0x8001);
3391 EXPECT_EQ(result->outputFrameId, 0x8002);
3392 EXPECT_EQ(result->deviceAlarmRef, 0x4321);
3393 EXPECT_TRUE(conn->HasAlarmCr());
3398 constexpr std::uint16_t index = 0xAFF0;
3399 const Bytes record = {0x11, 0x22, 0x33};
3401 transport = std::make_unique<TestRpcTransport>();
3402 rawTransport = transport.get();
3405 MakeNrdRpcResponseBytes(
3407 MakeIodResponsePayload(7, 3, 4, index, record)));
3409 conn = MakeTestTransportRpcConnection(std::move(transport));
3411 InvokeSetRPCConLive(
true);
3412 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3414 const PNIODHeader result = conn->Read(7, 3, 4, index);
3416 EXPECT_EQ(result.
api, 7U);
3417 EXPECT_EQ(result.
slot, 3U);
3418 EXPECT_EQ(result.
subslot, 4U);
3419 EXPECT_EQ(result.
index, index);
3420 EXPECT_EQ(result.
payload, record);
3427 transport = std::make_unique<TestRpcTransport>();
3431 conn = MakeTestTransportRpcConnection(std::move(transport));
3433 InvokeSetRPCConLive(
true);
3434 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3436 EXPECT_THROW(conn->Read(0, 0, 1, 0xAFF0),
PNIOError);
3441 constexpr std::uint16_t index = 0xAFF0;
3442 const Bytes record = {0xAA, 0xBB};
3444 transport = std::make_unique<TestRpcTransport>();
3445 rawTransport = transport.get();
3447 transport->
responses.push_back(MakeNrdRpcResponseBytes(
3449 MakeIodResponsePayload(0, 0, 1, index, record)));
3451 conn = MakeTestTransportRpcConnection(std::move(transport));
3453 const PNIODHeader result = conn->ReadImplicit(0, 0, 1, index);
3455 EXPECT_EQ(result.
index, index);
3456 EXPECT_EQ(result.
payload, record);
3463 transport = std::make_unique<TestRpcTransport>();
3467 conn = MakeTestTransportRpcConnection(std::move(transport));
3469 EXPECT_THROW(conn->ReadImplicit(0, 0, 1, 0xAFF0),
PNIOError);
3474 transport = std::make_unique<TestRpcTransport>();
3475 rawTransport = transport.get();
3479 conn = MakeTestTransportRpcConnection(std::move(transport));
3481 InvokeSetRPCConLive(
true);
3482 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3484 EXPECT_NO_THROW(conn->Write(1, 2, 3., 0xAFF1, {0x10, 0x20, 0x30}));
3492 transport = std::make_unique<TestRpcTransport>();
3496 conn = MakeTestTransportRpcConnection(std::move(transport));
3498 InvokeSetRPCConLive(
true);
3499 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3501 EXPECT_THROW(conn->Write(0, 0, 1, 0xAFF1, {0x01}),
PNIOError);
3506 constexpr std::uint16_t index = 0xAFF0;
3509 transport = std::make_unique<TestRpcTransport>();
3511 transport->responses.push_back(MakeNrdRpcResponseBytes(
3513 MakeIodResponsePayload(0, 0, 1, index,
payload)));
3515 conn = MakeTestTransportRpcConnection(std::move(transport));
3517 InvokeSetRPCConLive(
true);
3518 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3520 EXPECT_EQ(conn->ReadRaw(index, 2, 3),
payload);
3525 transport = std::make_unique<TestRpcTransport>();
3527 transport->responseHandler = [
this](
const Bytes& request) ->
Bytes
3535 return MakeNrdRpcResponseBytes(
3537 MakeIodResponsePayload(0, 0, 1, iod.
index,
3538 MakeImPayload(iod.
index)));
3541 conn = MakeTestTransportRpcConnection(std::move(transport));
3543 InvokeSetRPCConLive(
true);
3544 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3546 EXPECT_NO_THROW(conn->ReadIm0());
3547 EXPECT_NO_THROW(conn->ReadIm1());
3548 EXPECT_NO_THROW(conn->ReadIm2());
3549 EXPECT_NO_THROW(conn->ReadIm3());
3550 EXPECT_NO_THROW(conn->ReadIm4());
3551 EXPECT_NO_THROW(conn->ReadIm5());
3552 EXPECT_NO_THROW(conn->ReadIm6());
3553 EXPECT_NO_THROW(conn->ReadIm7());
3554 EXPECT_NO_THROW(conn->ReadIm8());
3555 EXPECT_NO_THROW(conn->ReadIm9());
3556 EXPECT_NO_THROW(conn->ReadIm10());
3557 EXPECT_NO_THROW(conn->ReadIm11());
3558 EXPECT_NO_THROW(conn->ReadIm12());
3559 EXPECT_NO_THROW(conn->ReadIm13());
3560 EXPECT_NO_THROW(conn->ReadIm14());
3561 EXPECT_NO_THROW(conn->ReadIm15());
3566 transport = std::make_unique<TestRpcTransport>();
3568 transport->responseHandler = [
this](
const Bytes& request) ->
Bytes
3576 return MakeNrdRpcResponseBytes(
3578 MakeIodResponsePayload(0, 0, 1, iod.
index,
3579 MakeImPayload(iod.
index)));
3582 conn = MakeTestTransportRpcConnection(std::move(transport));
3584 InvokeSetRPCConLive(
true);
3585 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3587 const AllIM result = conn->ReadAllIm();
3589 EXPECT_TRUE(result.
im0.has_value());
3590 EXPECT_TRUE(result.
im1.has_value());
3591 EXPECT_TRUE(result.
im2.has_value());
3592 EXPECT_TRUE(result.
im3.has_value());
3593 EXPECT_TRUE(result.
im4.has_value());
3594 EXPECT_TRUE(result.
im5.has_value());
3595 EXPECT_TRUE(result.
im6.has_value());
3596 EXPECT_TRUE(result.
im7.has_value());
3597 EXPECT_TRUE(result.
im8.has_value());
3598 EXPECT_TRUE(result.
im9.has_value());
3599 EXPECT_TRUE(result.
im10.has_value());
3600 EXPECT_TRUE(result.
im11.has_value());
3601 EXPECT_TRUE(result.
im12.has_value());
3602 EXPECT_TRUE(result.
im13.has_value());
3603 EXPECT_TRUE(result.
im14.has_value());
3604 EXPECT_TRUE(result.
im15.has_value());
3610 transport = std::make_unique<TestRpcTransport>();
3612 transport->responseHandler = [
this](
const Bytes& request) ->
Bytes
3622 throw RPCError(
"I&M2 unavailable");
3625 return MakeNrdRpcResponseBytes(
3627 MakeIodResponsePayload(0, 0, 1, iod.
index,
3628 MakeImPayload(iod.
index)));
3631 conn = MakeTestTransportRpcConnection(std::move(transport));
3633 InvokeSetRPCConLive(
true);
3634 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3636 const AllIM result = conn->ReadAllIm();
3638 EXPECT_TRUE(result.
im0.has_value());
3639 EXPECT_TRUE(result.
im1.has_value());
3640 EXPECT_FALSE(result.
im2.has_value());
3641 EXPECT_TRUE(result.
im3.has_value());
3647 transport = std::make_unique<TestRpcTransport>();
3649 transport->responseHandler = [
this](
const Bytes& request) ->
Bytes
3662 return MakeNrdRpcResponseBytes(
3664 MakeIodResponsePayload(0, 0, 1, iod.
index,
3665 MakeImPayload(iod.
index)));
3668 conn = MakeTestTransportRpcConnection(std::move(transport));
3670 InvokeSetRPCConLive(
true);
3671 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3673 const AllIM result = conn->ReadAllIm();
3675 EXPECT_TRUE(result.
im0.has_value());
3676 EXPECT_TRUE(result.
im1.has_value());
3677 EXPECT_FALSE(result.
im3.has_value());
3678 EXPECT_TRUE(result.
im4.has_value());
3684 transport = std::make_unique<TestRpcTransport>();
3686 transport->responseHandler = [](
const Bytes&) ->
Bytes
3688 throw RPCError(
"I&M0 unavailable");
3691 conn = MakeTestTransportRpcConnection(std::move(transport));
3693 InvokeSetRPCConLive(
true);
3694 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3696 EXPECT_THROW(conn->ReadAllIm(),
RPCError);
3701 transport = std::make_unique<TestRpcTransport>();
3702 rawTransport = transport.get();
3704 conn = MakeTestTransportRpcConnection(std::move(transport));
3706 InvokeSetRPCConLive(
true);
3707 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3709 const auto result = conn->WriteMultiple({});
3711 EXPECT_TRUE(result.empty());
3718 transport = std::make_unique<TestRpcTransport>();
3719 rawTransport = transport.get();
3723 conn = MakeTestTransportRpcConnection(std::move(transport));
3725 InvokeSetRPCConLive(
true);
3726 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3728 const std::vector<WriteItem> writes = {
3733 .data = {0x10, 0x20},
3740 .data = {0x30, 0x40, 0x50},
3745 EXPECT_NO_THROW(conn->WriteMultiple(writes));
3752 transport = std::make_unique<TestRpcTransport>();
3756 conn = MakeTestTransportRpcConnection(std::move(transport));
3758 const std::vector<WriteItem> writes = {
3767 InvokeSetRPCConLive(
true);
3768 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3769 EXPECT_THROW(conn->WriteMultiple(writes),
PNIOError);
3774 transport = std::make_unique<TestRpcTransport>();
3776 transport->responses.push_back(MakeNrdRpcResponseBytes(
3779 MakeModuleDiffPayload())));
3781 conn = MakeTestTransportRpcConnection(std::move(transport));
3783 InvokeSetRPCConLive(
true);
3784 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3785 const auto result = conn->ReadModuleDiff();
3787 ASSERT_EQ(result.modules.size(), 1U);
3788 EXPECT_EQ(result.modules[0].slotNumber, 1U);
3793 transport = std::make_unique<TestRpcTransport>();
3795 transport->responses.push_back(MakeNrdRpcResponseBytes(
3797 MakeIodResponsePayload(0, 0, 1, 0xF000,
3798 MakeDiagnosisPayload())));
3800 conn = MakeTestTransportRpcConnection(std::move(transport));
3802 InvokeSetRPCConLive(
true);
3803 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3805 const auto result = conn->ReadDiagnosis(3, 4, 0xF000);
3807 ASSERT_EQ(result.entries.size(), 1U);
3808 EXPECT_EQ(result.slot, 3U);
3809 EXPECT_EQ(result.subslot, 4U);
3833 transport = std::make_unique<TestRpcTransport>();
3835 transport->responses.push_back(
3836 MakeNrdRpcResponseBytes(
3838 MakeIodResponsePayload(0, 0, 1, 0xF000, simple)));
3840 conn = MakeTestTransportRpcConnection(std::move(transport));
3842 InvokeSetRPCConLive(
true);
3843 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3845 const auto result = conn->ReadDiagnosis();
3847 ASSERT_EQ(result.entries.size(), 1U);
3848 EXPECT_EQ(result.entries[0].base.errorTypeName,
"Error");
3849 EXPECT_EQ(result.entries[0].base.errorType, 0x0009);
3850 EXPECT_EQ(result.entries[0].base.channelNumber, 0U);
3856 transport = std::make_unique<TestRpcTransport>();
3858 transport->responses.push_back(MakeNrdRpcResponseBytes(
3861 MakeIodResponsePayload(0, 0, 1, 0xF000, {0x01, 0x02, 0x03, 0x04, 0x05, 0x06})));
3863 conn = MakeTestTransportRpcConnection(std::move(transport));
3865 InvokeSetRPCConLive(
true);
3866 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3868 const auto result = conn->ReadDiagnosis(7, 8, 0xF000);
3870 EXPECT_TRUE(result.entries.empty());
3871 EXPECT_EQ(result.api, 0U);
3872 EXPECT_EQ(result.slot, 7U);
3873 EXPECT_EQ(result.subslot, 8U);
3874 EXPECT_EQ(result.rawData.size(), 6U);
3880 transport = std::make_unique<TestRpcTransport>();
3882 transport->responseHandler = [](
const Bytes&) ->
Bytes
3884 throw RPCError(
"diagnosis unavailable");
3887 conn = MakeTestTransportRpcConnection(std::move(transport));
3889 InvokeSetRPCConLive(
true);
3890 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3892 const auto result = conn->ReadDiagnosis(2, 3, 0xF000);
3894 EXPECT_TRUE(result.entries.empty());
3895 EXPECT_EQ(result.api, 0U);
3896 EXPECT_EQ(result.slot, 2U);
3897 EXPECT_EQ(result.subslot, 3U);
3902 transport = std::make_unique<TestRpcTransport>();
3904 transport->responseHandler = [
this](
const Bytes& request) ->
Bytes
3912 if (iod.
index == 0x800A)
3914 return MakeNrdRpcResponseBytes(
3916 MakeIodResponsePayload(0, 0, 1, iod.
index, MakeDiagnosisPayload()));
3919 return MakeNrdRpcResponseBytes(
3921 MakeIodResponsePayload(0, 0, 1, iod.
index, {}));
3924 conn = MakeTestTransportRpcConnection(std::move(transport));
3926 InvokeSetRPCConLive(
true);
3927 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3929 const auto result = conn->ReadAllDiagnosis();
3931 ASSERT_EQ(result.size(), 1U);
3932 EXPECT_TRUE(result.contains(0x800A));
3938 transport = std::make_unique<TestRpcTransport>();
3940 transport->responseHandler = [
this](
const Bytes& request) ->
Bytes
3948 if (iod.
index == 0x800B)
3953 return MakeNrdRpcResponseBytes(
3956 MakeIodResponsePayload(0, 0, 1, iod.
index, MakeDiagnosisPayload()));
3959 conn = MakeTestTransportRpcConnection(std::move(transport));
3961 InvokeSetRPCConLive(
true);
3962 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3964 EXPECT_NO_THROW(conn->ReadAllDiagnosis());
3969 transport = std::make_unique<TestRpcTransport>();
3971 transport->responses.push_back(
3972 MakeNrdRpcResponseBytes(
3976 conn = MakeTestTransportRpcConnection(std::move(transport));
3978 InvokeSetRPCConLive(
true);
3979 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3981 EXPECT_NO_THROW(conn->ReadPdRealData());
3986 transport = std::make_unique<TestRpcTransport>();
3988 transport->responses.push_back(
3989 MakeNrdRpcResponseBytes(
3991 MakeIodResponsePayload(0, 0, 1,
REAL_ID_API, MakeRealIdentificationPayload())));
3993 conn = MakeTestTransportRpcConnection(std::move(transport));
3995 InvokeSetRPCConLive(
true);
3996 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
3998 const auto result = conn->ReadRealIdentificationData();
4000 ASSERT_EQ(result.slots.size(), 1U);
4001 EXPECT_EQ(result.slots[0].slot, 1U);
4002 EXPECT_EQ(result.slots[0].submoduleIdent, 0x99U);
4007 transport = std::make_unique<TestRpcTransport>();
4009 transport->responses.push_back(
4010 MakeNrdRpcResponseBytes(
4012 MakeIodResponsePayload(0, 0, 1,
REAL_ID_API, MakeRealIdentificationPayload())));
4014 conn = MakeTestTransportRpcConnection(std::move(transport));
4016 InvokeSetRPCConLive(
true);
4017 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4019 const auto result = conn->DiscoverSlots();
4021 ASSERT_EQ(result.size(), 1U);
4022 EXPECT_EQ(result[0].slot, 1U);
4027 transport = std::make_unique<TestRpcTransport>();
4029 transport->responses.push_back(
4030 MakeNrdRpcResponseBytes(
4034 transport->responses.push_back(
4035 MakeNrdRpcResponseBytes(
4037 MakeIodResponsePayload(0, 0, 1,
REAL_ID_API, MakeRealIdentificationPayload())));
4039 conn = MakeTestTransportRpcConnection(std::move(transport));
4041 InvokeSetRPCConLive(
true);
4042 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4044 const auto [pdReal, realId] = conn->DiscoverTopology();
4046 EXPECT_EQ(pdReal.slots.size(), 0U);
4047 ASSERT_EQ(realId.slots.size(), 1U);
4048 EXPECT_EQ(realId.slots[0].slot, 1U);
4054 transport = std::make_unique<TestRpcTransport>();
4056 transport->responseHandler = [
this](
const Bytes& request) ->
Bytes
4067 return MakeNrdRpcResponseBytes(
4069 MakeIodResponsePayload(0, 0, 1, iod.
index, {0x01, 0x02, 0x03}));
4072 return MakeNrdRpcResponseBytes(
4074 MakeIodResponsePayload(0, 0, 1, iod.
index, {}));
4077 return MakeRpcResponseBytes(
4082 throw RPCError(
"RPC probe failure");
4085 throw RPCError(
"unexpected test index");
4089 conn = MakeTestTransportRpcConnection(std::move(transport));
4091 InvokeSetRPCConLive(
true);
4092 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4094 const auto result = conn->EnumerateIndices(1, 2, std::vector<std::uint16_t>{0x1001, 0x1002, 0x1003, 0x1004, 0x1001});
4096 ASSERT_EQ(result.size(), 4U);
4098 EXPECT_EQ(result.at(0x1001).status,
"readable");
4099 EXPECT_EQ(result.at(0x1001).size, 3U);
4101 EXPECT_EQ(result.at(0x1002).status,
"empty");
4102 EXPECT_EQ(result.at(0x1002).size, 0U);
4104 EXPECT_EQ(result.at(0x1003).status,
"error");
4105 EXPECT_FALSE(result.at(0x1003).error.empty());
4107 EXPECT_EQ(result.at(0x1004).status,
"error");
4108 EXPECT_EQ(result.at(0x1004).error,
"RPC probe failure");
4113 InvokeSetRPCConLive(
false);
4121 InvokeSetRPCConLive(
true);
4122 InvokeSetRPCConArUuid(0x11);
4123 InvokeSetRPCConSessionKey(0x1234);
4125 const Bytes subBlocks = {0xAA, 0xBB};
4129 EXPECT_TRUE(result.empty());
4139 EXPECT_GE(nrd.
payload.size(),
static_cast<std::size_t
>(32));
4141 const auto begin = nrd.
payload.end() - subBlocks.size();
4149 transport = std::make_unique<TestRpcTransport>();
4151 const Bytes expectedPayload = {0x10, 0x20, 0x30};
4153 transport->responses.push_back(MakeNrdRpcResponseBytes(
PNRPCHeader::CONTROL, expectedPayload));
4155 conn = MakeTestTransportRpcConnection(std::move(transport));
4157 InvokeSetRPCConLive(
true);
4158 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4162 EXPECT_EQ(result, expectedPayload);
4168 transport = std::make_unique<TestRpcTransport>();
4172 conn = MakeTestTransportRpcConnection(std::move(transport));
4174 InvokeSetRPCConLive(
true);
4175 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4182 transport = std::make_unique<TestRpcTransport>();
4183 rawTransport = transport.get();
4187 conn = MakeTestTransportRpcConnection(std::move(transport));
4189 InvokeSetRPCConLive(
true);
4190 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4192 InvokeSetRPCConArUuid(0x11);
4193 InvokeSetRPCConSessionKey(0x1234);
4195 EXPECT_NO_THROW(conn->PrmBegin());
4209 transport = std::make_unique<TestRpcTransport>();
4210 rawTransport = transport.get();
4214 conn = MakeTestTransportRpcConnection(std::move(transport));
4216 InvokeSetRPCConLive(
true);
4217 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4219 EXPECT_NO_THROW(conn->PrmEnd());
4233 transport = std::make_unique<TestRpcTransport>();
4234 rawTransport = transport.get();
4238 conn = MakeTestTransportRpcConnection(std::move(transport));
4240 InvokeSetRPCConLive(
true);
4241 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4243 EXPECT_NO_THROW(conn->ReadyForRtClass3());
4261 EXPECT_FALSE(result.has_value());
4269 const Bytes data = MakeRawRpcHeader(
4279 ASSERT_TRUE(result.has_value());
4281 EXPECT_EQ(result->version, 0x04);
4283 EXPECT_EQ(result->flags1, 0x01);
4284 EXPECT_EQ(result->flags2, 0x02);
4286 EXPECT_EQ(result->drep[0], 0x00);
4287 EXPECT_EQ(result->drep[1], 0x00);
4288 EXPECT_EQ(result->drep[2], 0x00);
4290 EXPECT_FALSE(result->isLittleEndian);
4292 EXPECT_EQ(result->interfaceVersion, 0x11223344U);
4293 EXPECT_EQ(result->sequenceNumber, 0x55667788U);
4294 EXPECT_EQ(result->operationNumber, 0x99AA);
4296 EXPECT_EQ(result->serialHigh, 0x5A);
4297 EXPECT_EQ(result->serialLow, 0x00);
4298 EXPECT_EQ(result->payload,
payload);
4302 EXPECT_EQ(result->objectUuid[i],
static_cast<std::uint8_t
>(0x10 + i));
4303 EXPECT_EQ(result->interfaceUuid[i],
static_cast<std::uint8_t
>(0x20 + i));
4304 EXPECT_EQ(result->activityUuid[i],
static_cast<std::uint8_t
>(0x30 + i));
4313 const Bytes data = MakeRawRpcHeader(
true,
4322 ASSERT_TRUE(result.has_value());
4325 EXPECT_TRUE(result->isLittleEndian);
4327 EXPECT_EQ(result->interfaceVersion, 0x11223344U);
4328 EXPECT_EQ(result->sequenceNumber, 0x55667788U);
4329 EXPECT_EQ(result->operationNumber, 0x99AA);
4331 EXPECT_EQ(result->payload,
payload);
4337 Bytes data = MakeRawRpcHeader(
false,
4348 ASSERT_TRUE(result.has_value());
4349 EXPECT_EQ(result->serialHigh, 0xAB);
4357 const Bytes data = MakeRawRpcHeader(
false,
4368 ASSERT_TRUE(result.has_value());
4370 EXPECT_EQ(result->payload,
payload);
4376 InvokeSetRPCConLive(
false);
4378 EXPECT_THROW(conn->ApplicationReady(1.0),
RPCError);
4386 InvokeSetRPCConLive(
true);
4387 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4393 const Bytes result = conn->ApplicationReady(1.5);
4396 EXPECT_FALSE(result.empty());
4402 InvokeSetRPCConLive(
true);
4403 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4410 MakeApplicationReadyNrd(
4411 MakeApplicationReadyControlBlock(),
4416 const Bytes result = conn->ApplicationReady(2.0);
4418 EXPECT_FALSE(result.empty());
4426 InvokeSetRPCConLive(
true);
4427 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4429 const Bytes controlBlock = MakeApplicationReadyControlBlock();
4430 const Bytes nrd = MakeApplicationReadyNrd(controlBlock,
false);
4442 const Bytes result = conn->ApplicationReady(3.0);
4444 EXPECT_FALSE(result.empty());
4452 InvokeSetRPCConLive(
true);
4453 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4455 const Bytes shortNrd(19, 0x00);
4467 const Bytes result = conn->ApplicationReady(4.0);
4469 EXPECT_FALSE(result.empty());
4476 InvokeSetRPCConLive(
true);
4477 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4479 const Bytes shortNrdBody(31, 0x00);
4480 const Bytes nrd = MakeApplicationReadyNrd(shortNrdBody,
false);
4492 const Bytes result = conn->ApplicationReady(5.0);
4494 EXPECT_FALSE(result.empty());
4501 InvokeSetRPCConLive(
true);
4502 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4504 constexpr std::uint16_t wrongBlockType = 0x0110;
4510 const Bytes result = conn->ApplicationReady(6.0);
4512 EXPECT_FALSE(result.empty());
4520 InvokeSetRPCConLive(
true);
4521 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4523 const auto expectedEndpoint = asio::ip::udp::endpoint(
4524 asio::ip::make_address(
"192.168.1.20"),
4529 const Bytes expectedResult = MakeApplicationReadyControlBlock();
4533 const Bytes result = conn->ApplicationReady(7.0);
4535 ASSERT_EQ(result, expectedResult);
4542 ASSERT_TRUE(response.has_value());
4544 EXPECT_EQ(response->
version, 0x04);
4546 EXPECT_FALSE(response->isLittleEndian);
4562 EXPECT_EQ(responseBlock.
sessionKey, conn->SessionKey());
4563 EXPECT_TRUE(InvokeGetRPCConLive());
4568 InvokeSetRPCConLive(
true);
4569 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4571 const auto expectedEndpoint = asio::ip::udp::endpoint(
4572 asio::ip::make_address(
"10.0.0.20"),
4577 const Bytes expectedResult = MakeApplicationReadyControlBlock();
4581 const Bytes result = conn->ApplicationReady(8.0);
4583 ASSERT_EQ(result, expectedResult);
4588 ASSERT_TRUE(response.has_value());
4607 EXPECT_EQ(responseBlock.
sessionKey, conn->SessionKey());
4612 InvokeSetRPCConLive(
true);
4613 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4620 const Bytes result = conn->ApplicationReady(9.0);
4631 InvokeSetRPCConLive(
true);
4632 InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::now());
4636 EXPECT_THROW(conn->ApplicationReady(1.0),
RPCError);
4644 InvokeSetRPCConLive(
true);
4646 const auto expectedArUuid =
StringToUuidBytes(
"deadbeef-1234-5678-9abc-001122334455");
4647 const auto expectedSessionKey =
static_cast<std::uint16_t
>(0x3456);
4649 InvokeSetRPCConArUuid(expectedArUuid);
4650 InvokeSetRPCConSessionKey(expectedSessionKey);
4655 EXPECT_FALSE(InvokeGetRPCConLive());
4672 EXPECT_EQ(release.
arUuid, expectedArUuid);
4673 EXPECT_EQ(release.
sessionKey, expectedSessionKey);
4682 InvokeSetRPCConLive(
true);
4684 const auto expectedArUuid =
StringToUuidBytes(
"deadbeef-1234-5678-9abc-001122334455");
4685 const auto expectedSessionKey =
static_cast<std::uint16_t
>(0x3456);
4687 InvokeSetRPCConArUuid(expectedArUuid);
4688 InvokeSetRPCConSessionKey(expectedSessionKey);
4710 EXPECT_EQ(release.
arUuid, expectedArUuid);
4711 EXPECT_EQ(release.
sessionKey, expectedSessionKey);
4720 InvokeSetRPCConLive(
false);
4724 EXPECT_FALSE(InvokeGetRPCConLive());
4730 InvokeSetRPCConLive(
true);
4734 EXPECT_NO_THROW(conn->Disconnect());
4736 EXPECT_FALSE(InvokeGetRPCConLive());
4742 InvokeSetRPCConLive(
true);
4744 rawTransport->
sendException = std::make_exception_ptr(std::runtime_error(
"send failed"));
4748 EXPECT_FALSE(InvokeGetRPCConLive());
4753 InvokeSetRPCConLive(
true);
4757 EXPECT_FALSE(InvokeGetRPCConLive());
4768 InvokeSetRPCConLive(
false);
4772 EXPECT_FALSE(InvokeGetRPCConLive());
Device not found via DCP.
PNIO application error with error codes.
Failed to establish RPC connection.
RPC returned a fault response.
Parsed PROFINET device information from a DCP response.
std::string name
Station name.
Connection to a PROFINET IO-Device: establishes an AR (Application Relationship) and performs acyclic...
static std::uint16_t ParseIocrResponse(const Bytes &responseData, int iocrType)
Parse an IOCRBlockRes from a CONNECT response.
std::uint16_t SessionKey() const
The session key for this connection.
static Bytes BuildIocrBlock(IocrType iocrType, const IOCRSetup &setup)
Build an IOCRBlockReq (header + API object list) for the CONNECT request.
const std::array< std::uint8_t, uuidLenght > & ArUuid() const
The AR UUID for this connection.
Deterministic RPC transport used to test RPCCon without UDP.
std::exception_ptr sendException
Optional exception rethrown by Send().
std::function< Bytes(const Bytes &)> ResponseHandler
std::deque< Bytes > incomingDatagrams
Incoming datagrams returned by Receive() in FIFO order.
asio::ip::udp::endpoint receiveEndpoint
Last endpoint supplied by Receive().
void Open(const std::string &address, std::uint16_t port, bool isListener=false) override
Open the transport and configure the remote endpoint.
std::size_t receiveCount
Number of times Receive() was called.
std::vector< Bytes > receivedRequests
Requests passed to SendReceive().
std::deque< Bytes > responses
Queued responses returned by SendReceive().
std::vector< Bytes > sentRequests
Requests passed to Send().
std::exception_ptr receiveException
Optional exception rethrown by Receive().
std::size_t closeCount
Number of times Close() was called.
Bytes receiveData
Address supplied to Open().
bool openedAsListener
Listener flag supplied to Open().
void Send(const Bytes &request) override
ResponseHandler responseHandler
Optional callback used to generate a response from each request.
void QueueIncomingDatagram(const Bytes &data)
Queues one incoming datagram for Receive().
Bytes SendReceive(const Bytes &request, std::chrono::milliseconds) override
Bytes Receive(std::chrono::milliseconds, asio::ip::udp::endpoint &remoteEndpoint) override
Wait for an incoming byte array and capture the sender's endpoint.
void SendTo(const Bytes &data, const asio::ip::udp::endpoint &targetEndpoint) override
Send a byte array of data to a specific remote Asio endpoint.
asio::ip::udp::endpoint lastSendToEndpoint
Last endpoint supplied to SendTo().
std::uint16_t openedPort
Port supplied to Open().
std::string openedAddress
Address supplied to Open().
void Close() noexcept override
Close the transport.
std::size_t sendToCount
Number of times SendTo() was called.
std::size_t openCount
Number of times Open() was called.
Bytes MakeStationNameResponse(const MacAddress &deviceMac, const MacAddress &controllerMac, const std::string &stationName)
Builds a DCP response containing a station-name block.
Bytes MakeModuleDiffPayload()
Builds a valid ModuleDiffBlock payload.
void InvokeSetRPCConArUuid(const std::array< std::uint8_t, uuidLenght > &bytes)
Bytes MakeApplicationReadyRequest(bool littleEndian, std::uint16_t blockType=BLOCK_IOD_CONTROL_APP_READY_REQ, ControlCommand controlCommand=ControlCommand::ApplicationReady)
Creates a raw DCE/RPC Application Ready request.
Bytes MakeImPayload(std::uint16_t index)
Creates a deterministic I&M payload of the required size.
bool IsAllZeroUuid(const std::array< std::uint8_t, uuidLenght > &uuid)
Checks whether every byte in a UUID is zero.
Bytes MakeDcpResponseFrame(const MacAddress &src, const MacAddress &dst, DcpServiceId serviceId, std::uint32_t xid, const Bytes &payload)
Builds an untagged Ethernet frame containing a DCP response.
std::unique_ptr< RPCCon > conn
Bytes MakeIocrResponseBlock(std::uint16_t iocrType, std::uint16_t iocrReference, std::uint16_t frameId)
Creates a serialized IOCR response block.
TestRpcTransport * rawcTransport
Bytes MakePnioErrorNrdResponse(std::uint32_t status, const Bytes &payload={})
Creates an NRD response containing a PROFINET error status.
Bytes MakeCompleteEpmTower(const std::string &interfaceUuid, std::uint16_t major, std::uint16_t minor, std::uint16_t port, const std::array< std::uint8_t, 4 > &ipAddress)
Builds a complete EPM tower for an endpoint.
Bytes MakeRpcResponseBytes(std::uint8_t packetType=PNRPCHeader::RESPONSE, std::uint16_t operationNumber=0, const Bytes &payload={})
Creates an empty successful RPC response.
Bytes MakeEpmLookupBody(const std::array< std::uint8_t, uuidLenght > &objectUuid, const std::string &annotation, const Bytes &tower)
Builds a synthetic EPM lookup response body containing one entry.
Bytes InvokeBuildAlarmCrBlock(int transport, int priority)
Calls the private RPCCon::BuildAlarmCrBlock helper.
void InvokeSetRPCConLiveMonotonic(std::chrono::steady_clock::time_point time)
void InvokeSetRPCConArUuid(std::uint8_t byte)
Bytes MakeInterfaceTowerRhs(std::uint16_t minor)
Builds the RHS of an EPM interface tower floor.
bool InvokeGetRPCConLive()
void InvokeSetRPCConLive(bool live)
PNRPCHeader InvokeSendReceive(const PNRPCHeader &request)
void ReplyToEpmLookup(asio::ip::udp::socket &server, const Bytes &response, Bytes &request)
Receives an EPM lookup request and sends a synthetic response.
dcp::DCPDeviceDescription MakeRpcTestDeviceInfo()
Creates a deterministic DCP device description suitable for RPC constructor tests.
PNRPCHeader CreateRpc(const Bytes &payload, RPCCon *conn=nullptr)
Calls the private RPC header construction helper.
std::optional< MacAddress > InvokeGetRPCConSrcMac()
Bytes MakeAlarmCrResponseBlock(std::uint16_t localAlarmReference)
Creates a valid AlarmCR response block.
Bytes MakeInterfaceTowerLhs(const std::string &uuid, std::uint16_t major)
Builds the LHS of an EPM interface tower floor.
PNNRDData InvokeCreateNrd(const Bytes &payload)
Calls the private RPCCon::CreateNrd helper.
Bytes MakeDeviceResponse(const MacAddress &deviceMac, const MacAddress &controllerMac, const std::string &stationName, const Bytes &additionalBlocks={})
Builds a DCP response containing multiple station/device blocks.
Bytes MakeEpmResponse(const Bytes &body)
Builds a minimal DCE/RPC response containing the supplied body.
Bytes MakeDcpBlock(DcpOption option, std::uint8_t suboption, const Bytes &payload)
Builds a serialized DCP block.
Bytes MakeEpmTower(const std::vector< Bytes > &floors)
Builds a complete EPM tower from encoded floors.
Bytes InvokeBuildIocrBlock(IocrType iocrType, const IOCRSetup &setup)
Calls the IOCR block construction helper.
Bytes MakeBlockHeader(BlockType blockType, std::uint16_t blockLength)
Creates an unrelated serialized block header.
Bytes MakeRawRpcHeader(bool littleEndian, std::uint8_t packetType, std::uint32_t interfaceVersion, std::uint32_t sequenceNumber, std::uint16_t operationNumber, const Bytes &payload)
Creates a raw DCE/RPC message for unit-test input.
bool IsZeroUuid(const std::array< std::uint8_t, uuidLenght > &uuid)
Checks whether a UUID byte array contains only zero bytes.
std::unique_ptr< RPCCon > MakeTestRpcConnection()
Creates a deterministic RPC connection for helper tests.
std::map< dcp::BlockKey, std::vector< std::uint8_t > > MakeStationBlocks(const std::string &stationName)
Creates a map of DCP blocks containing only a station name.
std::unique_ptr< RPCCon > MakeTestTransportRpcConnection(std::unique_ptr< IRpcTransport > transport, std::unique_ptr< IRpcTransport > ctransport=nullptr)
Creates a test RPC connection using the injected transport.
Bytes MakeWriteMultipleResponsePayload()
Builds a minimal WriteMultiple response containing one successful entry.
Bytes MakeRealIdentificationPayload()
Builds a v1.0 RealIdentificationData payload.
Bytes MakeIodResponsePayload(std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint16_t idx, const Bytes &payload)
Creates a valid IOD response block.
void InvokeSetRPCConInfo(std::uint8_t deviceHigh, std::uint8_t deviceLow, std::uint8_t vendorHigh, std::uint8_t vendorLow)
Bytes MakeDiagnosisPayload()
Builds a diagnosis block containing one ChannelDiagnosis entry.
TestRpcTransport * rawTransport
std::unique_ptr< TestRpcTransport > ctransport
Bytes MakeApplicationReadyControlBlock(std::uint16_t blockType=BLOCK_IOD_CONTROL_APP_READY_REQ, ControlCommand controlCommand=ControlCommand::ApplicationReady)
Creates a serialized IOD control block.
std::uint16_t InvokeParseIocrResponse(const Bytes &responseData, int iocrType)
Bytes InvokeSendControl(BlockType blockType, ControlCommand controlCommand, bool waitResponse=true, const Bytes &subBlocks={})
std::unique_ptr< TestRpcTransport > transport
Bytes MakeNrdRpcResponseBytes(std::uint16_t operationNumber, const Bytes &payload={})
Creates an RPC response containing an NRD response.
void InvokeSetRPCConSrcMac(std::string mac)
void InvokeRPCConCheckTimeout()
Bytes MakeEpmTowerFloor(const Bytes &lhs, const Bytes &rhs)
Builds one EPM tower floor from an LHS and RHS field.
Bytes MakeNrdResponseBytes(const Bytes &payload={})
Creates a successful NRD response wrapping the supplied payload.
Bytes MakeApplicationReadyNrd(const Bytes &block, bool littleEndian)
Creates an NRD payload containing an IOD control block.
int InvokeParseAlarmCrResponse(const Bytes &responseData)
Calls the private RPCCon::ParseAlarmCrResponse helper.
void InvokeSetRPCConSessionKey(std::uint16_t byte)
Lightweight cursor for parsing a big-endian byte buffer without copies.
void SkipTo(std::size_t offset)
Move the read position to an absolute byte offset.
std::uint32_t U32LittleEndian()
Read and consume a little-endian 32-bit value.
std::uint16_t U16LittleEndian()
Read and consume a little-endian 16-bit value.
GoogleMock implementation of IRawEthernetSocket.
constexpr DcpBlockKey MakeBlockKey(std::uint16_t option, std::uint8_t suboption)
const std::string DCP_MULTICAST_MAC
DCP Identify/Get/Set multicast destination MAC.
std::optional< RawRpcHeader > ParseRawRpcHeader(const Bytes &data)
Parses a raw network byte vector into a structured DCE/RPC v4 Connectionless Header.
const std::string UUID_PNIO_PARAMETER_SERVER
PROFINET IO-ParameterServer interface UUID.
std::array< std::uint8_t, uuidLenght > BuildObjectUuid(std::uint8_t b10, std::uint8_t b11, std::uint8_t b12, std::uint8_t b13, std::uint8_t b14, std::uint8_t b15)
Builds a PROFINET object UUID.
constexpr std::uint16_t BLOCK_IOD_CONTROL_APP_READY_REQ
Block type: IODControlReqAppReady.
constexpr std::uint16_t BLOCK_PRM_BEGIN_REQ
Block type: PrmBeginReq.
std::string UuidBytesToString(const std::array< std::uint8_t, uuidLenght > &data)
Format a 16-byte DCE/RPC UUID as a canonical string.
const std::string UUID_PNIO_SUPERVISOR
PROFINET IO-Supervisor interface UUID.
dcp::DCPDeviceDescription GetStationInfo(IRawEthernetSocket &sock, const MacAddress &src, const std::string &name, double timeoutSec=DEFAULT_TIMEOUT *2)
Resolve a device by PROFINET station name via DCP.
constexpr std::uint16_t CONTROL_CMD_PRM_BEGIN
Control command bit: PrmBegin.
const std::string UUID_PNIO_CONTROLLER
PROFINET IO-Controller interface UUID.
const std::string UUID_EPM_V4
Endpoint Mapper interface UUID.
std::optional< EPMEndpoint > ParseEpmTower(const Bytes &towerData)
Parses a DCE/RPC EPM tower into a PROFINET endpoint.
constexpr std::uint16_t CONTROL_CMD_PRM_END
Control command bit: PrmEnd.
const std::string UUID_PNIO_DEVICE
PROFINET IO-Device interface UUID.
constexpr double CONNECTION_TIMEOUT
Maximum idle time in seconds before an AR is considered to need re-connection.
constexpr std::uint32_t EPM_INQUIRY_INTERFACE
EPM inquiry type: specific interface.
constexpr std::uint16_t CONTROL_CMD_READY_FOR_RT_CLASS_3
Control command bit: ReadyForRTClass3.
constexpr std::uint16_t BLOCK_IOD_CONTROL_APP_READY_RES
Block type: IODControlResAppReady.
std::array< std::uint8_t, uuidLenght > StringToUuidBytes(const std::string &uuidStr)
Parse a canonical UUID string into 16 raw DCE/RPC UUID bytes.
TEST_F(RpcTests, UuidBytesToStringStringToUuidBytesRoundtrip)
constexpr std::uint32_t EPM_LOOKUP
EPM operation: Lookup.
void PutFixed(std::vector< std::uint8_t > &out, const std::array< std::uint8_t, N > &data)
Append a fixed-size byte array to a buffer.
void PutU8(std::vector< std::uint8_t > &out, std::uint8_t value)
Append a single byte to a buffer.
void PutU16(std::vector< std::uint8_t > &out, std::uint16_t value)
Append a 16-bit value to a buffer in big-endian order.
void PutLittleEndian(std::vector< std::uint8_t > &out, T value)
Append an unsigned integral value to a buffer in little-endian order.
void PutU32(std::vector< std::uint8_t > &out, std::uint32_t value)
Append a 32-bit value to a buffer in big-endian order.
void PutBigEndian(std::vector< std::uint8_t > &out, T value)
Append an unsigned integral value to a buffer in big-endian order.
BlockType
PROFINET block types.
@ ModuleDifference
Module difference block.
@ ReleaseRequest
IOD Release request.
@ AlarmCrResponse
AlarmCR block response.
constexpr int uuidLenght
Constant lenght of a UUID.
constexpr int blockHeaderLength
Constant lenght of a blockHeader.
constexpr std::uint16_t PD_REAL_DATA
PD Real Data.
constexpr std::uint16_t MODULE_DIFF_BLOCK
Module Diff Block.
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
constexpr std::uint16_t IP_ETHERTYPE
EtherType value identifying IP frames (0x8000).
MacAddress String2Mac(const std::string &macStr)
Parse a colon-separated MAC address string.
IocrType
PROFINET IOCR types.
ControlCommand
PROFINET IOD control command values.
@ ApplicationReady
Application Ready command.
@ Release
Release command.
@ ReadyForRtClass3
Ready for RT Class 3 command.
@ PrmBegin
Prm Begin command.
constexpr std::uint16_t MODULE_STATE_PROPER_MODULE
Module State Proper Module.
constexpr std::uint16_t REAL_ID_API
Real ID API.
@ GetSet
DCP GET/SET request or response.
std::string ToHex(const std::uint8_t *data, std::size_t len)
Hex-encode a byte buffer.
@ Device
Device identification.
constexpr std::uint16_t PROFINET_ETHERTYPE
EtherType value identifying PROFINET frames (0x8892).
constexpr std::uint16_t SUBMODULE_STATE_OK
Submodule State Ok.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
DcpServiceId
DCP service identifiers.
@ Identify
IDENTIFY service.
@ ResponseSuccess
Successful response.
asio::ip::udp::endpoint remoteEndpoint
Shared data types used by the PROFINET DCE/RPC layer.
PROFINET DCE/RPC protocol structures and IO-Device connection API.
AlarmCRBlockReq: requests establishment of the alarm connection (part of the CONNECT request).
static constexpr std::uint16_t DEFAULT_MAX_ALARM_DATA_LENGTH
Default maximum alarm data length in bytes.
static constexpr std::uint16_t DEFAULT_TAG_HEADER_LOW
Default low word of the alarm VLAN tag header.
static constexpr BlockType BLOCK_TYPE
Block type identifier for AlarmCRBlockReq.
static PNAlarmCRBlockReq Parse(const Bytes &data)
Parse an AlarmCRBlockReq from raw bytes.
static constexpr std::uint16_t DEFAULT_TAG_HEADER_HIGH
Default high word of the alarm VLAN tag header.
std::uint16_t alarmCrType
Alarm CR type (always 1: Alarm).
std::uint16_t etherTypeLT
EtherType used for alarm frames (0x8892).
std::uint32_t alarmCrProperties
Alarm CR property flags (transport, priority).
static constexpr std::size_t kSize
Size in bytes of this block.
static constexpr std::uint16_t DEFAULT_RTA_RETRIES
Default number of RTA retries.
std::uint16_t localAlarmReference
Local alarm reference chosen by the controller.
std::uint16_t alarmCrTagHeaderHigh
High word of the alarm VLAN tag header.
std::uint16_t maxAlarmDataLength
Maximum alarm data length in bytes.
std::uint16_t alarmCrTagHeaderLow
Low word of the alarm VLAN tag header.
std::uint16_t rtaRetries
Number of RTA retransmission attempts.
std::uint16_t rtaTimeoutFactor
RTA (real-time acyclic) retransmission timeout factor.
static constexpr std::uint16_t DEFAULT_RTA_TIMEOUT_FACTOR
Default RTA timeout factor.
AlarmCRBlockRes: device's response confirming the alarm connection (part of the CONNECT response).
Bytes ToBytes() const
Serialize this block back to raw bytes.
std::array< std::uint8_t, blockHeaderLength > blockHeader
Nested 6-byte block header.
static constexpr std::size_t kSize
Size in bytes of this block.
std::uint16_t localAlarmReference
Local alarm reference assigned by the device.
DCP request block: Option(1) + SubOption(1) + Length(2) + payload(Length).
std::uint16_t length
Length in bytes of Payload.
std::uint8_t suboption
DCP suboption code.
Bytes payload
Block payload.
DcpOption option
DCP option code.
Bytes ToBytes() const
Serialize this block back to raw bytes.
static constexpr std::pair< std::uint8_t, std::uint8_t > NAME_OF_STATION
Block option/suboption: station name.
static constexpr std::uint16_t BLOCK_TYPE
Block type identifier for IOCRBlockRes.
static constexpr std::size_t kSize
Size in bytes of this block.
Control block used for Release/PrmBegin/PrmEnd/ApplicationReady/RTClass3 requests.
std::array< std::uint8_t, uuidLenght > arUuid
AR UUID this control operation applies to.
std::uint16_t controlBlockProperties
Control block property flags.
static constexpr std::size_t kSize
Size in bytes of this block.
std::uint16_t padding1
Reserved/padding.
static PNIODReleaseBlock Parse(const Bytes &data)
Parse a control block from raw bytes.
std::uint16_t sessionKey
Session key matching the AR.
std::array< std::uint8_t, blockHeaderLength > blockHeader
Nested 6-byte block header.
ControlCommand controlCommand
Control command bitmask (PrmEnd/AppReady/Release/Done/...).
std::uint16_t padding2
Reserved/padding.
Bytes ToBytes() const
Serialize this block back to raw bytes.
static constexpr std::size_t kSize
Size in bytes of this record.
static constexpr std::uint16_t IDX
Record data index for I&M0.
static constexpr std::uint16_t IDX
Record data index for I&M1.
static constexpr std::size_t kSize
Size in bytes of this record.
static constexpr std::uint16_t IDX
Record data index for I&M2.
static constexpr std::size_t kSize
Size in bytes of this record.
static constexpr std::uint16_t IDX
Record data index for I&M3.
static constexpr std::size_t kSize
Size in bytes of this record.
static constexpr std::uint16_t IDX
Record data index for I&M4.
static constexpr std::size_t kSize
Size in bytes of this record.
static constexpr std::uint16_t IDX
Record data index for I&M5.
static constexpr std::size_t kSize
Size in bytes of this record.
NRD (Network Representation Data) wrapper carrying the actual IOD payload.
Bytes payload
The wrapped IOD data.
static PNNRDData Parse(const Bytes &data)
Parse an NRD wrapper and payload from raw bytes.
Bytes ToBytes() const
Serialize this wrapper and payload back to raw bytes.
std::uint32_t argsLength
Length in bytes of the arguments (usually equals ActualCount).
std::uint32_t offset
Byte offset into the logical result (0 unless fragmented).
std::uint32_t argsMaximumStatus
Maximum status/argument buffer size accepted by the caller.
std::uint32_t maximumCount
Maximum number of bytes the caller can accept.
std::uint32_t actualCount
Actual length in bytes of Payload.
All I&M records a device supports, read in one call.
std::optional< PNInM7 > im7
I&M7 (reserved), if supported.
std::optional< PNInM15 > im15
I&M15 (reserved), if supported.
std::optional< PNInM9 > im9
I&M9 (reserved), if supported.
std::optional< PNInM14 > im14
I&M14 (reserved), if supported.
std::optional< PNInM1 > im1
I&M1 (tag function/location), if supported.
std::optional< PNInM5 > im5
I&M5 (free-text annotation), if supported.
std::optional< PNInM10 > im10
I&M10 (reserved), if supported.
std::optional< PNInM3 > im3
I&M3 (free-text descriptor), if supported.
std::optional< PNInM6 > im6
I&M6 (reserved), if supported.
std::optional< PNInM13 > im13
I&M13 (reserved), if supported.
std::optional< PNInM11 > im11
I&M11 (reserved), if supported.
std::optional< PNInM2 > im2
I&M2 (installation date), if supported.
std::optional< PNInM12 > im12
I&M12 (reserved), if supported.
std::optional< PNInM4 > im4
I&M4 (PROFIsafe signature), if supported.
std::optional< PNInM0 > im0
I&M0 (mandatory identification data).
std::optional< PNInM8 > im8
I&M8 (reserved), if supported.
One RPC endpoint advertised by a device's Endpoint Mapper.
std::string InterfaceName() const
Human-readable interface name.
std::string interfaceUuid
Interface UUID this endpoint serves.
Configuration for establishing cyclic IO alongside an AR.
std::uint16_t watchdogFactor
Watchdog factor (missed-frame tolerance before a timeout fault).
std::vector< IOSlot > slots
Slots to include in the cyclic data frames.
std::vector< std::string > Validate() const
Sanity-check this configuration.
double CycleTimeMs() const
Compute the cycle time in milliseconds.
std::uint16_t reductionRatio
Reduction ratio relative to the send clock.
std::uint16_t sendClockFactor
Send clock base factor (31.25us units).
std::uint32_t CycleTimeUs() const
Compute the cycle time in microseconds from SendClockFactor/ReductionRatio.
Configuration options for an RPC connection.
double timeoutSec
Timeout in seconds for RPC operations.
std::uint16_t rpcPort
UDP port used for RPC communication.
std::uint16_t frameId
Ethernet FrameID.