15 return std::visit([](
const auto& i)
16 {
return i.userStructureId; }, item);
21 return std::visit([](
const auto& i)
22 {
return i.UsiName(); }, item);
28std::pair<DiagnosisItem, std::size_t> ParseDiagnosisItem(
const Bytes& data, std::size_t offset,
Usi usi)
30 if (data.size() < offset + 6)
32 throw std::invalid_argument(
"Truncated DiagnosisItem");
38 item.userStructureId = usi;
39 item.channelNumber = r.U16();
40 offset +=
sizeof(std::uint16_t);
41 item.channelProperties = r.U16();
42 offset +=
sizeof(std::uint16_t);
43 item.channelErrorType = r.U16();
44 offset +=
sizeof(std::uint16_t);
48 if (data.size() < offset +
sizeof(std::uint16_t) +
sizeof(std::uint32_t))
50 throw std::invalid_argument(
"Truncated ExtChannelDiagnosis");
52 item.extChannelErrorType = r.U16();
53 offset +=
sizeof(std::uint16_t);
54 item.extChannelAddValue = r.U32();
55 offset +=
sizeof(std::uint32_t);
60 if (data.size() < offset +
sizeof(std::uint32_t))
62 throw std::invalid_argument(
"Truncated QualifiedChannelDiagnosis");
64 item.qualifiedChannelQualifier = r.U32();
65 offset +=
sizeof(std::uint32_t);
68 return {item, offset};
71std::pair<MaintenanceItem, std::size_t> ParseMaintenanceItem(
const Bytes& data, std::size_t offset)
73 if (data.size() < offset +
blockHeaderLength + (2 *
sizeof(std::uint8_t)) +
sizeof(std::uint32_t))
75 throw std::invalid_argument(
"Truncated MaintenanceItem");
82 const std::uint32_t status = r.U32();
83 offset +=
sizeof(std::uint32_t);
87 item.blockType = hdr.blockType;
88 item.blockLength = hdr.blockLength;
89 item.blockVersion = hdr.blockVersion;
90 item.maintenanceStatus = status;
91 return {item, offset};
94std::pair<UploadRetrievalItem, std::size_t> ParseUploadRetrievalItem(
const Bytes& data, std::size_t offset,
Usi usi)
96 if (data.size() < offset +
blockHeaderLength +
sizeof(std::uint16_t) + (2 *
sizeof(std::uint32_t)))
98 throw std::invalid_argument(
"Truncated UploadRetrievalItem");
102 wire::Reader r(data);
105 const std::uint32_t index = r.U32();
106 offset +=
sizeof(std::uint32_t);
107 const std::uint32_t
length = r.U32();
108 offset +=
sizeof(std::uint32_t);
110 UploadRetrievalItem item;
111 item.userStructureId = usi;
112 item.blockType = hdr.blockType;
113 item.blockLength = hdr.blockLength;
114 item.blockVersion = hdr.blockVersion;
115 item.urRecordIndex = index;
116 item.urRecordLength =
length;
117 return {item, offset};
120std::pair<PEAlarmItem, std::size_t> ParsePeAlarmItem(
const Bytes& data, std::size_t offset)
124 throw std::invalid_argument(
"Truncated PE_AlarmItem");
128 const std::uint8_t mode = data[offset];
129 offset +=
sizeof(std::uint8_t);
133 item.blockType = hdr.blockType;
134 item.blockLength = hdr.blockLength;
135 item.blockVersion = hdr.blockVersion;
136 item.peOperationalMode = mode;
137 return {item, offset};
140std::pair<RSAlarmItem, std::size_t> ParseRsAlarmItem(
const Bytes& data, std::size_t offset,
Usi usi)
142 if (data.size() < offset +
sizeof(std::uint16_t))
144 throw std::invalid_argument(
"Truncated RS_AlarmItem");
146 wire::Reader r(data);
149 const auto info = r.U16();
150 offset +=
sizeof(std::uint32_t);
153 item.userStructureId = usi;
154 item.rsAlarmInfo = info;
155 return {item, offset};
158std::pair<PRALAlarmItem, std::size_t> ParsePralAlarmItem(
const Bytes& data, std::size_t offset)
160 if (data.size() < offset + (4 *
sizeof(std::uint16_t)))
162 throw std::invalid_argument(
"Truncated PRAL_AlarmItem");
164 wire::Reader r(data);
167 const auto channelNum = r.U16();
168 offset +=
sizeof(std::uint16_t);
170 const auto channelProps = r.U16();
171 offset +=
sizeof(std::uint16_t);
173 const auto reason = r.U16();
174 offset +=
sizeof(std::uint16_t);
176 const auto extReason = r.U16();
177 offset +=
sizeof(std::uint16_t);
181 item.channelNumber = channelNum;
182 item.propAlChannelProperties = channelProps;
183 item.prAlReason = reason;
184 item.prAlExtReason = extReason;
185 item.prAlReasonAddValue =
Bytes(data.begin() + offset, data.end());
186 return {item, data.size()};
193 if (data.size() < offset +
sizeof(std::uint16_t))
195 throw std::invalid_argument(
"Insufficient data for USI");
200 const auto usi =
static_cast<Usi>(r.
U16());
201 offset +=
sizeof(std::uint16_t);
205 auto [item, new_offset] = ParseDiagnosisItem(data, offset, usi);
206 return {item, new_offset};
210 auto [item, new_offset] = ParseMaintenanceItem(data, offset);
211 return {item, new_offset};
215 auto [item, new_offset] = ParseUploadRetrievalItem(data, offset, usi);
216 return {item, new_offset};
220 auto [item, new_offset] = ParseRsAlarmItem(data, offset, usi);
221 return {item, new_offset};
225 auto [item, new_offset] = ParsePeAlarmItem(data, offset);
226 return {item, new_offset};
230 auto [item, new_offset] = ParsePralAlarmItem(data, offset);
231 return {item, new_offset};
236 generic.rawData =
Bytes(data.begin() + offset, data.end());
237 return {
generic, data.size()};
259 if (data.size() < 26)
261 throw std::invalid_argument(
"AlarmNotification too short");
264 std::size_t offset = 0;
268 wire::Reader r(data.data() + offset, data.size() - offset);
271 offset +=
sizeof(std::uint16_t);
273 const std::uint32_t api = r.
U32();
274 offset +=
sizeof(std::uint32_t);
276 const std::uint16_t slotNumber = r.
U16();
277 offset +=
sizeof(std::uint16_t);
279 const std::uint16_t subslotNumber = r.
U16();
280 offset +=
sizeof(std::uint16_t);
282 const std::uint32_t moduleIdent = r.
U32();
283 offset +=
sizeof(std::uint32_t);
285 const std::uint32_t submoduleIdent = r.
U32();
286 offset +=
sizeof(std::uint32_t);
288 const std::uint16_t alarmSpecifier = r.
U16();
289 offset +=
sizeof(std::uint16_t);
295 notification.
api = api;
308 const std::size_t blockBodyLength = hdr.
blockLength;
309 constexpr std::size_t blockVersionSize = 2;
310 constexpr std::size_t alarmBodySize = 20;
312 if (blockBodyLength < blockVersionSize + alarmBodySize)
314 throw std::invalid_argument(
"Invalid AlarmNotification BlockLength");
317 const std::size_t blockEnd = 4U + blockBodyLength;
319 if (data.size() < blockEnd)
321 throw std::invalid_argument(
"Truncated AlarmNotification block");
324 notification.
rawPayload =
Bytes(data.begin() + offset, data.begin() + blockEnd);
328 std::size_t itemOffset = 0;
330 while (itemOffset <
payload.size())
335 notification.
items.push_back(std::move(item));
336 itemOffset = new_offset;
338 catch (
const std::invalid_argument&)
571 return "Error within the coordination of sequence numbers";
574 return "Instance closed";
577 return "AR out of memory";
580 return "AR add provider or consumer failed";
583 return "AR consumer DHT/WDT expired";
586 return "AR CMI timeout";
589 return "AR alarm-open failed";
592 return "AR alarm-send.cnf(-)";
595 return "AR alarm-ack-send.cnf(-)";
598 return "AR alarm data too long";
601 return "AR alarm.ind(err)";
604 return "AR RPC-client call.cnf(-)";
607 return "AR abort.req";
610 return "AR re-run aborts existing";
613 return "AR release.ind received";
616 return "AR device deactivated";
622 return "AR protocol violation";
625 return "AR name resolution error";
628 return "AR RPC-Bind error";
631 return "AR RPC-Connect error";
634 return "AR RPC-Read error";
637 return "AR RPC-Write error";
640 return "AR RPC-Control error";
643 return "AR forbidden pull or plug after check.rsp and before in-data.ind";
646 return "AR AP removed";
649 return "AR link down";
652 return "AR could not register multicast-mac address";
655 return "Not synchronized (cannot start companion-AR)";
658 return "Wrong topology (cannot start companion-AR)";
661 return "DCP station-name changed";
664 return "DCP reset to factory-settings";
667 return "Cannot start companion-AR because of 0x8ipp submodule in first AR";
670 return "No irdata record yet";
676 return "PDEV: no port offers required speed/duplexity";
679 return "IP-Suite changed by DCP_Set(IPParameter) or local engineering";
682 return "IOCARSR: RDHT expired";
685 return "IOCARSR: PDEV parameterization impossible";
688 return "Remote application ready timeout expired";
691 return "IOCARSR: redundant interface lost or peripheral access impossible";
694 return "IOCARSR: MTOT expired";
697 return "IOCARSR: AR protocol violation";
700 return "PDEV: plug port without CombinedObjectContainer";
703 return "(Obsoleted) NME: no or wrong configuration";
709 return "Unknown RTA error";
713 const auto value =
static_cast<std::uint8_t
>(reason);
725 if (value >= 0x02U && value <= 0x25U)
730 if (value >= 0x26U && value <= 0x2CU)
Declares PROFINET alarm, diagnosis, maintenance, and USI payload types and parsers.
Lightweight cursor for parsing a big-endian byte buffer without copies.
std::uint32_t U32()
Read and consume a big-endian 32-bit value.
void SkipTo(std::size_t offset)
Move the read position to an absolute byte offset.
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
constexpr int blockHeaderLength
Constant lenght of a blockHeader.
@ ManufacturerSpecific
Bit 12: Indicates manufacturer-specific alarm details are present.
@ AlarmSequenceNumber
Bit mask extracting the alarm sequence number (bits 0–10, range 0–2047).
@ SubmoduleDiagnosisState
Bit 13: Indicates submodule diagnosis state is present.
@ ArDiagnosisState
Bit 14: Indicates Application Relation (AR) diagnosis state is present.
@ ChannelDiagnosis
Bit 15: Indicates channel diagnosis details are present.
std::pair< AlarmItemVariant, std::size_t > ParseAlarmItem(const Bytes &data, std::size_t offset=0)
Parse a single alarm item starting at an offset.
AlarmNotification ParseAlarmNotification(const Bytes &data)
Parse a complete AlarmNotification PDU (BlockHeader + body + items).
AlarmType
PROFINET Alarm Types used in AlarmNotification blocks.
RtaErrorReason
Exact current PROFINET RTA ErrorCode2 values.
@ PdevPlugPortWithoutCombinedObjectContainer
@ ArAddProviderConsumerFailed
@ ArRpcClientCallConfirmationNegative
@ PdevNoRequiredSpeedDuplexity
@ IocarSrRedundantInterfaceLost
@ IocarSrPdevParameterizationImpossible
@ RemoteApplicationReadyTimeout
@ ObsoletedNmeNoConfiguration
@ ArAlarmSendConfirmationNegative
@ ArConsumerDhtWdtExpired
@ CompanionAr8ippSubmodule
@ DcpResetToFactorySettings
@ IocarSrArProtocolViolation
@ ArAlarmAckSendConfirmationNegative
@ ArMulticastMacRegistrationFailed
RtaErrorClass ClassifyRtaError(RtaErrorReason reason)
std::string UsiNameOf(const AlarmItemVariant &item)
Get the human-readable USI name for any AlarmItemVariant alternative.
Usi
PROFINET User Structure Identifiers (USI).
@ Maintenance
Maintenance item.
@ IParameter
iParameter item.
@ PralAlarm
PRAL alarm item.
@ ExtendedChannelDiagnosis
Extended channel diagnosis.
@ ChannelDiagnosis
Channel diagnosis.
@ RsAlarmHigh
RS alarm item for high alarm.
@ RsAlarmSubmodule
RS alarm item for submodule.
@ RsAlarmLow
RS alarm item for low alarm.
@ QualifiedChannelDiagnosis
Qualified channel diagnosis.
RtaErrorClass
Classification of an RTA protocol error reason.
@ UserSpecific
User-specific RTA abort.
@ SequenceNumber
Error coordinating RTA sequence numbers.
@ Abort
RTA error that aborts the application relation.
@ Reserved
No error / reserved reason.
@ Obsoleted
Obsoleted reason code.
@ Extended
Additional AR/IOCARSR/PDEV RTA reason defined by PROFINET.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Usi UsiOf(const AlarmItemVariant &item)
Get the USI shared by every AlarmItemVariant alternative.
std::variant< AlarmItem, DiagnosisItem, MaintenanceItem, UploadRetrievalItem, PEAlarmItem, RSAlarmItem, PRALAlarmItem > AlarmItemVariant
A parsed alarm payload item; the held alternative is selected by USI.
Generic/unknown alarm item.
Usi userStructureId
User Structure Identifier for this item.
Complete parsed AlarmNotification PDU: header fields + parsed items.
bool manufacturerSpecific
Whether the alarm specifier indicates a manufacturer-specific diagnosis.
std::vector< AlarmItemVariant > items
Parsed alarm items carried by this notification.
std::uint16_t slotNumber
Slot number where the alarm originated.
std::uint16_t alarmSequenceNumber
Sequence number extracted from the alarm specifier.
BlockType blockType
Block type (high or low priority notification).
std::uint32_t api
API number.
Bytes rawPayload
Raw, undecoded payload the items were parsed from.
std::string Location() const
Location string identifying where the alarm originated.
AlarmType alarmType
Alarm type (Diagnosis/Process/Pull/Plug/...).
std::pair< std::uint8_t, std::uint8_t > blockVersion
Block format version.
std::uint32_t submoduleIdentNumber
Submodule ident number of the affected submodule.
std::uint16_t subslotNumber
Subslot number where the alarm originated.
bool channelDiagnosis
Whether the alarm specifier indicates channel diagnosis is present.
std::uint32_t moduleIdentNumber
Module ident number of the affected module.
bool arDiagnosisState
Whether the alarm specifier indicates AR diagnosis state.
bool submoduleDiagnosisState
Whether the alarm specifier indicates submodule diagnosis state.
static RtaErrorReason ClassifyRtaErrorReason(std::uint8_t errorCode2)
Return the decoded RTA reason.
static RtaErrorClass ClassifyRtaError(std::uint8_t errorCode2)
Return the decoded RTA reason.
RtaErrorReason reason
Decoded RTA reason.
std::string ReasonName() const
Return the decoded RTA reason name.
std::uint8_t errorCode2
PNIO ErrorCode2 byte.
Generic helpers for reading and writing integer and byte data in big- and little-endian wire formats.