28std::array<std::uint8_t, uuidLenght> RandomUuidBytes()
30 static thread_local std::mt19937 rng{std::random_device{}()};
31 static constexpr int maxByteValue = 255;
32 static thread_local std::uniform_int_distribution<int> dist(0, maxByteValue);
33 std::array<std::uint8_t, uuidLenght> out{};
36 b =
static_cast<std::uint8_t
>(dist(rng));
41std::uint16_t RandomNonzeroU16()
43 static thread_local std::mt19937 rng{std::random_device{}()};
45 static constexpr int maxU16Value = 0xFFFF;
46 static thread_local std::uniform_int_distribution<int> dist(0, maxU16Value);
47 const int v = dist(rng);
48 return static_cast<std::uint16_t
>(v == 0 ? 1 : v);
56std::array<std::uint8_t, uuidLenght>
BuildObjectUuid(std::uint8_t b10, std::uint8_t b11, std::uint8_t b12, std::uint8_t b13,
57 std::uint8_t b14, std::uint8_t b15)
63 static constexpr std::uint8_t pnPrefixByte0 = 0xDE;
64 static constexpr std::uint8_t pnPrefixByte1 = 0xA0;
65 static constexpr std::uint8_t pnPrefixByte2 = 0x00;
66 static constexpr std::uint8_t pnPrefixByte3 = 0x00;
69 static constexpr std::size_t uuidIdxTimeLow0 = 0;
70 static constexpr std::size_t uuidIdxTimeLow1 = 1;
71 static constexpr std::size_t uuidIdxTimeLow2 = 2;
72 static constexpr std::size_t uuidIdxTimeLow3 = 3;
74 static constexpr std::size_t uuidIdxTimeMid0 = 4;
75 static constexpr std::size_t suffixBytesToCopy = 6;
77 static constexpr std::size_t uuidIdxNode0 = 10;
78 static constexpr std::size_t uuidIdxNode1 = 11;
79 static constexpr std::size_t uuidIdxNode2 = 12;
80 static constexpr std::size_t uuidIdxNode3 = 13;
81 static constexpr std::size_t uuidIdxNode4 = 14;
82 static constexpr std::size_t uuidIdxNode5 = 15;
84 std::array<std::uint8_t, uuidLenght> objectUuid{};
87 objectUuid[uuidIdxTimeLow0] = pnPrefixByte0;
88 objectUuid[uuidIdxTimeLow1] = pnPrefixByte1;
89 objectUuid[uuidIdxTimeLow2] = pnPrefixByte2;
90 objectUuid[uuidIdxTimeLow3] = pnPrefixByte3;
93 std::ranges::copy(
kPnUuidSuffix | std::views::take(suffixBytesToCopy),
94 (objectUuid | std::views::drop(uuidIdxTimeMid0)).begin());
97 objectUuid[uuidIdxNode0] = b10;
98 objectUuid[uuidIdxNode1] = b11;
99 objectUuid[uuidIdxNode2] = b12;
100 objectUuid[uuidIdxNode3] = b13;
101 objectUuid[uuidIdxNode4] = b14;
102 objectUuid[uuidIdxNode5] = b15;
115 const std::uint16_t clockSeq = r.
U16();
117 const auto node = r.
Fixed<6U>();
120 const std::string out = std::format(
"{:08x}-{:04x}-{:04x}-{:04x}-{}",
121 timeLow, timeMid, timeHi, clockSeq,
122 ToHex(node.data(), node.size()));
129 static constexpr std::size_t expectedHexLength = 32;
130 static constexpr int hexBase = 16;
131 static constexpr std::size_t singleByteHexLength = 2;
134 static constexpr std::size_t timeLowOffset = 0U;
135 static constexpr std::size_t timeLowLength = 8U;
137 static constexpr std::size_t timeMidOffset = 8U;
138 static constexpr std::size_t timeMidLength = 4U;
140 static constexpr std::size_t timeHiOffset = 12U;
141 static constexpr std::size_t timeHiLength = 4U;
143 static constexpr std::size_t clockSeqOffset = 16U;
144 static constexpr std::size_t clockSeqLength = 4U;
146 static constexpr std::size_t nodeDataStartHexOffset = 20;
149 hex.reserve(expectedHexLength);
150 for (
const char c : uuidStr)
158 if (hex.size() != expectedHexLength)
160 throw std::invalid_argument(
"Invalid UUID string: " + uuidStr);
164 auto parseValue = [&hex](std::size_t offset, std::size_t
length) -> std::uint32_t
166 auto hexString = hex.substr(offset,
length);
167 auto hexLong = std::stoul(hexString,
nullptr, hexBase);
168 return static_cast<std::uint32_t
>(hexLong);
171 const std::uint32_t timeLow = parseValue(timeLowOffset, timeLowLength);
172 const std::uint16_t timeMid =
static_cast<std::uint16_t
>(parseValue(timeMidOffset, timeMidLength));
173 const std::uint16_t timeHi =
static_cast<std::uint16_t
>(parseValue(timeHiOffset, timeHiLength));
174 const std::uint16_t clockSeq =
static_cast<std::uint16_t
>(parseValue(clockSeqOffset, clockSeqLength));
188 for (std::size_t i = 0U; i < 6U; ++i)
190 auto hexString = hex.substr(nodeDataStartHexOffset + i * singleByteHexLength, singleByteHexLength);
191 auto hexLong = std::stoul(hexString,
nullptr, hexBase);
192 const auto nodeByte =
static_cast<std::uint8_t
>(hexLong);
193 out.push_back(nodeByte);
198 throw std::logic_error(
"UUID serialization produced an invalid size");
201 std::array<std::uint8_t, uuidLenght> result{};
202 std::ranges::copy(out, result.begin());
214 std::ranges::transform(lower, lower.begin(), ::tolower);
218 return "PNIO-Device";
222 return "PNIO-Controller";
226 return "PNIO-Supervisor";
230 return "PNIO-ParameterServer";
241 if (towerData.size() <
sizeof(std::uint16_t))
251 for (std::uint16_t floorIdx = 0; floorIdx < floorCount; ++floorIdx)
253 if (r.
Remaining() < 2U *
sizeof(std::uint16_t))
260 if (r.
Remaining() < lhsLen +
sizeof(std::uint16_t))
281 const std::uint8_t protocolId = lhsData[0];
283 if (protocolId == 0x0D && lhsLen >= 19U)
296 if (rhsLen >=
sizeof(std::uint16_t))
303 else if (protocolId == 0x0A)
307 else if (protocolId == 0x08 && rhsLen >=
sizeof(std::uint16_t))
310 endpoint.
port = rhsReader.
U16();
312 else if (protocolId == 0x09 && rhsLen >= 4U)
315 std::to_string(rhsData[0]) +
"." +
316 std::to_string(rhsData[1]) +
"." +
317 std::to_string(rhsData[2]) +
"." +
318 std::to_string(rhsData[3]);
330std::vector<EPMEndpoint>
RPCCon::EpmLookup(
const std::string& ip, std::uint16_t port,
double timeoutSec,
331 std::optional<std::string> interfaceFilter)
333 std::vector<EPMEndpoint> results;
337 transport.
Open(ip, port);
340 auto activityUuidArr = RandomUuidBytes();
341 Bytes activityUuidBytes(activityUuidArr.begin(), activityUuidArr.end());
342 auto objectUuidBytes = std::array<std::uint8_t, uuidLenght>{};
349 header.push_back(flags1);
350 header.push_back(0x00);
352 header.push_back(0x00);
353 header.push_back(0x00);
359 wire::PutLittleEndian<std::uint32_t>(header, 0);
360 wire::PutLittleEndian<std::uint32_t>(header, 3);
361 wire::PutLittleEndian<std::uint32_t>(header, 0);
363 wire::PutLittleEndian<std::uint16_t>(header,
EPM_LOOKUP);
368 const std::size_t lengthOfBodyOffset = header.size();
369 wire::PutLittleEndian<std::uint16_t>(header, 0);
370 wire::PutLittleEndian<std::uint16_t>(header, 0);
381 body.insert(body.end(), ifaceBytes.begin(), ifaceBytes.end());
382 wire::PutLittleEndian<std::uint16_t>(body, 1);
383 wire::PutLittleEndian<std::uint16_t>(body, 0);
388 wire::PutLittleEndian<std::uint16_t>(body, 0);
389 wire::PutLittleEndian<std::uint16_t>(body, 0);
391 wire::PutLittleEndian<std::uint32_t>(body, 0);
392 wire::PutLittleEndian<std::uint32_t>(body, 0);
393 constexpr std::uint32_t maxEnts = 100U;
396 wire::PutLittleEndian<std::uint16_t>(header.data() + lengthOfBodyOffset, body.size());
400 Bytes request = header;
401 request.insert(request.end(), body.begin(), body.end());
403 auto timeoutInMilliSeconds = std::chrono::floor<std::chrono::milliseconds>(std::chrono::duration<double>(timeoutSec));
412 const std::uint8_t respType = data[1];
413 if (respType == 0x03 || respType != 0x02)
418 constexpr std::uint16_t kBodyLenIndex = 74U;
427 if (bodyData.size() < 12)
432 std::size_t offset =
sizeof(std::uint32_t);
437 offset +=
sizeof(std::uint32_t);
439 offset += 3U *
sizeof(std::uint32_t);
441 for (std::uint32_t i = 0; i < numEnts; ++i)
453 if (bodyData.size() < offset +
sizeof(std::uint32_t))
457 offset +=
sizeof(std::uint32_t);
459 if (bodyData.size() < offset +
sizeof(std::uint32_t))
465 offset +=
sizeof(std::uint32_t);
467 std::string annotation;
468 if (annotationLen > 0 && offset + annotationLen <= bodyData.size())
470 const std::size_t end = offset + annotationLen;
471 std::size_t trim = end;
472 while (trim > offset && bodyData[trim - 1] == 0)
476 annotation = std::string(bodyData.begin() + offset, bodyData.begin() + trim);
478 offset += annotationLen;
479 offset = (offset + 3) & ~
static_cast<std::size_t
>(3);
481 if (offset + 4 > bodyData.size())
487 offset +=
sizeof(std::uint32_t);
489 if (bodyData.size() < offset + towerLen)
493 const Bytes towerData(bodyData.begin() + offset, bodyData.begin() + offset + towerLen);
495 offset = (offset + 3) & ~
static_cast<std::size_t
>(3);
500 endpoint->objectUuid = entryObjectUuid;
501 endpoint->annotation = annotation;
502 results.push_back(*endpoint);
511 const std::string& name,
515 Bytes(name.begin(), name.end()));
518 if (responses.empty())
522 for (
const auto& [
Mac, Blocks] : responses)
525 std::string lowerDescName = desc.
name;
526 std::string lowerTarget = name;
527 std::ranges::transform(lowerDescName, lowerDescName.begin(), ::tolower);
528 std::ranges::transform(lowerTarget, lowerTarget.begin(), ::tolower);
529 if (lowerDescName == lowerTarget)
537 const auto& [
Mac, Blocks] = *responses.begin();
548 std::vector<std::string> warnings;
552 warnings.emplace_back(
"Cycle time too fast - sub-1ms is impractical without hardware support");
556 warnings.emplace_back(
"Cycle time may cause jitter (8ms+ recommended)");
560 warnings.emplace_back(
"Watchdog factor too low (use 6+)");
564 warnings.emplace_back(
"No IO slots configured");
574 std::unique_ptr<IRpcTransport> rpcTransport,
575 std::unique_ptr<IRpcTransport> ccontrolTransport,
576 asio::io_context* context)
578 timeout(options.timeoutSec),
579 sessionKey(RandomNonzeroU16()),
580 rpcContext(context ? nullptr : std::make_unique<asio::io_context>()),
582 ioContext(context ? *context : *rpcContext),
583 rpcTransport(std::move(rpcTransport)),
584 ccontrolTransport(std::move(ccontrolTransport))
586 if (this->rpcTransport ==
nullptr)
588 this->rpcTransport = std::make_unique<RpcTransport>(
ioContext);
590 if (this->ccontrolTransport ==
nullptr)
592 this->ccontrolTransport = std::make_unique<RpcTransport>(
ioContext);
595 arUuid = RandomUuidBytes();
604 const std::string anyIp = asio::ip::address_v4::any().to_string();
607 this->ccontrolTransport->Open(anyIp,
RPC_PORT,
true);
633 h.
lengthOfBody =
static_cast<std::uint16_t
>(nrd.size());
655 auto properties =
static_cast<std::uint32_t
>((transport << 1) | priority);
681 std::size_t offset = 0;
685 const std::size_t blockSize = 4 + hdr.blockLength;
687 if (offset + blockSize > responseData.size())
693 const Bytes blockBytes(
694 responseData.begin() + offset,
695 responseData.begin() + offset + blockSize);
697 return res.localAlarmReference;
699 offset += 4 + hdr.blockLength;
733 throw std::invalid_argument(
"BuildIocrBlock: unsupported IOCR type " + std::string(magic_enum::enum_name(iocrType)));
744 std::vector<IOCRAPIObject> ioDataObjects;
745 std::vector<IOCRAPIObject> iocsObjects;
747 std::uint16_t frameOffset = 0;
748 std::uint16_t dapOffset = 0;
749 std::uint16_t processByteLength = 0;
755 << std::hex << slot.subslot << std::dec
756 <<
" in=" << slot.inputLength
757 <<
" out=" << slot.outputLength
762 if ((slot.inputLength >= 0 && slot.outputLength == 0) || (slot.inputLength > 0 && slot.outputLength > 0))
764 ioDataObjects.push_back({.slotNumber = slot.slot, .subslotNumber = slot.subslot, .frameOffset = frameOffset});
765 processByteLength = slot.inputLength;
767 frameOffset += slot.inputLength;
770 if (slot.outputLength > 0)
772 iocsObjects.push_back({.slotNumber = slot.slot, .subslotNumber = slot.subslot, .frameOffset = frameOffset});
778 if ((slot.inputLength >= 0 && slot.outputLength == 0) || (slot.inputLength > 0 && slot.outputLength > 0))
780 iocsObjects.push_back({.slotNumber = slot.slot, .subslotNumber = slot.subslot, .frameOffset = frameOffset});
781 processByteLength = slot.outputLength;
784 if (slot.outputLength > 0)
786 ioDataObjects.push_back({.slotNumber = slot.slot, .subslotNumber = slot.subslot, .frameOffset = frameOffset});
788 frameOffset += slot.outputLength;
796 const std::uint16_t numberOfIocsBytes = iocsObjects.size();
797 const std::uint16_t numberOfIopsBytes = ioDataObjects.size();
799 const std::uint16_t dataLength = numberOfIocsBytes + processByteLength + numberOfIopsBytes;
954 if (ioDataObjects.size() > std::numeric_limits<std::uint16_t>::max())
956 throw std::invalid_argument(
"BuildIocrBlock: too many IODataObjects");
959 if (iocsObjects.size() > std::numeric_limits<std::uint16_t>::max())
961 throw std::invalid_argument(
"BuildIocrBlock: too many IOCSObjects");
969 wire::PutU16(apiBlock,
static_cast<std::uint16_t
>(ioDataObjects.size()));
973 const Bytes bytes =
object.ToBytes();
974 apiBlock.insert(apiBlock.end(), bytes.begin(), bytes.end());
977 wire::PutU16(apiBlock,
static_cast<std::uint16_t
>(iocsObjects.size()));
981 const Bytes bytes =
object.ToBytes();
982 apiBlock.insert(apiBlock.end(), bytes.begin(), bytes.end());
1001 if (totalSize < 4 || totalSize - 4 > std::numeric_limits<std::uint16_t>::max())
1003 throw std::invalid_argument(
"BuildIocrBlock: IOCR block is too large");
1006 blockHeader.
blockLength =
static_cast<std::uint16_t
>(totalSize - 4);
1015 std::ranges::copy(blockHeaderBytes, iocrHeader.
blockHeader.begin());
1017 iocrHeader.
iocrType =
static_cast<std::uint16_t
>(iocrType);
1018 iocrHeader.
iocrReference =
static_cast<std::uint16_t
>(iocrType);
1024 iocrHeader.
dataLength =
static_cast<std::uint16_t
>(std::max<std::uint16_t>(40, dataLength));
1057 iocrHeader.
phase = 8;
1074 out.insert(out.end(), apiBlock.begin(), apiBlock.end());
1081 Bytes expectedSlotModuleBlocks;
1083 std::uint16_t previousSlotnr = 0;
1084 for (
const auto& slot : setup.
slots)
1086 if (previousSlotnr != slot.slot)
1088 previousSlotnr = slot.slot;
1089 const auto slotBytes = builder.
ToBytes();
1090 expectedSlotModuleBlocks.insert(expectedSlotModuleBlocks.end(), slotBytes.begin(), slotBytes.end());
1093 const bool hasInput = slot.inputLength > 0;
1094 const bool hasOutput = slot.outputLength > 0;
1099 if (hasInput && hasOutput)
1122 slot.submoduleIdent,
1123 static_cast<std::uint16_t
>(submoduleType),
1127 const auto slotBytes = builder.
ToBytes();
1128 expectedSlotModuleBlocks.insert(expectedSlotModuleBlocks.end(), slotBytes.begin(), slotBytes.end());
1129 return expectedSlotModuleBlocks;
1134 std::size_t offset = 0;
1140 const Bytes blockBytes(responseData.begin() + offset, responseData.end());
1144 if (res.iocrType == iocrType)
1150 catch (
const std::exception&)
1155 offset += 4 + hdr.blockLength;
1163 auto timeoutInMilliSeconds = std::chrono::floor<std::chrono::milliseconds>(std::chrono::duration<double>(
timeout));
1168 const Bytes responseBytes =
rpcTransport->SendReceive(rpcBytes, timeoutInMilliSeconds);
1172 throw RPCError(
"Failed to parse RPC response: data too short (" + std::to_string(responseBytes.size()) +
" bytes)");
1178 catch (
const std::exception& e)
1180 throw RPCError(std::string(
"Failed to parse RPC response: ") + e.what());
1200 const std::string errorMessage = std::format(
"Unexpected RPC packet type: 0x{:02X}", resp.
packetType);
1214 auto elapsed = std::chrono::duration<double>(std::chrono::steady_clock::now() -
liveMonotonic).count();
1225 std::optional<IOCRSetup> setup)
1231 throw std::invalid_argument(
"srcMac required for initial connection");
1237 arUuid = RandomUuidBytes();
1247 throw std::invalid_argument(
"No source MAC address available");
1253 const std::uint32_t arProperties = this->
iocrSetup
1258 std::string initiatorStationName =
"rhodium-profinet-connector";
1266 const std::uint16_t arType = this->
iocrSetup ? 0x0001 : 0x0006;
1278 ar.
stationNameLength =
static_cast<std::uint16_t
>(initiatorStationName.length());
1279 const Bytes initiatorStationNameInBytes(initiatorStationName.begin(), initiatorStationName.end());
1287 nrdPayload.insert(nrdPayload.end(), inputIocr.begin(), inputIocr.end());
1290 nrdPayload.insert(nrdPayload.end(), outputIocr.begin(), outputIocr.end());
1293 nrdPayload.insert(nrdPayload.end(), expectedSubmodule.begin(), expectedSubmodule.end());
1296 nrdPayload.insert(nrdPayload.end(), alarmCrData.begin(), alarmCrData.end());
1299 else if (withAlarmCr)
1302 nrdPayload.insert(nrdPayload.end(), alarmCrData.begin(), alarmCrData.end());
1316 throw RPCConnectionError(std::string(
"Connect rejected by device: ") + pnioErr.what());
1321 std::optional<ConnectResult> result;
1332 if (result.has_value())
1434void RPCCon::Write(std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint16_t idx,
1454 iod.
length =
static_cast<std::uint32_t
>(data.size());
1545 auto tryRead = [&](
auto& field,
auto memberFn)
1549 field = (this->*memberFn)(slot, subslot);
1557 catch (
const std::exception&)
1593 for (
const auto& w : writes)
1595 builder.
AddWrite(w.slot, w.subslot, w.index, w.data, w.api);
1609 iod.
api = 0xFFFFFFFF;
1645 if (data.size() > 6)
1673 static const std::vector<std::tuple<std::uint16_t, std::uint16_t, std::uint16_t>> kDiagnosisProbes = {
1682 std::map<std::uint16_t, diagnosis::DiagnosisData> results;
1683 for (
const auto& [idx, Slot, Subslot] : kDiagnosisProbes)
1690 results[idx] = diag;
1724 std::uint16_t slot, std::uint16_t subslot, std::optional<std::vector<std::uint16_t>> customIndices)
1726 std::vector<std::pair<std::uint16_t, std::string>> probeList;
1730 for (
auto idx : *customIndices)
1739 probeList.insert(probeList.end(), src.begin(), src.end());
1754 std::map<std::uint16_t, IndexProbeResult> results;
1755 std::vector<std::uint16_t> seen;
1756 for (
const auto& [idx,
Name] : probeList)
1758 if (std::ranges::find(seen, idx) != seen.end())
1762 seen.push_back(idx);
1798 bool waitResponse,
const Bytes& subBlocks)
1822 nrdPayload.insert(nrdPayload.end(), subBlocks.begin(), subBlocks.end());
1862 return std::nullopt;
1910 auto timeoutInMilliSeconds = std::chrono::floor<std::chrono::milliseconds>(std::chrono::duration<double>(timeoutSec));
1919 asio::ip::udp::endpoint senderEndpoint;
1935 const Bytes& nrdPayload = hdr->payload;
1936 if (nrdPayload.size() < 20)
1942 const std::uint32_t nrdArgsMaximum = hdr->isLittleEndian ? r1.
U32LittleEndian()
1949 Bytes nrdBody(nrdPayload.begin() + 20, nrdPayload.end());
1950 if (nrdBody.size() < 32)
1956 const std::uint16_t blockType = r.
U16();
1961 const std::uint16_t controlCmd = r.
U16();
1963 std::cout <<
"Parsed BlockType: 0x" << std::hex << blockType
1964 <<
", ControlCmd: 0x" << controlCmd << std::dec <<
"\n";
1968 std::cerr <<
"Ignoring non-AppReady block: 0x" << std::hex << blockType <<
"\n";
1982 constexpr std::uint16_t kApplicationReadyBlockLength = 28;
1984 respBlock.
blockLength = kApplicationReadyBlockLength;
1999 const auto respNrdLen =
static_cast<std::uint32_t
>(respNrdPayload.size());
2002 if (hdr->isLittleEndian)
2004 wire::PutLittleEndian<std::uint32_t>(respNrd, 0);
2007 wire::PutLittleEndian<std::uint32_t>(respNrd, 0);
2040 respBytes.push_back(hdr->version);
2043 constexpr std::uint8_t flag2 = 0x00;
2044 respBytes.push_back(flag1);
2045 respBytes.push_back(flag2);
2055 respBytes.push_back(hdr->serialHigh);
2063 if (hdr->isLittleEndian)
2065 wire::PutLittleEndian<std::uint32_t>(respBytes, 0);
2072 wire::PutLittleEndian<std::uint16_t>(respBytes, 0);
2082 wire::PutU16(respBytes,
static_cast<std::uint16_t
>(respNrd.size()));
2086 respBytes.push_back(0);
2087 respBytes.push_back(hdr->serialLow);
Keeps an Asio I/O context running on a dedicated worker thread.
Device not found via DCP.
Abstract interface for raw Ethernet frame transport.
PNIO application error with error codes.
std::uint8_t errorCode1
Raw ErrorCode1 byte (category / block type).
std::uint8_t errorCode2
Raw ErrorCode2 byte (specific error within the category).
static PNIOError FromArgsStatus(std::uint32_t argsStatus)
Create a PNIOError from the little-endian ArgsStatus field carried in a DCE/RPC PDU.
Failed to establish RPC connection.
RPC returned a fault response.
ExpectedSubmoduleBlockReq (0x0104) builder.
Bytes ToBytes() const
Serialize the complete block to raw bytes.
ExpectedSubmoduleBlockReq & AddSubmodule(std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint32_t moduleIdent, std::uint32_t submoduleIdent, std::uint16_t submoduleType=0, std::uint16_t inputLength=0, std::uint16_t outputLength=0)
Add a single submodule.
Builder for IODWriteMultipleReq packets (index 0xE040).
Bytes Build() const
Build the complete IODWriteMultipleReq packet.
IODWriteMultipleBuilder & AddWrite(std::uint16_t slot, std::uint16_t subslot, std::uint16_t index, Bytes data, std::uint32_t api=0)
Add a write operation.
static constexpr std::uint16_t writeMultipleIndex
Record data index for WriteMultiple (0xE040).
Parsed PROFINET device information from a DCP response.
std::uint8_t vendorLow
Low byte of the PROFINET vendor ID.
std::string name
Station name.
std::uint8_t vendorHigh
High byte of the PROFINET vendor ID.
std::uint8_t deviceLow
Low byte of the PROFINET device ID.
std::uint8_t deviceHigh
High byte of the PROFINET device ID.
std::string ip
IPv4 address, or "0.0.0.0" if unset.
PNInM13 ReadIm13(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M13 (reserved).
PNInM0 ReadIm0(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M0 (mandatory identification data).
static std::uint16_t ParseIocrResponse(const Bytes &responseData, int iocrType)
Parse an IOCRBlockRes from a CONNECT response.
std::uint32_t sequenceNumber
Monotonically increasing RPC sequence number.
blocks::PDRealData ReadPdRealData() override
Read and parse the device's PDRealData (physical topology).
std::array< std::uint8_t, uuidLenght > arUuid
AR UUID for this connection.
blocks::ModuleDiffBlock ReadModuleDiff() override
Read and parse the device's ModuleDiffBlock.
PNInM2 ReadIm2(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M2 (installation date).
RPCCon(RPCCon &&)=delete
Deleted move constructor to prevent moving instances of this class.
Bytes BuildAlarmCrBlock(int transport=0, int priority=0) const
Build an AlarmCRBlockReq for inclusion in the CONNECT request.
double timeout
Default RPC response timeout in seconds.
PNInM8 ReadIm8(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M8 (reserved).
std::array< std::uint8_t, uuidLenght > localObjectUuid
This controller's own object UUID.
~RPCCon()
Disconnect (best-effort) and close the underlying sockets.
static PNNRDData CreateNrd(const Bytes &payload)
Wrap a payload in an NRD (Network Representation Data) header.
PNInM4 ReadIm4(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M4 (PROFIsafe signature).
PNInM15 ReadIm15(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M15 (reserved).
AllIM ReadAllIm(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read all I&M records a device supports.
std::vector< blocks::SlotInfo > DiscoverSlots() override
Convenience accessor for ReadRealIdentificationData().Slots.
static int ParseAlarmCrResponse(const Bytes &responseData)
Parse the AlarmCRBlockRes from a CONNECT response.
Bytes PrmEnd() override
Send PrmEnd (end of the parameterization phase).
PNInM6 ReadIm6(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M6 (reserved).
static Bytes BuildExpectedSubmoduleBlock(const IOCRSetup &setup)
Build an ExpectedSubmoduleBlockReq for the CONNECT request.
Bytes SendControl(BlockType blockType, ControlCommand controlCommand, bool waitResponse=true, const Bytes &subBlocks={})
Send a CONTROL operation and optionally wait for its response.
void Disconnect() override
Send Release to terminate the AR.
int deviceAlarmRef
Device's local alarm reference, if an AlarmCR was established.
std::vector< EPMEndpoint > EpmLookup(const std::string &ip, std::uint16_t port=RPC_PORT, double timeoutSec=DEFAULT_TIMEOUT, std::optional< std::string > interfaceFilter=std::nullopt) override
Query a device's Endpoint Mapper for available RPC endpoints.
PNInM11 ReadIm11(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M11 (reserved).
std::pair< blocks::PDRealData, blocks::RealIdentificationData > DiscoverTopology() override
Read both PDRealData and RealIdentificationData in one call.
std::array< std::uint8_t, 3 > dataRepresentation
AR DREP for this connection.
std::array< std::uint8_t, uuidLenght > remoteObjectUuid
The target device's object UUID.
dcp::DCPDeviceDescription info
DCP-discovered description of the target device.
bool reconnecting
Whether timeout has occured and reconnection is needed.
diagnosis::DiagnosisData ReadDiagnosis(std::uint16_t slot=0, std::uint16_t subslot=0, std::uint16_t index=0xF000) override
Read and parse diagnosis data at the given location/index.
PNInM14 ReadIm14(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M14 (reserved).
void Close() noexcept override
Disconnect() and close the underlying sockets.
PNInM12 ReadIm12(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M12 (reserved).
std::optional< MacAddress > srcMac
Source MAC address used to establish the AR.
std::map< std::uint16_t, diagnosis::DiagnosisData > ReadAllDiagnosis() override
Read diagnosis from all standard diagnosis indices.
std::map< std::uint16_t, IndexProbeResult > EnumerateIndices(std::uint16_t slot=0, std::uint16_t subslot=1, std::optional< std::vector< std::uint16_t > > customIndices=std::nullopt) override
Probe a set of indices and report which are readable/empty/erroring.
static Bytes BuildIocrBlock(IocrType iocrType, const IOCRSetup &setup)
Build an IOCRBlockReq (header + API object list) for the CONNECT request.
bool alarmCrEnabled
Whether an AlarmCR was successfully established.
std::chrono::steady_clock::time_point liveMonotonic
Timestamp of the last successful RPC exchange, for CONNECTION_TIMEOUT tracking.
void CheckTimeout()
Reconnect if the AR has been idle longer than CONNECTION_TIMEOUT.
Bytes PrmBegin() override
Send PrmBegin (start of the parameterization phase).
std::uint16_t outputFrameId
Frame ID assigned to the output IOCR.
Bytes ApplicationReady(double timeoutSec=30.0) override
Wait for the device's CControl/ApplicationReady request and confirm it.
PNInM5 ReadIm5(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M5 (free-text annotation).
PNInM9 ReadIm9(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M9 (reserved).
std::unique_ptr< IRpcTransport > rpcTransport
std::uint16_t alarmRef
Controller's own local alarm reference.
std::array< std::uint8_t, uuidLenght > interfaceUuid
Interface UUID for the current RPC exchange.
PNInM10 ReadIm10(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M10 (reserved).
PNInM3 ReadIm3(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M3 (free-text descriptor).
asio::io_context & ioContext
Asio execution context used by the RPC transport.
std::uint16_t inputFrameId
Frame ID assigned to the input IOCR.
blocks::RealIdentificationData ReadRealIdentificationData() override
Read and parse the device's RealIdentificationData (logical structure).
std::optional< ConnectResult > Connect(std::optional< MacAddress > srcMac=std::nullopt, bool withAlarmCr=false, std::optional< IOCRSetup > iocrSetup=std::nullopt) override
Establish (or re-establish) the AR with the device.
PNInM1 ReadIm1(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M1 (tag function/location).
PNIODHeader Read(std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint16_t idx) override
Read a data record via slot/subslot/index (AR must be connected).
void Write(std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint16_t idx, const Bytes &data) override
Write a data record via slot/subslot/index (AR must be connected).
std::uint16_t sessionKey
Session key for this connection.
PNRPCHeader CreateRpc(std::uint16_t operation, const Bytes &nrd)
Build an RPC header for the given operation and NRD body.
std::unique_ptr< IRpcTransport > ccontrolTransport
Bytes ReadRaw(std::uint16_t idx, std::uint16_t slot=0, std::uint16_t subslot=1)
Read a raw record payload by index (convenience wrapper over Read()).
PNRPCHeader SendReceive(const PNRPCHeader &request)
Send an RPC request and synchronously wait for its response.
PNInM7 ReadIm7(std::uint16_t slot=0, std::uint16_t subslot=1) override
Read I&M7 (reserved).
Bytes ReadyForRtClass3() override
Send ReadyForRTClass3 (isochronous real-time readiness).
std::vector< blocks::WriteMultipleResult > WriteMultiple(const std::vector< WriteItem > &writes) override
Write multiple records atomically via IODWriteMultipleReq (0xE040).
bool live
Whether the AR is currently established.
PNIODHeader ReadImplicit(std::uint32_t api, std::uint16_t slot, std::uint16_t subslot, std::uint16_t idx) override
Read a data record without an established AR.
std::optional< IOCRSetup > iocrSetup
Cyclic IO configuration used for the current AR, if any.
std::array< std::uint8_t, uuidLenght > activityUuid
Activity UUID for the current RPC exchange.
Synchronous UDP transport used by PROFINET DCE/RPC.
void Open(const std::string &address, std::uint16_t port, bool isListener=false) override
Open the UDP socket and configure the remote endpoint.
Bytes SendReceive(const Bytes &data, std::chrono::milliseconds timeout) override
Send a UDP datagram and wait for one response.
Lightweight cursor for parsing a big-endian byte buffer without copies.
std::uint8_t U8()
Read and consume one byte.
std::uint32_t U32()
Read and consume a big-endian 32-bit value.
std::size_t Remaining() const
Number of bytes not yet consumed.
std::array< std::uint8_t, N > Fixed()
Read and consume a fixed-size byte array.
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::vector< std::uint8_t > ReadBytes(std::size_t n)
Read and consume a variable-length byte range.
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
std::uint16_t U16LittleEndian()
Read and consume a little-endian 16-bit value.
Declares the PROFINET RTC1 cyclic IO controller and process-data exchange API.
ModuleDiffBlock ParseModuleDiffBlock(const Bytes &data)
Parse a ModuleDiffBlock (0x8104) from raw bytes.
std::vector< WriteMultipleResult > ParseWriteMultipleResponse(const Bytes &data)
Parse an IODWriteMultipleRes into individual per-write results.
PDRealData ParsePdRealData(const Bytes &data)
Parse a complete PDRealData (0xF841) response.
RealIdentificationData ParseRealIdentificationData(const Bytes &data)
Parse a RealIdentificationData (0xF000 or 0x0013) response.
constexpr int MIN_CYCLE_MS
Minimum cycle time (ms) considered reliable.
void SendDiscover(IRawEthernetSocket &sock, const MacAddress &src, std::uint16_t responseDelay=DEFAULT_RESPONSE_DELAY_FACTOR)
Send a DCP Identify multicast request to discover all devices on the segment.
constexpr DcpBlockKey MakeBlockKey(std::uint16_t option, std::uint8_t suboption)
void SendRequest(IRawEthernetSocket &sock, const MacAddress &src, BlockKey blockType, const std::vector< std::uint8_t > &value)
Send a DCP Identify request filtered to a specific (option, suboption).
ResponseMap ReadResponse(IRawEthernetSocket &sock, const MacAddress &myMac, double timeoutSec=DEFAULT_RESPONSE_TIMEOUT, bool once=false, std::optional< std::uint32_t > expectedXid=std::nullopt)
Read and parse DCP Identify responses.
DiagnosisData ParseDiagnosisSimple(const Bytes &data, std::uint32_t api=0, std::uint16_t slot=0, std::uint16_t subslot=0)
Parse a simple, fixed-format diagnosis block.
DiagnosisData ParseDiagnosisBlock(const Bytes &data, std::uint32_t api=0, std::uint16_t slot=0, std::uint16_t subslot=0)
Parse a full DiagnosisData block from raw bytes.
std::optional< RawRpcHeader > ParseRawRpcHeader(const Bytes &data)
Parses a raw network byte vector into a structured DCE/RPC v4 Connectionless Header.
@ LastFragment
Bit 1 (0x02): Final fragment in the sequence.
@ Idempotent
Bit 5 (0x20): Activity call is idempotent (safe to retry).
@ NoFack
Bit 3 (0x08): Do not send FACK (fragment acknowledgment) frames.
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::uint8_t VERSION_HIGH
Version high.
constexpr std::uint16_t BLOCK_IOD_CONTROL_APP_READY_REQ
Block type: IODControlReqAppReady.
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.
const std::string UUID_PNIO_CONTROLLER
PROFINET IO-Controller interface UUID.
const std::string UUID_EPM_V4
Endpoint Mapper interface UUID.
constexpr std::uint16_t RPC_PORT
PROFINET IO RPC port (34964).
std::optional< EPMEndpoint > ParseEpmTower(const Bytes &towerData)
Parses a DCE/RPC EPM tower into a PROFINET endpoint.
constexpr std::uint32_t EPM_INQUIRY_ALL
EPM inquiry type: all interfaces.
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.
BlockHeaderView PeekBlockHeader(const Bytes &data, std::size_t offset)
constexpr std::uint32_t EPM_INQUIRY_INTERFACE
EPM inquiry type: specific interface.
constexpr std::uint8_t VERSION_LOW
Version low.
std::array< std::uint8_t, uuidLenght > StringToUuidBytes(const std::string &uuidStr)
Parse a canonical UUID string into 16 raw DCE/RPC UUID bytes.
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 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.
@ ControlPrmEndRequest
IOD Control Prm End request.
@ ApplicationRelationRequest
AR block request.
@ ControlRtClass3Request
IOD Control RT Class 3 request.
@ ReleaseRequest
IOD Release request.
@ ParameterizationBeginRequest
Parameterization Begin request.
@ IoDataWriteRequest
IOD write request block.
@ ControlApplicationReadyResponse
IOD Control Application Ready response.
const IndexNamePairs & DiagnosisIndices(const std::string &scope)
Diagnosis indices for a given addressing scope.
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).
IocrType
PROFINET IOCR types.
ControlCommand
PROFINET IOD control command values.
@ Release
Release command.
@ ReadyForRtClass3
Ready for RT Class 3 command.
@ PrmBegin
Prm Begin command.
std::vector< std::pair< std::uint16_t, std::string > > IndexNamePairs
(index, name) pairs, e.g. for building a "try each of these" scan.
constexpr std::uint16_t EXPECTED_ID_SUBSLOT
Expected ID Subslot.
constexpr std::uint16_t REAL_ID_API
Real ID API.
const IndexNamePairs & DeviceIndices()
Device-level indices.
constexpr std::uint16_t RECORD_INPUT_DATA
Record Input Data.
std::string ToHex(const std::uint8_t *data, std::size_t len)
Hex-encode a byte buffer.
constexpr std::uint16_t PROFINET_ETHERTYPE
EtherType value identifying PROFINET frames (0x8892).
const IndexNamePairs & ImIndices()
I&M0-I&M15 indices.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
constexpr std::uint16_t REAL_ID_SUBSLOT
Real ID Subslot.
const IndexNamePairs & InterfaceIndices()
Interface-level indices.
std::string GetIndexName(std::uint16_t index)
Get the human-readable name for a record data index.
const IndexNamePairs & PortIndices()
Port-level indices.
constexpr std::uint16_t RECORD_OUTPUT_DATA
Record Output Data.
constexpr std::array< std::uint8_t, 12 > kPnUuidSuffix
PROFINET UUID suffix, shared by all interface/object UUIDs.
PROFINET DCE/RPC protocol structures and IO-Device connection API.
One slot/subslot's placement within an IOCR's cyclic data frame.
ARBlockReq: establishes an Application Relationship (sent in the CONNECT request).
std::uint32_t arProperties
AR property flags (supervisor takeover, device access, etc.).
std::uint16_t cmInitiatorActivityTimeoutFactor
Activity timeout factor for the initiator.
MacAddress cmInitiatorMacAddress
MAC address of the initiating controller.
std::uint16_t sessionKey
Session key chosen by the initiator.
std::array< std::uint8_t, uuidLenght > arUuid
Unique AR UUID chosen by the initiator (controller).
Bytes cmInitiatorStationName
Station name of the initiating controller (variable length).
Bytes ToBytes() const
Serialize this block back to raw bytes.
std::uint16_t arType
AR type (e.g. IOCARSingle, IOSAR).
std::uint16_t initiatorUdpRtport
UDP port the initiator uses for real-time data.
static constexpr std::size_t kFixedSize
Size in bytes of the fixed portion of this block.
std::array< std::uint8_t, blockHeaderLength > blockHeader
Nested 6-byte block header.
std::array< std::uint8_t, uuidLenght > cmInitiatorObjectUuid
Object UUID of the initiating controller.
std::uint16_t stationNameLength
Length in bytes of CmInitiatorStationName.
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.
Bytes ToBytes() const
Serialize this block back to 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::array< std::uint8_t, blockHeaderLength > blockHeader
Nested 6-byte block 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.
static constexpr std::uint16_t BLOCK_TYPE
Block type identifier for AlarmCRBlockRes.
static PNAlarmCRBlockRes Parse(const Bytes &data)
Parse an AlarmCRBlockRes from raw bytes.
static constexpr std::pair< std::uint8_t, std::uint8_t > NAME_OF_STATION
Block option/suboption: station name.
static PNIOCRBlockRes Parse(const Bytes &data)
Parse an IOCRBlockRes from raw bytes.
static constexpr std::uint16_t BLOCK_TYPE
Block type identifier for IOCRBlockRes.
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.
std::uint16_t padding1
Reserved/padding.
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.
I&M0: mandatory identification data (vendor, order ID, serial number, revisions).
static PNInM0 Parse(const Bytes &data)
Parse an I&M0 record from raw bytes.
static constexpr std::uint16_t IDX
Record data index for I&M0.
I&M1: user-assigned tag function and location strings.
static PNInM1 Parse(const Bytes &data)
Parse an I&M1 record from raw bytes.
static constexpr std::uint16_t IDX
Record data index for I&M1.
static PNInM2 Parse(const Bytes &data)
Parse an I&M2 record from raw bytes.
static constexpr std::uint16_t IDX
Record data index for I&M2.
I&M3: free-text descriptor.
static constexpr std::uint16_t IDX
Record data index for I&M3.
static PNInM3 Parse(const Bytes &data)
Parse an I&M3 record from raw bytes.
I&M4: PROFIsafe signature (binary, not text).
static PNInM4 Parse(const Bytes &data)
Parse an I&M4 record from raw bytes.
static constexpr std::uint16_t IDX
Record data index for I&M4.
I&M5: free-text annotation.
static PNInM5 Parse(const Bytes &data)
Parse an I&M5 record from raw bytes.
static constexpr std::uint16_t IDX
Record data index for I&M5.
I&M6-I&M15: reserved for future use per IEC 61158-6-10.
static constexpr std::uint16_t IDX
Record data index for this reserved I&M slot.
static PNInMReserved Parse(const Bytes &data)
Parse a reserved I&M record from raw bytes.
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.
Parsed ModuleDiffBlock (0x8104).
Parsed PDRealData (0xF841) structure.
Parsed RealIdentificationData (0xF000/0x0013) structure.
std::vector< SlotInfo > slots
Populated slots/subslots.
Complete diagnosis data for one slot/subslot.
std::vector< AnyChannelDiagnosis > entries
Parsed diagnosis entries.
Bytes rawData
Raw, undecoded source data.
std::uint32_t api
API number.
std::uint16_t subslot
Subslot number.
std::uint16_t slot
Slot number.
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.
Result of a successful RPCCon::Connect() that also established cyclic IO.
std::array< std::uint8_t, uuidLenght > arUuid
AR UUID assigned to this connection.
One RPC endpoint advertised by a device's Endpoint Mapper.
std::string protocol
Transport protocol, e.g. "ncadg_ip_udp".
std::uint16_t interfaceVersionMinor
Minor version of the interface.
std::string ipAddress
IPv4 address the endpoint listens on.
std::uint16_t port
UDP/TCP port the endpoint listens on.
std::uint16_t interfaceVersionMajor
Major version of the interface.
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::uint16_t dataHoldFactor
Data hold factor (how long to keep the last good frame's data).
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).
ApplicationRelations applicationRelations
One slot/subslot's expected module configuration and IO data sizes.
Result of probing one index in EnumerateIndices().
std::uint8_t errorCode1
PNIOError ErrorCode1, if the error was a PNIOError.
std::string name
Human-readable name of the index.
std::size_t size
Payload size in bytes; valid when Status == "readable".
std::uint8_t errorCode2
PNIOError ErrorCode2, if the error was a PNIOError.
std::string error
Error message; valid when Status == "error".
std::string status
Probe outcome: "readable", "empty", or "error".
Configuration options for an RPC connection.
std::uint16_t rpcPort
UDP port used for RPC communication.
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.