69 return std::visit([](
const auto& i)
70 {
return i.userStructureId; }, item);
75 return std::visit([](
const auto& i)
76 {
return i.UsiName(); }, item);
82std::pair<DiagnosisItem, std::size_t> ParseDiagnosisItem(
const Bytes& data, std::size_t offset,
Usi usi)
84 if (data.size() < offset + 6)
86 throw std::invalid_argument(
"Truncated DiagnosisItem");
90 item.userStructureId = usi;
91 item.channelNumber =
static_cast<std::uint16_t
>((data[offset] <<
OneOctetShift) | data[offset + 1]);
92 item.channelProperties =
static_cast<std::uint16_t
>((data[offset + 2] <<
OneOctetShift) | data[offset + 3]);
93 item.channelErrorType =
static_cast<std::uint16_t
>((data[offset + 4] <<
OneOctetShift) | data[offset + 5]);
98 if (data.size() < offset + 6)
100 throw std::invalid_argument(
"Truncated ExtChannelDiagnosis");
102 item.extChannelErrorType =
static_cast<std::uint16_t
>((data[offset] <<
OneOctetShift) | data[offset + 1]);
103 item.extChannelAddValue =
static_cast<std::uint32_t
>(data[offset + 2]) <<
ThreeOctetsShift |
105 static_cast<std::uint32_t
>(data[offset + 4]) <<
OneOctetShift | data[offset + 5];
111 if (data.size() < offset + 4)
113 throw std::invalid_argument(
"Truncated QualifiedChannelDiagnosis");
115 item.qualifiedChannelQualifier =
static_cast<std::uint32_t
>(data[offset]) <<
ThreeOctetsShift |
117 static_cast<std::uint32_t
>(data[offset + 2]) <<
OneOctetShift | data[offset + 3];
121 return {item, offset};
124std::pair<MaintenanceItem, std::size_t> ParseMaintenanceItem(
const Bytes& data, std::size_t offset)
126 if (data.size() < offset + 12)
128 throw std::invalid_argument(
"Truncated MaintenanceItem");
133 const std::uint32_t status =
static_cast<std::uint32_t
>(data[offset]) <<
ThreeOctetsShift |
135 static_cast<std::uint32_t
>(data[offset + 2]) <<
OneOctetShift | data[offset + 3];
138 MaintenanceItem item;
140 item.blockType = hdr.blockType;
141 item.blockLength = hdr.blockLength;
142 item.blockVersion =
static_cast<std::uint16_t
>((hdr.blockVersionHigh <<
OneOctetShift) | hdr.blockVersionLow);
143 item.maintenanceStatus = status;
144 return {item, offset};
147std::pair<UploadRetrievalItem, std::size_t> ParseUploadRetrievalItem(
const Bytes& data, std::size_t offset,
Usi usi)
149 if (data.size() < offset + 16)
151 throw std::invalid_argument(
"Truncated UploadRetrievalItem");
156 const std::uint32_t index =
static_cast<std::uint32_t
>(data[offset]) <<
ThreeOctetsShift |
158 static_cast<std::uint32_t
>(data[offset + 2]) <<
OneOctetShift | data[offset + 3];
160 const std::uint32_t length =
static_cast<std::uint32_t
>(data[offset]) <<
ThreeOctetsShift |
162 static_cast<std::uint32_t
>(data[offset + 2]) <<
OneOctetShift | data[offset + 3];
165 UploadRetrievalItem item;
166 item.userStructureId = usi;
167 item.blockType = hdr.blockType;
168 item.blockLength = hdr.blockLength;
169 item.blockVersion =
static_cast<std::uint16_t
>((hdr.blockVersionHigh <<
OneOctetShift) | hdr.blockVersionLow);
170 item.urRecordIndex = index;
171 item.urRecordLength = length;
172 return {item, offset};
175std::pair<PEAlarmItem, std::size_t> ParsePeAlarmItem(
const Bytes& data, std::size_t offset)
177 if (data.size() < offset + 7)
179 throw std::invalid_argument(
"Truncated PE_AlarmItem");
183 const std::uint8_t mode = data[offset];
188 item.blockType = hdr.blockType;
189 item.blockLength = hdr.blockLength;
190 item.blockVersion =
static_cast<std::uint16_t
>((hdr.blockVersionHigh <<
OneOctetShift) | hdr.blockVersionLow);
191 item.peOperationalMode = mode;
192 return {item, offset};
195std::pair<RSAlarmItem, std::size_t> ParseRsAlarmItem(
const Bytes& data, std::size_t offset,
Usi usi)
197 if (data.size() < offset + 2)
199 throw std::invalid_argument(
"Truncated RS_AlarmItem");
201 const auto info =
static_cast<std::uint16_t
>((data[offset] <<
OneOctetShift) | data[offset + 1]);
205 item.userStructureId = usi;
206 item.rsAlarmInfo = info;
207 return {item, offset};
210std::pair<PRALAlarmItem, std::size_t> ParsePralAlarmItem(
const Bytes& data, std::size_t offset)
212 if (data.size() < offset + 8)
214 throw std::invalid_argument(
"Truncated PRAL_AlarmItem");
216 const auto channelNum =
static_cast<std::uint16_t
>((data[offset] <<
OneOctetShift) | data[offset + 1]);
217 const auto channelProps =
static_cast<std::uint16_t
>((data[offset + 2] <<
OneOctetShift) | data[offset + 3]);
218 const auto reason =
static_cast<std::uint16_t
>((data[offset + 4] <<
OneOctetShift) | data[offset + 5]);
219 const auto extReason =
static_cast<std::uint16_t
>((data[offset + 6] <<
OneOctetShift) | data[offset + 7]);
224 item.channelNumber = channelNum;
225 item.propAlChannelProperties = channelProps;
226 item.prAlReason = reason;
227 item.prAlExtReason = extReason;
228 item.prAlReasonAddValue =
Bytes(data.begin() + offset, data.end());
229 return {item, data.size()};
236 if (data.size() < offset + 2)
238 throw std::invalid_argument(
"Insufficient data for USI");
241 const auto usi =
static_cast<Usi>((data[offset] <<
OneOctetShift) | data[offset + 1]);
246 auto [item, new_offset] = ParseDiagnosisItem(data, offset, usi);
247 return {item, new_offset};
251 auto [item, new_offset] = ParseMaintenanceItem(data, offset);
252 return {item, new_offset};
256 auto [item, new_offset] = ParseUploadRetrievalItem(data, offset, usi);
257 return {item, new_offset};
261 auto [item, new_offset] = ParseRsAlarmItem(data, offset, usi);
262 return {item, new_offset};
266 auto [item, new_offset] = ParsePeAlarmItem(data, offset);
267 return {item, new_offset};
271 auto [item, new_offset] = ParsePralAlarmItem(data, offset);
272 return {item, new_offset};
277 generic.rawData =
Bytes(data.begin() + offset, data.end());
278 return {
generic, data.size()};
303 if (data.size() < 26)
305 throw std::invalid_argument(
"AlarmNotification too short");
308 std::size_t offset = 0;
312 wire::Reader r(data.data() + offset, data.size() - offset);
314 const std::uint32_t api = r.
U32();
315 const std::uint16_t slotNumber = r.
U16();
316 const std::uint16_t subslotNumber = r.
U16();
317 const std::uint32_t moduleIdent = r.
U32();
318 const std::uint32_t submoduleIdent = r.
U32();
319 const std::uint16_t alarmSpecifier = r.
U16();
326 notification.
api = api;
337 const std::size_t blockBodyLength = hdr.
blockLength;
338 constexpr std::size_t blockVersionSize = 2;
339 constexpr std::size_t alarmBodySize = 20;
341 if (blockBodyLength < blockVersionSize + alarmBodySize)
343 throw std::invalid_argument(
"Invalid AlarmNotification BlockLength");
346 const std::size_t blockEnd = 4U + blockBodyLength;
348 if (data.size() < blockEnd)
350 throw std::invalid_argument(
"Truncated AlarmNotification block");
353 notification.
rawPayload =
Bytes(data.begin() + offset, data.begin() + blockEnd);
357 std::size_t itemOffset = 0;
359 while (itemOffset <
payload.size())
364 notification.
items.push_back(std::move(item));
365 itemOffset = new_offset;
367 catch (
const std::invalid_argument&)
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.
std::uint16_t U16()
Read and consume a big-endian 16-bit value.
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.
static constexpr int OneOctetShift
The bit-shift distance required to move data across a single octet.
static constexpr int ThreeOctetsShift
The bit-shift distance required to move data across three octets.
static constexpr int TwoOctetsShift
The bit-shift distance required to move data across two octets.
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.
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.
Small helpers for reading/writing big-endian ("network order") integers to/from byte buffers....