PROFINET IO Controller Stack 1.0.0
Modern C++ implementation of a PROFINET IO Controller stack
Loading...
Searching...
No Matches
rt.cpp
Go to the documentation of this file.
1
3
4#include "profinet/rt.h"
5
6#include <algorithm>
7#include <cstdio>
8#include <ranges>
9#include <stdexcept>
10
11namespace profinet::rt
12{
13
15{
16 static constexpr int minimumRtFrameSize = 6;
17 if (data.size() < minimumRtFrameSize)
18 {
19 throw std::runtime_error("RT frame too short: " + std::to_string(data.size()) + " bytes");
20 }
21
22 RTFrame f;
23 f.frameId = static_cast<std::uint16_t>((data[0] << OneOctetShift) | data[1]);
24 const std::size_t n = data.size();
25 f.cycleCounter = static_cast<std::uint16_t>((data[n - 4] << OneOctetShift) | data[n - 3]);
26 f.dataStatus = data[n - 2];
27 f.transferStatus = data[n - 1];
28 f.payload = Bytes(data.begin() + 2, data.end() - 4);
29 return f;
30}
31
33{
34 Bytes out;
35 out.push_back(static_cast<std::uint8_t>(frameId >> OneOctetShift));
36 out.push_back(static_cast<std::uint8_t>(frameId & LowByteMask));
37 out.insert(out.end(), payload.begin(), payload.end());
38 out.push_back(static_cast<std::uint8_t>(cycleCounter >> OneOctetShift));
39 out.push_back(static_cast<std::uint8_t>(cycleCounter & LowByteMask));
40 out.push_back(dataStatus);
41 out.push_back(transferStatus);
42 return out;
43}
44
45std::string RTFrame::ToString() const
46{
47 std::vector<std::string> flags;
48 if (IsValid())
49 {
50 flags.emplace_back("VALID");
51 }
52 if (IsRunning())
53 {
54 flags.emplace_back("RUN");
55 }
56 if (IsOk())
57 {
58 flags.emplace_back("OK");
59 }
60 if (IsPrimary())
61 {
62 flags.emplace_back("PRIMARY");
63 }
64 std::string flagsStr;
65 for (std::size_t i = 0; i < flags.size(); ++i)
66 {
67 flagsStr += flags[i];
68 if (i + 1 < flags.size())
69 {
70 flagsStr += "|";
71 }
72 }
73 if (flagsStr.empty())
74 {
75 flagsStr = "NONE";
76 }
77
78 return std::format("RTFrame(id=0x{:04X}, cycle={}, status={}, payload={}B)",
79 frameId, cycleCounter, flagsStr, payload.size());
80}
81
83 : config(std::move(config)),
84 writeBuffer(static_cast<std::size_t>(this->config.dataLength), 0),
85 sendBuffer(static_cast<std::size_t>(this->config.dataLength), 0)
86{
87}
88
89void CyclicDataBuilder::SetData(int slot, int subslot, const Bytes& data)
90{
91 for (const auto& obj : config.objects)
92 {
93 if (obj.slot == slot && obj.subslot == subslot)
94 {
95 const std::size_t copyLen = std::min<std::size_t>(data.size(), static_cast<std::size_t>(obj.dataLength));
96 const std::lock_guard<std::mutex> lock(writeLock);
97 std::ranges::copy(
98 data | std::views::take(copyLen),
99 writeBuffer.begin() + obj.frameOffset);
100 dirty = true;
101 return;
102 }
103 }
104 throw std::invalid_argument("Unknown slot/subslot: " + std::to_string(slot) + "/" + std::to_string(subslot));
105}
106
107Bytes CyclicDataBuilder::GetData(int slot, int subslot) const
108{
109 for (const auto& obj : config.objects)
110 {
111 if (obj.slot == slot && obj.subslot == subslot)
112 {
113 const std::lock_guard<std::mutex> lock(writeLock);
114 // NOLINTNEXTLINE(modernize-return-braced-init-list)
115 return Bytes(writeBuffer.begin() + obj.frameOffset,
116 writeBuffer.begin() + obj.frameOffset + obj.dataLength);
117 }
118 }
119 throw std::invalid_argument("Unknown slot/subslot: " + std::to_string(slot) + "/" + std::to_string(subslot));
120}
121
122void CyclicDataBuilder::SetIops(int slot, int subslot, std::uint8_t status)
123{
124 for (const auto& obj : config.objects)
125 {
126 if (obj.slot == slot && obj.subslot == subslot)
127 {
128 if (obj.dataLength > 0)
129 {
130 const std::lock_guard<std::mutex> lock(writeLock);
131 writeBuffer.at(static_cast<std::size_t>(obj.offsetIOPS)) = status;
132 dirty = true;
133 }
134 return;
135 }
136 }
137}
138
139void CyclicDataBuilder::SetIocs(int slot, int subslot, std::uint8_t status)
140{
141 for (const auto& obj : config.objects)
142 {
143 if (obj.slot == slot && obj.subslot == subslot)
144 {
145 if (obj.offsetIOCS > 0)
146 {
147 const std::lock_guard<std::mutex> lock(writeLock);
148 writeBuffer.at(static_cast<std::size_t>(obj.offsetIOCS)) = status;
149 dirty = true;
150 }
151 return;
152 }
153 }
154}
155
156void CyclicDataBuilder::SetAllIops(std::uint8_t status)
157{
158 const std::lock_guard<std::mutex> lock(writeLock);
159 for (const auto& obj : config.objects)
160 {
161 if (obj.dataLength > 0)
162 {
163 writeBuffer.at(static_cast<std::size_t>(obj.offsetIOPS)) = status;
164 }
165 }
166 dirty = true;
167}
168
169void CyclicDataBuilder::SetAllIocs(std::uint8_t status)
170{
171 const std::lock_guard<std::mutex> lock(writeLock);
172 for (const auto& obj : config.objects)
173 {
174 if (obj.offsetIOCS > 0)
175 {
176 writeBuffer.at(static_cast<std::size_t>(obj.offsetIOCS)) = status;
177 }
178 }
179 dirty = true;
180}
181
183{
184 const std::lock_guard<std::mutex> lock(writeLock);
185 std::ranges::fill(writeBuffer, 0);
186 dirty = true;
187}
188
190{
191 if (dirty)
192 {
193 const std::lock_guard<std::mutex> lock(writeLock);
195 dirty = false;
196 }
197}
198
200{
201 return sendBuffer;
202}
203
205{
206 const std::size_t copyLen = std::min(payload.size(), writeBuffer.size());
207 const std::lock_guard<std::mutex> lock(writeLock);
208 std::ranges::copy(payload | std::views::take(copyLen), writeBuffer.begin());
209 dirty = true;
210}
211
212Bytes BuildEthernetFrame(const MacAddress& dstMac, const MacAddress& srcMac, const RTFrame& rtFrame)
213{
214 Bytes out;
215 out.insert(out.end(), dstMac.begin(), dstMac.end());
216 out.insert(out.end(), srcMac.begin(), srcMac.end());
217 // need to check for VLAN ?
218 // out.push_back(static_cast<std::uint8_t>(VLAN_TAG_RT >> ThreeOctetsShift));
219 // out.push_back(static_cast<std::uint8_t>(VLAN_TAG_RT >> TwoOctetsShift));
220 // out.push_back(static_cast<std::uint8_t>(VLAN_TAG_RT >> OneOctetShift));
221 // out.push_back(static_cast<std::uint8_t>(VLAN_TAG_RT & LowByteMask));
222 out.push_back(static_cast<std::uint8_t>(ETHERTYPE_PROFINET >> OneOctetShift));
223 out.push_back(static_cast<std::uint8_t>(ETHERTYPE_PROFINET & LowByteMask));
224 Bytes frameBytes = rtFrame.ToBytes();
225 out.insert(out.end(), frameBytes.begin(), frameBytes.end());
226 return out;
227}
228
229std::optional<RTFrame> ParseEthernetFrame(const Bytes& data)
230{
231 if (data.size() < 18)
232 {
233 return std::nullopt; // 14 (eth) + 4 (min RT)
234 }
235
236 const auto ethertype = static_cast<std::uint16_t>((data[12] << OneOctetShift) | data[13]);
237 if (ethertype != ETHERTYPE_PROFINET)
238 {
239 return std::nullopt;
240 }
241
242 try
243 {
244 return RTFrame::FromBytes(Bytes(data.begin() + 14, data.end()));
245 }
246 catch (const std::exception&)
247 {
248 return std::nullopt;
249 }
250}
251
252} // namespace profinet::rt
std::mutex writeLock
Guards writeBuffer against concurrent access.
Definition rt.h:352
Bytes GetData(int slot, int subslot) const
Read one object's data from the write buffer.
Definition rt.cpp:107
Bytes writeBuffer
Buffer the application thread writes into.
Definition rt.h:346
CyclicDataBuilder(IOCRConfig config)
Construct a builder for the given IOCR configuration.
Definition rt.cpp:82
void Clear()
Zero the entire write buffer.
Definition rt.cpp:182
IOCRConfig config
IOCR configuration describing the objects this builder manages.
Definition rt.h:343
void Load(const Bytes &payload)
Load received payload data into the write buffer.
Definition rt.cpp:204
void SetAllIocs(std::uint8_t status=IOXS_GOOD)
Set every object's IOCS byte to the same value.
Definition rt.cpp:169
void SetData(int slot, int subslot, const Bytes &data)
Write one object's data into the write buffer.
Definition rt.cpp:89
void SetIocs(int slot, int subslot, std::uint8_t status=IOXS_GOOD)
Set one object's IOCS (consumer status) byte, if it has one.
Definition rt.cpp:139
bool dirty
Whether writeBuffer has changed since the last Swap().
Definition rt.h:355
void SetIops(int slot, int subslot, std::uint8_t status=IOXS_GOOD)
Set one object's IOPS (provider status) byte.
Definition rt.cpp:122
Bytes sendBuffer
Buffer the TX thread reads from (updated only by Swap()).
Definition rt.h:349
void Swap()
Promote the write buffer to the send buffer.
Definition rt.cpp:189
Bytes Build() const
Get the current send buffer contents.
Definition rt.cpp:199
void SetAllIops(std::uint8_t status=IOXS_GOOD)
Set every object's IOPS byte to the same value.
Definition rt.cpp:156
xmlXPathObjectPtr obj
Definition gsdml.cpp:79
std::optional< RTFrame > ParseEthernetFrame(const Bytes &data)
Parse a complete Ethernet frame and extract its RT frame.
Definition rt.cpp:229
Bytes BuildEthernetFrame(const MacAddress &dstMac, const MacAddress &srcMac, const RTFrame &rtFrame)
Build a complete Ethernet frame carrying an RT frame.
Definition rt.cpp:212
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:67
static constexpr int OneOctetShift
The bit-shift distance required to move data across a single octet.
Definition util.h:50
static constexpr std::uint8_t LowByteMask
Bitmask used to isolate the lowest significant byte (8 bits) of a larger integer.
Definition util.h:59
std::vector< std::uint8_t > Bytes
Generic byte buffer alias used throughout the library for raw wire data.
Definition protocol.h:52
Bytes payload
Definition rpc.cpp:3254
Declares PROFINET RT_CLASS_1 cyclic frame and IOCR data structures.
IOCR configuration derived from AR setup: timing parameters and IO object mappings needed for cyclic ...
Definition rt.h:126
std::vector< IODataObject > objects
IO data objects carried in this IOCR's cyclic frame.
Definition rt.h:152
A single PROFINET Real-Time cyclic frame: Frame ID + C_SDU payload + cycle counter/status trailer.
Definition rt.h:197
std::uint8_t dataStatus
Data status bitmask (see DATA_STATUS_* constants).
Definition rt.h:205
std::uint8_t transferStatus
Transfer status (0 = OK).
Definition rt.h:208
static RTFrame FromBytes(const Bytes &data)
Parse an RT frame from raw bytes (after the Ethernet header).
Definition rt.cpp:14
Bytes ToBytes() const
Serialize this frame back to raw bytes.
Definition rt.cpp:32
std::uint16_t frameId
Frame ID identifying which IOCR this frame belongs to.
Definition rt.h:199
std::uint16_t cycleCounter
Cycle counter, incremented each transmission.
Definition rt.h:202
bool IsPrimary() const
Whether the DataStatus state bit indicates Primary.
Definition rt.h:260
bool IsOk() const
Whether the DataStatus station-OK bit is set.
Definition rt.h:253
Bytes payload
C_SDU payload: process data plus IOPS/IOCS trailers.
Definition rt.h:211
bool IsValid() const
Whether the DataStatus valid bit is set.
Definition rt.h:239
bool IsRunning() const
Whether the DataStatus provider-run bit is set.
Definition rt.h:246
std::string ToString() const
Human-readable summary of this frame, for logging/debugging.
Definition rt.cpp:45