19 endpoint(std::move(endpoint)),
20 controllerMac(controllerMac.value_or(
MacAddress{}))
48 if (!targetPtr || !*targetPtr)
53 auto fnToMatch = *targetPtr;
59 auto target = cb.template target<void (*)(const AlarmNotification&)>();
60 return target && *target == fnToMatch;
81 auto targetPtr = callback.target<void (*)(
const RtaError&)>();
82 if (!targetPtr || !*targetPtr)
87 auto fnToMatch = *targetPtr;
93 auto target = cb.template target<void (*)(const RtaError&)>();
94 return target && *target == fnToMatch;
112 if (eSocket ==
nullptr)
116 static constexpr uint16_t timeoutMs = 1000;
117 eSocket->SetTimeout(std::chrono::milliseconds(timeoutMs));
121 if (uSocket ==
nullptr)
123 uSocket = std::make_unique<UdpSocket>(
ALARM_UDP_PORT, std::chrono::seconds(1));
134 l2Sock = std::move(eSocket);
139 worker = std::jthread([
this]
141 thread = std::move(worker);
179 catch (
const std::exception& e)
204 constexpr std::size_t macHeaderLength = 12U;
205 constexpr std::size_t etherTypeLength = 2U;
206 constexpr std::size_t frameIdLength = 2U;
208 if (data.size() < macHeaderLength + etherTypeLength + frameIdLength)
215 std::size_t offset = 0;
216 std::ranges::copy(data.begin() + offset, data.begin() + offset +
macAddressLength, dstMac.begin());
218 std::ranges::copy(data.begin() + offset, data.begin() + offset +
macAddressLength, srcMac.begin());
228 if (data.size() < etherTypeOffset + etherTypeLength + frameIdLength)
233 r.
SkipTo(etherTypeOffset);
234 const auto ethertype = r.
U16();
235 offset +=
sizeof(std::uint16_t);
242 const std::size_t frameIdOffset = etherTypeOffset + etherTypeLength;
246 const std::size_t rtaOffset = frameIdOffset + frameIdLength;
249 const auto alarmData =
Bytes(data.begin() + rtaOffset, data.end());
254 const auto alarmData =
Bytes(data.begin() + rtaOffset, data.end());
267 if (datagram.
data.empty() || datagram.
data.size() < 28)
276 std::optional<MacAddress> srcMac, std::optional<UdpEndpoint> udpSource)
288 catch (
const std::exception&)
299 auto version = (rta.
pduType >> 4) & 0xFF;
312 if (
error.IsRtaProtocolError())
314 std::vector<std::function<void(
const RtaError&)>> callbacksCopy;
321 for (
const auto& cb : callbacksCopy)
357 catch (
const std::exception&)
363 SendAck(alarm, srcMac, udpSource);
372 for (
const auto& cb : callbacksCopy)
409 auto alarmSpecifier =
431 else if (udpSource && udpSource.has_value())
438constexpr std::uint16_t kVlanTpid = 0x8100;
439constexpr std::uint16_t kVlanPcp6Vid0 = 0xC000;
440constexpr std::uint16_t kVlanPcp5Vid0 = 0xA000;
446 ackData, dstMac, highPriority);
471 frame.insert(
frame.end(), dstMac.begin(), dstMac.end());
474 const std::uint16_t vlanTci = highPriority ? kVlanPcp6Vid0 : kVlanPcp5Vid0;
484 frame.insert(
frame.end(), rtaBytes.begin(), rtaBytes.end());
485 frame.insert(
frame.end(), ackData.begin(), ackData.end());
491 catch (
const std::exception&)
503 udpSock->SendTo(ackData, dstAddr);
Declares the PROFINET RTA alarm listener and AlarmAck transmission support.
Asio-backed platform-independent raw Ethernet transport.
std::atomic< bool > running
Whether the listener thread should keep running.
AlarmListener(asio::io_context &ioContext, AlarmEndpoint endpoint, std::optional< MacAddress > controllerMac=std::nullopt)
Construct a listener for the given endpoint.
asio::io_context & ioContext
Asio execution context used by the Layer-2 socket.
AlarmEndpoint endpoint
Alarm endpoint configuration.
void RemoveAlarmNotificationCallback(std::function< void(const AlarmNotification &)> callback) override
Remove a registered (alarm notification) callback.
void SendNack(std::optional< MacAddress > srcMac, bool highPriority)
Send an RTA-NACK for an out-of-sequence DATA PDU.
void RemoveRtaAlarmCallback(std::function< void(const RtaError &)> callback) override
Remove a registered (rta alarm) callback.
void HandleUdpFrame()
Receive and process one UDP frame.
~AlarmListener() override
Stop (if running) and release resources.
void Stop() override
Stop the listener.
std::uint16_t recvSeqNum
Last received sequence number, for duplicate detection.
std::unique_ptr< IRawEthernetSocket > l2Sock
Raw Ethernet transport used for Layer 2 alarm reception.
MacAddress controllerMac
This host's MAC address.
std::uint16_t sendSeqNum
Sequence number for outgoing AlarmAck-PDUs.
void HandleLayer2Frame()
Receive and process one Layer 2 (RTA-PDU) frame.
void AddAlarmNotificationCallback(std::function< void(const AlarmNotification &)> callback) override
Register a callback for received alarms.
void SendAck(const AlarmNotification &alarm, std::optional< MacAddress > srcMac, std::optional< UdpEndpoint > udpSource)
Build and send an AlarmAck-PDU for a received notification.
void ListenLoop()
Listener thread body: dispatches to the Layer 2 or UDP handler.
std::mutex lifecycleMutex
Guards lifecycle steps of listener against concurrent access.
std::jthread thread
Background thread receiving and processing alarms.
std::vector< std::function< void(const RtaError &)> > rtaAlarmCallbacks
Registered callbacks invoked for successfully parsed rta alarms.
void AddRtaAlarmCallback(std::function< void(const RtaError &)> callback) override
Register a callback for received alarms.
void SendTransportAck(std::optional< MacAddress > srcMac, bool highPriority)
Send a pure RTA-ACK (TACK) with empty var part.
void SendLayer2Ack(const Bytes &ackData, const MacAddress &dstMac, bool highPriority)
Send an AlarmAck-PDU over the Layer 2 transport.
void SendRtaFrame(std::uint8_t pduType, std::uint8_t addFlags, const Bytes &ackData, const MacAddress &dstMac, bool highPriority)
Build and send an RTA PDU (Layer 2); returns the frame bytes.
void Start(std::unique_ptr< IRawEthernetSocket > eSocket=nullptr, std::unique_ptr< IUdpSocket > uSocket=nullptr) override
Start the background listener (socket + thread).
void ProcessAlarm(const Bytes &payload, std::optional< bool > highPriority, std::optional< MacAddress > srcMac, std::optional< UdpEndpoint > udpSource)
Parse an alarm payload and dispatch it to registered callbacks.
std::unique_ptr< IUdpSocket > udpSock
UDP transport used for UDP alarm reception and acknowledgement.
std::vector< std::function< void(const AlarmNotification &)> > alarmNotificationCallbacks
Registered callbacks invoked for successfully parsed alarmsnotifications.
void SendUdpAck(const Bytes &ackData, const UdpEndpoint &dstAddr)
Send an AlarmAck-PDU over the UDP transport.
std::mutex callbacksMutex
Protects concurrent access to callbacks.
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::uint16_t U16()
Read and consume a big-endian 16-bit value.
Declares exceptions and error types used by the PROFINET IO controller stack.
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.
@ AlarmAcknowledgementLow
Alarm acknowledgement low block.
@ AlarmAcknowledgementHigh
Alarm acknowledgement high block.
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.
AlarmNotification ParseAlarmNotification(const Bytes &data)
Parse a complete AlarmNotification PDU (BlockHeader + body + items).
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
constexpr std::uint16_t ALARM_UDP_PORT
UDP port used for alarm transport when Transport == 1.
RtaError ParseRtaError(const PNRTAHeader &header)
Parse an RTA ERR-PDU from an already parsed RTA header.
std::size_t SkipVlanTags(std::span< const std::uint8_t > frame) noexcept
Return the byte offset of the real EtherType in a raw Ethernet frame.
constexpr int macAddressLength
Constant lenght of a mac address.
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
constexpr std::size_t RECEIVE_BUFFER_LENGTH
Receive buffer length.
constexpr std::uint16_t FRAME_ID_ALARM_HIGH
Frame ID used for high-priority alarms.
constexpr std::uint16_t ETHERTYPE_PROFINET
EtherType used by PROFINET RTA frames (0x8892).
constexpr std::uint16_t FRAME_ID_ALARM_LOW
Frame ID used for low-priority alarms.
Alarm endpoint configuration.
int transport
Transport: 0 = Layer2 (RTA), 1 = UDP.
std::string interface
Network interface name to bind to, e.g. "eth0".
std::uint16_t controllerRef
Controller's local alarm reference (from AlarmCRBlockReq).
std::uint16_t deviceRef
Device's local alarm reference (from AlarmCRBlockRes).
MacAddress deviceMac
The device's MAC address.
Complete parsed AlarmNotification PDU: header fields + parsed items.
bool manufacturerSpecific
Whether the alarm specifier indicates a manufacturer-specific diagnosis.
std::uint16_t slotNumber
Slot number where the alarm originated.
std::uint16_t alarmSequenceNumber
Sequence number extracted from the alarm specifier.
std::uint32_t api
API number.
AlarmType alarmType
Alarm type (Diagnosis/Process/Pull/Plug/...).
std::uint16_t subslotNumber
Subslot number where the alarm originated.
bool IsHighPriority() const
Whether this is a high-priority notification.
bool channelDiagnosis
Whether the alarm specifier indicates channel diagnosis is present.
bool arDiagnosisState
Whether the alarm specifier indicates AR diagnosis state.
bool submoduleDiagnosisState
Whether the alarm specifier indicates submodule diagnosis state.
AlarmAck-PDU: acknowledges receipt of an AlarmNotification-PDU.
std::uint16_t alarmSpecifier
Alarm specifier flags echoed from the notification.
AlarmType alarmType
Alarm type being acknowledged.
std::uint16_t slotNumber
Slot number being acknowledged.
std::array< std::uint8_t, blockHeaderLength > blockHeader
Nested 6-byte block header.
std::uint32_t statusPNIO
PNIO status code (0 = acknowledged OK).
std::uint16_t subslotNumber
Subslot number being acknowledged.
static constexpr std::size_t kSize
Size in bytes of this PDU.
std::uint32_t api
API number.
Bytes ToBytes() const
Serialize this PDU back to raw bytes.
Decoded PROFINET RTA ERR-PDU.
A UDP datagram together with the IPv4 address of its sender.
std::vector< std::uint8_t > data
Datagram payload.
UdpEndpoint source
IPv4 address and UDP port of the datagram sender.
Portable IPv4 UDP endpoint.
std::uint8_t addFlags
RTA AdditionalFlags field.
std::uint16_t frameId
Ethernet FrameID.
std::unique_ptr< asio::io_context > ioContext
Shared ASIO context.
Declares the portable IPv4 UDP socket implementation.