PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
virtual_profinet_rpc_device.h
Go to the documentation of this file.
1
26#pragma once
27
28#include <array>
29#include <asio.hpp>
30#include <atomic>
31#include <condition_variable>
32#include <cstdint>
33#include <mutex>
34#include <string>
35#include <thread>
36#include <vector>
37
38#include "profinet/protocol.h"
39#include "profinet/rpc.h"
40
41namespace profinet::sim
42{
43
63
65[[nodiscard]] const char* ToString(VirtualProfinetDeviceState state) noexcept;
66
70{
76 std::uint16_t rpcPort = 34965;
77
80
82 std::string controllerAddress = "127.0.0.1";
83
85 std::string stationName = "valve2";
86
88 MacAddress deviceMac{0xA0, 0xD3, 0x85, 0x00, 0xF6, 0x13};
89
91 std::uint16_t vendorId = 0x013F;
92
94 std::uint16_t deviceId = 0x0002;
95
97 std::chrono::milliseconds applicationReadyDelay{10};
98
100 std::uint16_t inputFrameId = 0xC000;
101
103 std::uint16_t outputFrameId = 0xC001;
104
106 std::uint16_t deviceAlarmReference = 42;
107
109 bool requireBothIocrs = true;
110
113
115 bool requireAlarmCr = true;
116
119};
120
131{
132 public:
137
140
143
146 void Start();
147
149 void Stop() noexcept;
150
152 [[nodiscard]] VirtualProfinetDeviceState State() const noexcept;
153
155 [[nodiscard]] std::size_t ConnectCount() const noexcept;
156
158 [[nodiscard]] std::size_t PrmBeginCount() const noexcept;
159
161 [[nodiscard]] std::size_t PrmEndCount() const noexcept;
162
164 [[nodiscard]] std::size_t ApplicationReadyRequestCount() const noexcept;
165
167 [[nodiscard]] std::size_t ApplicationReadyResponseCount() const noexcept;
168
170 [[nodiscard]] std::size_t ReleaseCount() const noexcept;
171
173 [[nodiscard]] bool SawInputIocr() const noexcept;
174
176 [[nodiscard]] bool SawOutputIocr() const noexcept;
177
179 [[nodiscard]] bool SawExpectedSubmodules() const noexcept;
180
182 [[nodiscard]] bool SawAlarmCr() const noexcept;
183
185 [[nodiscard]] std::string LastError() const;
186
191 [[nodiscard]] bool WaitForState(
193 std::chrono::milliseconds timeout) const;
194
196 [[nodiscard]] std::uint16_t RpcPort() const noexcept;
197
198 private:
200 void Run();
201
205 void HandlePacket(const PNRPCHeader& request,
206 const asio::ip::udp::endpoint& sender);
207
209 void HandleConnect(const PNRPCHeader& request,
210 const asio::ip::udp::endpoint& sender);
211
213 void HandleControl(const PNRPCHeader& request,
214 const asio::ip::udp::endpoint& sender);
215
217 void HandleApplicationReadyResponse(const PNRPCHeader& response);
218
222 bool ValidateConnect(const Bytes& nrdPayload);
223
225 void SendResponse(const PNRPCHeader& request,
226 const Bytes& nrdPayload,
227 const asio::ip::udp::endpoint& sender);
228
231
233 static Bytes MakeNrd(const Bytes& payload);
234
236 static PNRPCHeader MakeResponse(const PNRPCHeader& request,
237 const Bytes& nrdPayload);
238
240 static Bytes MakeIocrResponse(
241 const PNIOCRBlockReqHeader& request,
242 std::uint16_t frameId);
243
245 static Bytes MakeAlarmCrResponse(std::uint16_t deviceAlarmReference);
246
248 static bool ContainsBlock(const Bytes& data, BlockType blockType);
249
251 static void ParseArIdentity(
252 const Bytes& connectPayload,
253 std::array<std::uint8_t, uuidLenght>& arUuid,
254 std::uint16_t& sessionKey);
256 void WakeWorker() noexcept;
257
260
262 mutable std::mutex mutex;
263 mutable std::condition_variable stateCondition;
264
265 std::atomic<bool> running{false};
266 std::thread worker;
269
270 std::unique_ptr<asio::io_context> ioContext;
271 std::unique_ptr<asio::ip::udp::socket> socket;
272
274 std::size_t connectCount = 0;
275 std::size_t prmBeginCount = 0;
276 std::size_t prmEndCount = 0;
279 std::size_t releaseCount = 0;
280
281 bool sawInputIocr = false;
282 bool sawOutputIocr = false;
284 bool sawAlarmCr = false;
285
286 std::string lastError;
287 std::array<std::uint8_t, uuidLenght> arUuid{};
288 std::uint16_t sessionKey = 0;
289 std::optional<asio::ip::udp::endpoint> controllerEndpoint;
290 std::optional<PNRPCHeader> lastApplicationReadyRequest;
291};
292
293} // namespace profinet::sim
Stateful UDP PROFINET IO-Device protocol simulator.
static bool ContainsBlock(const Bytes &data, BlockType blockType)
Find a block type in a serialized CONNECT payload.
void WakeWorker() noexcept
Wake the synchronous receive so the worker can observe shutdown.
std::size_t PrmEndCount() const noexcept
Return the number of PrmEnd requests accepted.
std::array< std::uint8_t, uuidLenght > arUuid
std::size_t PrmBeginCount() const noexcept
Return the number of PrmBegin requests accepted.
void ScheduleApplicationReady()
Start a cancellable ApplicationReady delay.
void SendApplicationReady()
Send a device-originated ApplicationReady request.
std::uint16_t RpcPort() const noexcept
Return the configured device RPC port.
std::unique_ptr< asio::ip::udp::socket > socket
std::size_t ApplicationReadyResponseCount() const noexcept
Return the number of ApplicationReady responses accepted.
std::optional< asio::ip::udp::endpoint > controllerEndpoint
void Stop() noexcept
Stop listening and join the worker thread.
void Start()
Start listening for controller RPC packets.
VirtualProfinetRpcDevice & operator=(const VirtualProfinetRpcDevice &)=delete
static Bytes MakeAlarmCrResponse(std::uint16_t deviceAlarmReference)
Build a minimal successful AlarmCRBlockRes.
void Run()
Worker loop receiving controller datagrams.
std::thread applicationReadyWorker
Delayed ApplicationReady worker.
std::unique_ptr< asio::io_context > ioContext
bool SawInputIocr() const noexcept
Return whether the controller supplied an Input IOCR.
void HandleApplicationReadyResponse(const PNRPCHeader &response)
Handle the response to the device-originated ApplicationReady.
void HandleControl(const PNRPCHeader &request, const asio::ip::udp::endpoint &sender)
Handle a CONTROL request such as PrmBegin or PrmEnd.
VirtualProfinetRpcDevice(const VirtualProfinetRpcDevice &)=delete
std::size_t ConnectCount() const noexcept
Return the number of CONNECT requests accepted.
std::size_t ApplicationReadyRequestCount() const noexcept
Return the number of ApplicationReady requests sent.
static void ParseArIdentity(const Bytes &connectPayload, std::array< std::uint8_t, uuidLenght > &arUuid, std::uint16_t &sessionKey)
Parse the AR UUID and session key from a CONNECT request.
VirtualProfinetDeviceState State() const noexcept
Return the current protocol state.
static Bytes MakeIocrResponse(const PNIOCRBlockReqHeader &request, std::uint16_t frameId)
Build one IOCRBlockRes from a controller IOCR request.
std::string LastError() const
Return the last diagnostic/rejection text.
bool ValidateConnect(const Bytes &nrdPayload)
Validate and extract the CONNECT blocks.
void HandlePacket(const PNRPCHeader &request, const asio::ip::udp::endpoint &sender)
Dispatch one complete RPC packet.
void SendResponse(const PNRPCHeader &request, const Bytes &nrdPayload, const asio::ip::udp::endpoint &sender)
Send a successful RPC response.
std::size_t ReleaseCount() const noexcept
Return the number of Release requests observed.
bool SawExpectedSubmodules() const noexcept
Return whether the controller supplied ExpectedSubmodule blocks.
static Bytes MakeNrd(const Bytes &payload)
Build an NRD wrapper with successful status.
void HandleConnect(const PNRPCHeader &request, const asio::ip::udp::endpoint &sender)
Handle a CONNECT request.
static PNRPCHeader MakeResponse(const PNRPCHeader &request, const Bytes &nrdPayload)
Build an RPC response preserving correlation fields.
bool SawAlarmCr() const noexcept
Return whether the controller supplied an AlarmCR.
bool SawOutputIocr() const noexcept
Return whether the controller supplied an Output IOCR.
bool WaitForState(VirtualProfinetDeviceState expected, std::chrono::milliseconds timeout) const
Wait until the simulator reaches a state.
constexpr std::uint16_t RPC_PORT
PROFINET IO RPC port (34964).
Definition rpcTypes.h:28
@ Fault
Application fault: movement stops, IOPS becomes BAD and alarms are emitted.
VirtualProfinetDeviceState
Observable Application Relation state of the virtual device.
@ ApplicationReady
The controller confirmed the device's ApplicationReady request.
@ Connected
A CONNECT was accepted and the AR is being configured.
@ Parameterizing
PrmBegin was accepted.
@ Parameterized
PrmEnd was accepted and the device can issue ApplicationReady.
@ Idle
No Application Relation exists.
@ Released
The controller released the AR.
const char * ToString(VirtualAumaScenario scenario) noexcept
Convert a virtual AUMA scenario into its stable CLI spelling.
BlockType
PROFINET block types.
Definition indices.h:89
constexpr int uuidLenght
Constant lenght of a UUID.
Definition util.h:40
std::array< std::uint8_t, macAddressLength > MacAddress
A 6-byte Ethernet MAC address.
Definition util.h:48
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:55
PROFINET protocol wire-format structures and serialization helpers.
PROFINET DCE/RPC protocol structures and IO-Device connection API.
IOCRBlockReq header: requests establishment of one IOCR (part of the CONNECT request).
Definition protocol.h:1984
DCE/RPC PDU header used to carry PROFINET connect/read/write/control requests.
Definition protocol.h:860
Configuration for a controller-facing virtual PROFINET device.
bool requireExpectedSubmodules
Require ExpectedSubmoduleBlockReq in cyclic CONNECT requests.
std::chrono::milliseconds applicationReadyDelay
Delay between accepting PrmEnd and sending ApplicationReady.
bool automaticApplicationReady
Automatically send ApplicationReady after PrmEnd.
bool requireBothIocrs
Require an Input and Output IOCR when CONNECT contains cyclic data.
std::string stationName
Station name reported by the virtual device.
std::string controllerAddress
Controller loopback address used for ApplicationReady.
std::uint16_t outputFrameId
Frame ID returned for the output IOCR.
std::uint16_t inputFrameId
Frame ID returned for the input IOCR.
bool requireAlarmCr
Require AlarmCR when cyclic CONNECT is requested.
std::uint16_t controllerCcontrolPort
Controller CControl listener port used for ApplicationReady.
std::uint16_t rpcPort
UDP port on which the virtual device accepts controller RPC.
MacAddress deviceMac
Device MAC address used by the virtual device model.
std::uint16_t deviceAlarmReference
Alarm reference returned by the device when AlarmCR is supported.
std::uint16_t frameId
Ethernet FrameID.
Bytes payload
Definition test_dcp.cpp:392