PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
cyclic.h
Go to the documentation of this file.
1
18#pragma once
19
20#include <asio/io_context.hpp>
21#include <atomic>
22#include <chrono>
23#include <cstdint>
24#include <functional>
25#include <map>
26#include <mutex>
27#include <optional>
28#include <string>
29#include <thread>
30#include <utility>
31
33#include "profinet/rt.h"
34#include "profinet/util.h"
35
37{
38
40enum class CyclicState
41{
43 Idle,
47 Running,
51 Stopped,
53 Fault,
54};
55
59std::string ToString(CyclicState state);
60
67{
69 std::atomic<std::uint64_t> framesSent{0};
70
72 std::atomic<std::uint64_t> framesReceived{0};
73
75 std::atomic<std::uint64_t> framesMissed{0};
76
78 std::atomic<std::uint64_t> framesInvalid{0};
79
81 std::atomic<std::uint64_t> framesDuplicate{0};
82
84 std::atomic<std::uint64_t> framesOutOfOrder{0};
85
87 std::atomic<std::uint32_t> lastCycleTimeUs{0};
88
90 std::atomic<std::uint32_t> maxJitterUs{0};
91
93 std::atomic<std::uint32_t> minCycleTimeUs{0x7FFFFFFF};
94
96 std::atomic<std::uint32_t> maxCycleTimeUs{0};
97
99 std::atomic<std::chrono::steady_clock::time_point> lastReceiveTime{std::chrono::steady_clock::now()};
100
102 std::atomic<int> consecutiveTimeouts{0};
103
105 std::atomic<std::uint64_t> cycleTimeSumUs{0};
106
108 std::atomic<std::uint64_t> cycleCount{0};
109
112 [[nodiscard]] std::uint64_t AvgCycleTimeUs() const
113 {
114 return (cycleCount != 0U) ? cycleTimeSumUs / cycleCount : 0;
115 }
116
118 void Reset();
119};
120
123{
124 std::uint64_t framesSent{};
125 std::uint64_t framesReceived{};
126 std::uint64_t framesMissed{};
127 std::uint64_t framesInvalid{};
128 std::uint64_t framesDuplicate{};
129 std::uint64_t framesOutOfOrder{};
130
131 std::uint32_t lastCycleTimeUs{};
132 std::uint32_t maxJitterUs{};
133 std::uint32_t minCycleTimeUs{};
134 std::uint32_t maxCycleTimeUs{};
135
136 std::chrono::steady_clock::time_point lastReceiveTime;
137
139
140 std::uint64_t cycleTimeSumUs{};
141 std::uint64_t cycleCount{};
142
145 [[nodiscard]] std::uint64_t AvgCycleTimeUs() const
146 {
147 return (cycleCount != 0U) ? cycleTimeSumUs / cycleCount : 0;
148 }
149};
150
151// NOLINTBEGIN(readability-identifier-naming)
153inline constexpr int MIN_CYCLE_MS = 8;
155inline constexpr int RECOMMENDED_CYCLE_MS = 32;
157inline constexpr int DEFAULT_MAX_CONSECUTIVE_TIMEOUTS = 3;
159inline constexpr int STOP_FRAME_COUNT = 3;
160// NOLINTEND(readability-identifier-naming)
172{
173 public:
179 virtual ~ICyclicController() = default;
180
188 virtual void Start(std::unique_ptr<IRawEthernetSocket> tx = nullptr, std::unique_ptr<IRawEthernetSocket> rx = nullptr) = 0;
189
199 virtual void Stop() = 0;
200
203 [[nodiscard]] virtual bool IsRunning() const = 0;
204
207 [[nodiscard]] virtual CyclicState State() const = 0;
208
213 virtual void SetOutputData(int slot, int subslot, const Bytes& data) = 0;
214
219 [[nodiscard]] virtual Bytes GetOutputData(int slot, int subslot) const = 0;
220
226 [[nodiscard]] virtual std::optional<Bytes> GetInputData(int slot, int subslot, bool allowBad = false) const = 0;
227};
228
248{
249 public:
251 friend class ReceivedIopsTest;
253 friend class CyclicTest;
254
264 CyclicController(asio::io_context& ioContext, std::string interface, MacAddress srcMac, MacAddress dstMac,
267 std::uint16_t etherType = rt::ETHERTYPE_PROFINET);
268
270 ~CyclicController() override;
271
274
277
283 void SetOutputData(int slot, int subslot, const Bytes& data) override;
284
289 Bytes GetOutputData(int slot, int subslot) const override;
290
301 std::optional<Bytes> GetInputData(int slot, int subslot, bool allowBad = false) const override;
302
320 void OnInputStatus(std::function<void(std::uint16_t slot, std::uint16_t subslot, std::uint8_t iops)> callback);
321
324 void OnInput(std::function<void(int slot, int subslot, const Bytes& data)> callback);
325
328 void OnOutput(std::function<void(int cycleCounter, const Bytes& data)> callback);
329
332 void OnTimeout(std::function<void()> callback);
333
336 void OnError(std::function<void(const std::string&)> callback);
337
340 void OnStateChange(std::function<void(CyclicState oldState, CyclicState newState)> callback);
341
346 void Start(std::unique_ptr<IRawEthernetSocket> tx = nullptr, std::unique_ptr<IRawEthernetSocket> rx = nullptr) override;
347
352 void Stop() override;
353
356 bool IsRunning() const override
357 {
358 return state == CyclicState::Running;
359 }
360
363 CyclicState State() const override
364 {
365 return state;
366 }
367
370 const CyclicStats& Stats() const
371 {
372 return stats;
373 }
374
375 [[nodiscard]] CyclicStatsSnapshot StatsSnapshot() const;
376
389 [[nodiscard]] std::optional<std::uint8_t> GetInputStatus(std::uint16_t slot, std::uint16_t subslot) const;
390
401 [[nodiscard]] bool IsInputGood(std::uint16_t slot, std::uint16_t subslot) const;
402
403 private:
406 void Transition(CyclicState newState);
407
409 void CheckCycleTime();
410
425 void TxCycle();
426
428 void TxLoop();
429
432 void SendOutputFrame(std::optional<std::uint8_t> dataStatusOverride = std::nullopt);
433
435 void SendStopFrames();
436
438 void RxLoop();
439
442
445 void ProcessInputFrame(const Bytes& data);
446
456 [[nodiscard]] bool LocalIsInputGood(std::uint16_t slot, std::uint16_t subslot) const;
457
460 std::map<std::pair<std::uint16_t, std::uint16_t>, std::uint8_t> inputStatus;
461
464 void TrackCycleCounter(std::uint16_t rxCounter);
465
467 asio::io_context& ioContext;
468
470 std::string interface;
471
474
477
480
483
486
488 std::atomic<CyclicState> state{CyclicState::Idle};
489
492
494 std::atomic<bool> running{false};
495
497 std::jthread txThread;
498
500 std::jthread rxThread;
501
503 std::unique_ptr<IRawEthernetSocket> txSock;
504
506 std::unique_ptr<IRawEthernetSocket> rxSock;
507
509 std::uint16_t cycleCounter = 0;
510
513
515 mutable std::mutex inputLock;
516
518 mutable std::mutex callbackMutex;
519
521 std::map<std::pair<int, int>, Bytes> inputData;
522
524 std::optional<std::uint16_t> lastRxCycleCounter;
525
527 std::uint32_t rxCounterStep;
528
530 std::uint32_t txCounterStep;
531
533 std::function<void(int, int, const std::uint8_t)> onInputStatus;
534
536 std::function<void(int, int, const Bytes&)> onInputData;
537
539 std::function<void(int, const Bytes&)> onOutputData;
540
542 std::function<void()> onTimeout;
543
545 std::function<void(const std::string&)> onError;
546
548 std::function<void(CyclicState, CyclicState)> onStateChange;
549
552
556};
557
558} // namespace profinet::cyclic
Abstraction for sending and receiving raw Ethernet frames.
Test fixture for cyclic controller protocol conformance.
Narrow private-access adapter used only by cyclic controller tests.
RT_CLASS_1 cyclic data exchange controller.
Definition cyclic.h:248
void OnInputStatus(std::function< void(std::uint16_t slot, std::uint16_t subslot, std::uint8_t iops)> callback)
Register a callback for PROFINET provider status (IOPS) changes.
Definition cyclic.cpp:196
const CyclicStats & Stats() const
Communication statistics for this controller.
Definition cyclic.h:370
bool iocsGood
Tracks the watchdog-driven IOCS state so the RX/timeout paths only rewrite the buffer on transitions ...
Definition cyclic.h:555
void TrackCycleCounter(std::uint16_t rxCounter)
Update cycle-counter tracking stats (gaps/duplicates/reordering) for a received frame.
Definition cyclic.cpp:796
bool IsRunning() const override
Whether the controller is actively exchanging cyclic data.
Definition cyclic.h:356
std::uint32_t rxCounterStep
Expected cycle-counter increment per received input frame.
Definition cyclic.h:527
rt::CyclicDataBuilder outputBuilder
Double-buffered builder for output cyclic data.
Definition cyclic.h:512
std::optional< Bytes > GetInputData(int slot, int subslot, bool allowBad=false) const override
Get the latest input data received from the device, if any or IOPS is BAD.
Definition cyclic.cpp:152
std::uint32_t txCounterStep
Cycle-counter increment applied per sent output frame.
Definition cyclic.h:530
std::mutex callbackMutex
Guards callback setting against concurrent access.
Definition cyclic.h:518
std::string interface
Network interface name.
Definition cyclic.h:470
void SendOutputFrame(std::optional< std::uint8_t > dataStatusOverride=std::nullopt)
Build and send one output frame.
Definition cyclic.cpp:444
std::unique_ptr< IRawEthernetSocket > rxSock
Raw socket used for receiving input frames.
Definition cyclic.h:506
void TxLoop()
TX thread body: sends output frames at the configured cycle rate.
Definition cyclic.cpp:370
CyclicController(const CyclicController &)=delete
Not copyable (owns sockets and background threads).
asio::io_context & ioContext
Asio execution context used by the raw Ethernet transports.
Definition cyclic.h:467
std::jthread txThread
Thread sending output frames.
Definition cyclic.h:497
std::mutex inputLock
Guards inputData against concurrent access.
Definition cyclic.h:515
void TxCycle()
Swap the output buffer and transmit one cyclic output frame.
Definition cyclic.cpp:361
void SendStopFrames()
Send STOP_FRAME_COUNT output frames with ProviderRun cleared.
Definition cyclic.cpp:486
rt::IOCRConfig outputIocr
Configuration for the output (controller -> device) IOCR.
Definition cyclic.h:482
void OnError(std::function< void(const std::string &)> callback)
Register a callback invoked on TX/RX socket errors.
Definition cyclic.cpp:218
CyclicState State() const override
The controller's current lifecycle state.
Definition cyclic.h:363
std::function< void(int, int, const std::uint8_t)> onInputStatus
Registered OnInputStatus callback, if any.
Definition cyclic.h:533
void OnStateChange(std::function< void(CyclicState oldState, CyclicState newState)> callback)
Register a callback invoked on every state transition.
Definition cyclic.cpp:223
void Stop() override
Stop gracefully.
Definition cyclic.cpp:289
~CyclicController() override
Stop (if running) and release resources.
Definition cyclic.cpp:95
void OnOutput(std::function< void(int cycleCounter, const Bytes &data)> callback)
Register a callback invoked whenever new output data is send.
Definition cyclic.cpp:208
std::atomic< bool > running
Whether the TX/RX threads should keep running.
Definition cyclic.h:494
void Transition(CyclicState newState)
Change state and invoke the OnStateChange callback if registered.
Definition cyclic.cpp:118
void CheckCycleTime()
Validate the configured cycle time, throwing if it's below 1ms.
Definition cyclic.cpp:103
void RxLoop()
RX thread body: receives and processes input frames, checking the watchdog.
Definition cyclic.cpp:516
void HandleWatchdogTimeout()
Handle a watchdog timeout: update stats and possibly transition to Fault.
Definition cyclic.cpp:557
std::jthread rxThread
Thread receiving input frames.
Definition cyclic.h:500
std::map< std::pair< std::uint16_t, std::uint16_t >, std::uint8_t > inputStatus
Last provider status (IOPS) the device sent per submodule. Payload bytes are only meaningful while th...
Definition cyclic.h:460
void SetOutputData(int slot, int subslot, const Bytes &data) override
Set output data for the next cycle.
Definition cyclic.cpp:137
std::function< void()> onTimeout
Registered OnTimeout callback, if any.
Definition cyclic.h:542
CyclicController & operator=(const CyclicController &)=delete
Not copyable (owns sockets and background threads).
int maxConsecutiveTimeouts
Consecutive watchdog timeouts before transitioning to Fault.
Definition cyclic.h:485
rt::IOCRConfig inputIocr
Configuration for the input (device -> controller) IOCR.
Definition cyclic.h:479
MacAddress srcMac
This host's MAC address.
Definition cyclic.h:473
void OnTimeout(std::function< void()> callback)
Register a callback invoked on each watchdog timeout.
Definition cyclic.cpp:213
bool LocalIsInputGood(std::uint16_t slot, std::uint16_t subslot) const
Check whether a submodule has GOOD provider status without locking.
Definition cyclic.cpp:184
CyclicStatsSnapshot StatsSnapshot() const
Definition cyclic.cpp:326
std::atomic< CyclicState > state
Current lifecycle state.
Definition cyclic.h:488
bool IsInputGood(std::uint16_t slot, std::uint16_t subslot) const
Check whether the device currently reports GOOD provider status.
Definition cyclic.cpp:177
std::function< void(CyclicState, CyclicState)> onStateChange
Registered OnStateChange callback, if any.
Definition cyclic.h:548
std::optional< std::uint8_t > GetInputStatus(std::uint16_t slot, std::uint16_t subslot) const
Get the last provider status (IOPS) received for a submodule.
Definition cyclic.cpp:163
std::function< void(const std::string &)> onError
Registered OnError callback, if any.
Definition cyclic.h:545
std::uint16_t etherType
Current lifecycle state.
Definition cyclic.h:491
std::unique_ptr< IRawEthernetSocket > txSock
Raw socket used for sending output frames.
Definition cyclic.h:503
std::map< std::pair< int, int >, Bytes > inputData
Latest received data per (slot, subslot).
Definition cyclic.h:521
MacAddress dstMac
The device's MAC address.
Definition cyclic.h:476
std::function< void(int, const Bytes &)> onOutputData
Registered OnOutputData callback, if any.
Definition cyclic.h:539
std::function< void(int, int, const Bytes &)> onInputData
Registered OnInput callback, if any.
Definition cyclic.h:536
void ProcessInputFrame(const Bytes &data)
Parse and process one received input frame.
Definition cyclic.cpp:595
std::uint16_t cycleCounter
Current TX cycle counter value.
Definition cyclic.h:509
Bytes GetOutputData(int slot, int subslot) const override
Get the current output data for a slot/subslot.
Definition cyclic.cpp:147
CyclicStats stats
Communication statistics.
Definition cyclic.h:551
void OnInput(std::function< void(int slot, int subslot, const Bytes &data)> callback)
Register a callback invoked whenever new input data is received.
Definition cyclic.cpp:202
std::optional< std::uint16_t > lastRxCycleCounter
Last received cycle counter, for gap/duplicate/reorder detection.
Definition cyclic.h:524
Abstract interface for controlling cyclic PROFINET communication.
Definition cyclic.h:172
virtual Bytes GetOutputData(int slot, int subslot) const =0
Read the currently configured output data for one slot/subslot.
virtual ~ICyclicController()=default
Virtual destructor.
virtual CyclicState State() const =0
The controller's current lifecycle state.
virtual void Stop()=0
Stops cyclic PROFINET communication.
virtual void Start(std::unique_ptr< IRawEthernetSocket > tx=nullptr, std::unique_ptr< IRawEthernetSocket > rx=nullptr)=0
Starts cyclic PROFINET communication.
virtual std::optional< Bytes > GetInputData(int slot, int subslot, bool allowBad=false) const =0
Get the latest valid input process data.
virtual void SetOutputData(int slot, int subslot, const Bytes &data)=0
Set complete process output data for one slot/subslot.
virtual bool IsRunning() const =0
Whether the controller is actively exchanging cyclic data.
Test fixture for received cyclic input IOPS behavior.
Builds C_SDU payload from IO data objects with double-buffering.
Definition rt.h:287
constexpr int DEFAULT_MAX_CONSECUTIVE_TIMEOUTS
Default number of consecutive watchdog timeouts before transitioning to Fault.
Definition cyclic.h:157
std::string ToString(CyclicState state)
Human-readable name for a CyclicState value.
Definition cyclic.cpp:24
constexpr int MIN_CYCLE_MS
Minimum cycle time (ms) considered reliable.
Definition cyclic.h:153
CyclicState
Lifecycle state of a CyclicController.
Definition cyclic.h:41
@ Fault
Communication failure (e.g. consecutive watchdog timeouts).
@ Running
Active cyclic data exchange.
@ Stopping
Graceful shutdown in progress (sending STOP frames).
@ Stopped
Fully stopped, threads joined.
@ Starting
Sockets created, threads launching.
@ Idle
Initial state, not yet started.
constexpr int RECOMMENDED_CYCLE_MS
Recommended cycle time (ms).
Definition cyclic.h:155
constexpr int STOP_FRAME_COUNT
Number of STOP frames sent during a graceful Stop().
Definition cyclic.h:159
constexpr std::uint16_t ETHERTYPE_PROFINET
EtherType used by PROFINET RT frames (0x8892).
Definition rt.h:34
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
Declares PROFINET RT_CLASS_1 cyclic frame and IOCR data structures.
Copyable snapshot of cyclic communication statistics.
Definition cyclic.h:123
std::chrono::steady_clock::time_point lastReceiveTime
Definition cyclic.h:136
std::uint64_t AvgCycleTimeUs() const
Compute average TX cycle duration.
Definition cyclic.h:145
Statistics for cyclic communication.
Definition cyclic.h:67
void Reset()
Reset all counters to their initial values.
Definition cyclic.cpp:44
std::atomic< std::uint32_t > maxJitterUs
Largest observed deviation from the target cycle time, in microseconds.
Definition cyclic.h:90
std::uint64_t AvgCycleTimeUs() const
Compute the average TX cycle duration.
Definition cyclic.h:112
std::atomic< std::uint64_t > framesReceived
Total number of input frames received.
Definition cyclic.h:72
std::atomic< std::uint64_t > framesInvalid
Total number of frames rejected for having an invalid DataStatus.
Definition cyclic.h:78
std::atomic< std::uint64_t > framesOutOfOrder
Total number of frames received out of cycle-counter order.
Definition cyclic.h:84
std::atomic< std::uint32_t > minCycleTimeUs
Smallest observed TX cycle duration, in microseconds.
Definition cyclic.h:93
std::atomic< std::uint64_t > cycleTimeSumUs
Running sum of observed TX cycle durations, in microseconds.
Definition cyclic.h:105
std::atomic< std::uint64_t > framesDuplicate
Total number of duplicate (repeated cycle counter) frames received.
Definition cyclic.h:81
std::atomic< std::uint64_t > framesMissed
Total number of watchdog timeouts (missed input frames).
Definition cyclic.h:75
std::atomic< std::uint32_t > lastCycleTimeUs
Duration of the most recently completed TX cycle, in microseconds.
Definition cyclic.h:87
std::atomic< std::uint64_t > framesSent
Total number of output frames sent.
Definition cyclic.h:69
std::atomic< std::uint64_t > cycleCount
Number of TX cycles included in CycleTimeSumUs.
Definition cyclic.h:108
std::atomic< int > consecutiveTimeouts
Current run of consecutive watchdog timeouts (resets on a good frame).
Definition cyclic.h:102
std::atomic< std::chrono::steady_clock::time_point > lastReceiveTime
Timestamp of the last received input frame (or watchdog check).
Definition cyclic.h:99
std::atomic< std::uint32_t > maxCycleTimeUs
Largest observed TX cycle duration, in microseconds.
Definition cyclic.h:96
IOCR configuration derived from AR setup: timing parameters and IO object mappings needed for cyclic ...
Definition rt.h:137
std::unique_ptr< MockRawEthernetSocket > tx
std::unique_ptr< MockRawEthernetSocket > rx
Declares Linux Ethernet, addressing, timing, and utility helpers used by the PROFINET stack.