ESI.cpp
Go to the documentation of this file.
1#include "ESI.h"
2
4
5#include "../ErrorReporting.h"
6
7extern "C"
8{
9#include <ethercat.h>
10}
11
13{
14
15 std::optional<std::vector<std::byte>>
16 ESIHandler::readESIBinaryBlob(std::uint16_t slaveIndex,
17 std::uint16_t startAddress,
18 std::uint16_t endAddress) const
19 {
20 bool pdiHadEEPROMControl = static_cast<bool>(ec_slave[slaveIndex].eep_pdi);
21 if (ec_eeprom2master(slaveIndex) == 0)
22 {
24 "Failed to read ESI of slave at index %u - could not get control of the EEPROM.",
25 slaveIndex);
26 return std::nullopt;
27 }
28
29 std::vector<std::byte> result = readFromEEPROM(
30 slaveIndex, startAddress, endAddress, ec_slave[slaveIndex].eep_8byte > 0); // NOLINT
31
32 if (pdiHadEEPROMControl)
33 {
34 ec_eeprom2pdi(slaveIndex);
35 }
36
37 return std::move(result);
38 }
39
40 std::vector<std::byte>
41 ESIHandler::readFromEEPROM(std::uint16_t slaveIndex,
42 std::uint16_t startAddress,
43 std::uint16_t endAddress,
44 bool has64BitPackets) const
45 {
46 ARMARX_CHECK_GREATER_EQUAL(endAddress, startAddress);
47
48 std::vector<std::byte> result;
49
50 // Some slaves return data from the EEPROM in 4 byte (2 word) units,
51 // others in 8 byte (4 word) units.
52 std::uint16_t addressIncr = 2;
53 if (has64BitPackets)
54 {
55 addressIncr = 4;
56 }
57
58 result.reserve((endAddress - startAddress) * addressIncr * 2);
59
60 std::uint16_t slaveConfiguredAddress = ec_slave[slaveIndex].configadr;
61
62 for (std::uint16_t currentAddress = startAddress; currentAddress < endAddress;
63 currentAddress += addressIncr)
64 {
65 std::uint64_t eepromData =
66 ec_readeepromFP(slaveConfiguredAddress, currentAddress, EC_TIMEOUTEEP);
67
68 // Repack the result into some std::bytes
69 for (int i = 0; i < addressIncr * 2; ++i)
70 {
71 static const int byteLength = 8;
72 static const int byteMask = 0xFF;
73 result.push_back(std::byte((eepromData >> (i * byteLength)) & byteMask));
74 }
75 }
76
77 return result;
78 }
79
80 std::uint8_t
81 getU8(const std::vector<std::byte>& esiBinary, std::uint16_t address)
82 {
83 if (esiBinary.size() < static_cast<std::size_t>(address + 1))
84 ARMARX_FATAL << "The ESI binary ended unexpectedly!";
85 return static_cast<std::uint8_t>(esiBinary.at(address));
86 }
87
88 std::uint16_t
89 getU16(const std::vector<std::byte>& esiBinary, std::uint16_t address)
90 {
91 if (esiBinary.size() < static_cast<std::size_t>(address + 2))
92 ARMARX_FATAL << "The ESI binary ended unexpectedly!";
93 const std::uint16_t byteSize = 8;
94 return static_cast<std::uint16_t>(esiBinary.at(address)) &
95 (static_cast<std::uint16_t>(esiBinary.at(address + 1)) << byteSize);
96 }
97
98 std::int16_t
99 get16(const std::vector<std::byte>& esiBinary, std::uint16_t address)
100 {
101 auto unsignedValue = getU16(esiBinary, address);
102 return *reinterpret_cast<std::int16_t*>(&unsignedValue);
103 }
104
105 std::uint16_t
106 getU16W(const std::vector<std::byte>& esiBinary, std::uint16_t wordAddress)
107 {
108 return getU16(esiBinary, wordAddress * 2);
109 }
110
111 std::uint32_t
112 getU32(const std::vector<std::byte>& esiBinary, std::uint16_t address)
113 {
114 if (esiBinary.size() < static_cast<std::size_t>(address + 4))
115 ARMARX_FATAL << "The ESI binary ended unexpectedly!";
116 const auto byteSize = 8;
117 return static_cast<std::uint32_t>(esiBinary.at(address)) +
118 (static_cast<std::uint32_t>(esiBinary.at(address + 1)) << byteSize) +
119 (static_cast<std::uint32_t>(esiBinary.at(address + 2)) << 2 * byteSize) +
120 (static_cast<std::uint32_t>(esiBinary.at(address + 3)) << 3 * byteSize);
121 }
122
123 std::uint32_t
124 getU32W(const std::vector<std::byte>& esiBinary, std::uint16_t wordAddress)
125 {
126 return getU32(esiBinary, wordAddress * 2);
127 }
128
129 ESIData
130 ESIParser::parseESI(const std::vector<std::byte>& esiBinary)
131 {
132 ESIData esiData{};
133 esiData.header = parseHeader(esiBinary);
134 std::uint16_t categoryOffset = 0x0040;
135 auto categoryType = getU16W(esiBinary, categoryOffset);
136 // Attention - the MSB is undefined! -> setting to zero.
137 categoryType &= (1 << 15) - 1;
138 auto categorySize = getU16W(esiBinary, categoryOffset + 1);
139 while (categoryType != 0x7fff)
140 {
141 switch (categoryType)
142 {
143 case 0:
144 // NOP
145 break;
146 // 1 - 9 are device specific categories -> ignored
147 case 10:
148 // STRINGS
149 esiData.strings = parseStrings(esiBinary, categoryOffset + 2);
150 break;
151 case 20:
152 // DataTypes
153 // "for future use" -> ignored
154 break;
155 case 30:
156 // General
157 esiData.general = parseGeneral(esiBinary, categoryOffset + 2);
158 break;
159 case 40:
160 // FMMU
161 esiData.fmmu = parseFMMU(esiBinary, categoryOffset + 2, categorySize);
162 break;
163 case 41:
164 // SyncM
165 esiData.syncM = parseSyncM(esiBinary, categoryOffset + 2, categorySize);
166 break;
167 case 50:
168 // TxPDO
169 esiData.txPDO = parsePDOs(esiBinary, categoryOffset + 2, categorySize);
170 break;
171 case 51:
172 // RxPDO
173 esiData.rxPDO = parsePDOs(esiBinary, categoryOffset + 2, categorySize);
174 break;
175 // 60+ are reserved or vendor specific -> ignored
176 }
177 categoryOffset += 2 + categorySize;
178 categoryType = getU16W(esiBinary, categoryOffset);
179 // Attention - the MSB is undefined! -> setting to zero.
180 categoryType &= (1 << 15) - 1;
181 categorySize = getU16W(esiBinary, categoryOffset + 1);
182 }
183 return esiData;
184 }
185
187 ESIParser::parseHeader(const std::vector<std::byte>& esiBinary)
188 {
189 ESIHeader esiHeader;
190 esiHeader.pdiControl = getU16W(esiBinary, 0x0000);
191 esiHeader.pdiConfiguration = getU16W(esiBinary, 0x0001);
192 esiHeader.syncImpulseLen = getU16W(esiBinary, 0x0002);
193 esiHeader.pdiConfiguration2 = getU16W(esiBinary, 0x0003);
194 esiHeader.stationAlias = getU16W(esiBinary, 0x0004);
195 esiHeader.checkSum = getU16W(esiBinary, 0x0007);
196 esiHeader.vendorID = getU32W(esiBinary, 0x0008);
197 esiHeader.productCode = getU32W(esiBinary, 0x000A);
198 esiHeader.revisionNumber = getU32W(esiBinary, 0x000C);
199 esiHeader.serialNumber = getU32W(esiBinary, 0x000E);
200 esiHeader.bootstrapReceiveMailboxOffset = getU16W(esiBinary, 0x0014);
201 esiHeader.bootstrapReceiveMailboxSize = getU16W(esiBinary, 0x0015);
202 esiHeader.bootstrapSendMailboxOffset = getU16W(esiBinary, 0x0016);
203 esiHeader.bootstrapSendMailboxSize = getU16W(esiBinary, 0x0017);
204 esiHeader.standardReceiveMailboxOffset = getU16W(esiBinary, 0x0018);
205 esiHeader.standardReceiveMailboxSize = getU16W(esiBinary, 0x0019);
206 esiHeader.standardSendMailboxOffset = getU16W(esiBinary, 0x001A);
207 esiHeader.standardSendMailboxSize = getU16W(esiBinary, 0x001B);
208 esiHeader.mailboxProtocol = getU16W(esiBinary, 0x001C);
209 esiHeader.eepromSize = getU16W(esiBinary, 0x003E);
210 esiHeader.version = getU16W(esiBinary, 0x003F);
211 return esiHeader;
212 }
213
214 std::vector<std::string>
215 ESIParser::parseStrings(const std::vector<std::byte>& esiBinary, std::uint16_t wordOffset)
216 {
217 std::vector<std::string> strings;
218 auto nStrings = getU8(esiBinary, 2 * wordOffset);
219 strings.reserve(nStrings);
220 std::uint16_t currentOffset = 2 * wordOffset + 1;
221 for (auto i = 0; i < nStrings; ++i)
222 {
223 std::uint8_t len;
224 std::string tmp;
225 len = static_cast<std::uint8_t>(esiBinary.at(currentOffset));
226 ++currentOffset;
227 if (currentOffset + len > esiBinary.size())
228 ARMARX_FATAL << "The ESI binary ended unexpectedly while reading a string!";
229 tmp.assign(reinterpret_cast<const char*>(&esiBinary[currentOffset]), len);
230 strings.emplace_back(tmp);
231 currentOffset += len;
232 }
233 return strings;
234 }
235
237 ESIParser::parseGeneral(const std::vector<std::byte>& esiBinary, std::uint16_t wordOffset)
238 {
239 ESIGeneral esiGeneral;
240 std::uint16_t offset = 2 * wordOffset;
241 esiGeneral.groupIdx = getU8(esiBinary, offset + 0x0000);
242 esiGeneral.imgIdx = getU8(esiBinary, offset + 0x0001);
243 esiGeneral.orderIdx = getU8(esiBinary, offset + 0x0002);
244 esiGeneral.nameIdx = getU8(esiBinary, offset + 0x0003);
245 esiGeneral.coEDetails = getU8(esiBinary, offset + 0x0005);
246 esiGeneral.foEDetails = getU8(esiBinary, offset + 0x0006);
247 esiGeneral.eoEDetails = getU8(esiBinary, offset + 0x0007);
248 esiGeneral.soEChannels = getU8(esiBinary, offset + 0x0008);
249 esiGeneral.dS402Channels = getU8(esiBinary, offset + 0x0009);
250 esiGeneral.sysmanClass = getU8(esiBinary, offset + 0x000a);
251 esiGeneral.flags = getU8(esiBinary, offset + 0x000b);
252 esiGeneral.currentOnEBus = get16(esiBinary, offset + 0x000c);
253 esiGeneral.physicalPort = getU16(esiBinary, offset + 0x0010);
254 esiGeneral.physicalMemoryAddress = getU16(esiBinary, offset + 0x0012);
255 return esiGeneral;
256 }
257
258 ESIFMMU
259 ESIParser::parseFMMU(const std::vector<std::byte>& esiBinary,
260 std::uint16_t wordOffset,
261 std::uint16_t len)
262 {
263 std::uint16_t count = 2 * len;
264 ESIFMMU esiFmmu;
265 esiFmmu.reserve(count);
266 for (std::uint16_t i = 0; i < count; ++i)
267 {
268 esiFmmu.emplace_back(getU8(esiBinary, 2 * wordOffset + i));
269 }
270 return esiFmmu;
271 }
272
274 ESIParser::parseSyncM(const std::vector<std::byte>& esiBinary,
275 std::uint16_t wordOffset,
276 std::uint16_t len)
277 {
278 const std::uint16_t syncMElementLen = 8;
279 const std::uint16_t count = 2 * len / syncMElementLen;
280 ESISyncM esiSyncM;
281 esiSyncM.reserve(count);
282 std::uint16_t currentOffset = 2 * wordOffset;
283 for (size_t i = 0; i < count; ++i)
284 {
285 ESISyncMElement elem;
286 elem.physicalStartAddress = getU16(esiBinary, currentOffset + 0x0000);
287 elem.length = getU16(esiBinary, currentOffset + 0x0002);
288 elem.controlRegister = getU8(esiBinary, currentOffset + 0x0004);
289 elem.statusRegister = getU8(esiBinary, currentOffset + 0x0005);
290 elem.enableSynchManager = getU8(esiBinary, currentOffset + 0x0006);
291 elem.syncManagerType = getU8(esiBinary, currentOffset + 0x0007);
292 esiSyncM.emplace_back(elem);
293 currentOffset += syncMElementLen;
294 }
295 return esiSyncM;
296 }
297
298 std::vector<ESIPDOObject>
299 ESIParser::parsePDOs(const std::vector<std::byte>& esiBinary,
300 std::uint16_t wordOffset,
301 std::uint16_t len)
302 {
303 std::vector<ESIPDOObject> esiPDOs;
304 std::uint16_t currentOffset = 2 * wordOffset;
305 while (currentOffset < 2 * (wordOffset + len))
306 {
307 ESIPDOObject pdoObject;
308 pdoObject.pdoIndex = getU16(esiBinary, currentOffset + 0x0000);
309 pdoObject.entryCount = getU8(esiBinary, currentOffset + 0x0002);
310 pdoObject.syncManager = getU8(esiBinary, currentOffset + 0x0003);
311 pdoObject.synchronization = getU8(esiBinary, currentOffset + 0x0004);
312 pdoObject.nameIdx = getU8(esiBinary, currentOffset + 0x0005);
313 pdoObject.flags = getU16(esiBinary, currentOffset + 0x0006);
314 pdoObject.entries = std::vector<ESIPDOEntry>{pdoObject.entryCount};
315 currentOffset += 8;
316 for (int i = 0; i < pdoObject.entryCount; ++i)
317 {
318 ESIPDOEntry pdoEntry;
319 pdoEntry.index = getU16(esiBinary, currentOffset + 0x0000);
320 pdoEntry.subIndex = getU8(esiBinary, currentOffset + 0x0002);
321 pdoEntry.nameIdx = getU8(esiBinary, currentOffset + 0x0003);
322 pdoEntry.dataType = getU8(esiBinary, currentOffset + 0x0004);
323 pdoEntry.bitLength = getU8(esiBinary, currentOffset + 0x0005);
324 pdoEntry.flags = getU16(esiBinary, currentOffset + 0x0006);
325 pdoObject.entries.emplace_back(pdoEntry);
326 currentOffset += 8;
327 }
328 esiPDOs.emplace_back(pdoObject);
329 }
330 return esiPDOs;
331 }
332} // namespace armarx::control::ethercat
#define GENERAL_ERROR(...)
uint16_t slaveConfiguredAddress
std::optional< std::vector< std::byte > > readESIBinaryBlob(std::uint16_t slaveIndex, std::uint16_t startAddress, std::uint16_t endAddress) const
Definition ESI.cpp:16
std::vector< std::string > parseStrings(const std::vector< std::byte > &esiBinary, std::uint16_t wordOffset)
Definition ESI.cpp:215
ESIFMMU parseFMMU(const std::vector< std::byte > &esiBinary, std::uint16_t wordOffset, std::uint16_t len)
Definition ESI.cpp:259
ESIGeneral parseGeneral(const std::vector< std::byte > &esiBinary, std::uint16_t wordOffset)
Definition ESI.cpp:237
std::vector< ESIPDOObject > parsePDOs(const std::vector< std::byte > &esiBinary, std::uint16_t wordOffset, std::uint16_t len)
Definition ESI.cpp:299
ESIHeader parseHeader(const std::vector< std::byte > &esiBinary)
Definition ESI.cpp:187
ESIData parseESI(const std::vector< std::byte > &esiBinary)
Parse a standard-conformant binary in SII format to an ESI structure.
Definition ESI.cpp:130
ESISyncM parseSyncM(const std::vector< std::byte > &esiBinary, std::uint16_t wordOffset, std::uint16_t len)
Definition ESI.cpp:274
#define ARMARX_CHECK_GREATER_EQUAL(lhs, rhs)
This macro evaluates whether lhs is greater or equal (>=) rhs and if it turns out to be false it will...
#define ARMARX_FATAL
The logging level for unexpected behaviour, that will lead to a seriously malfunctioning program and ...
Definition Logging.h:199
std::uint8_t getU8(const std::vector< std::byte > &esiBinary, std::uint16_t address)
Definition ESI.cpp:81
std::uint16_t getU16W(const std::vector< std::byte > &esiBinary, std::uint16_t wordAddress)
Definition ESI.cpp:106
std::uint16_t getU16(const std::vector< std::byte > &esiBinary, std::uint16_t address)
Definition ESI.cpp:89
std::int16_t get16(const std::vector< std::byte > &esiBinary, std::uint16_t address)
Definition ESI.cpp:99
std::uint32_t getU32W(const std::vector< std::byte > &esiBinary, std::uint16_t wordAddress)
Definition ESI.cpp:124
std::uint32_t getU32(const std::vector< std::byte > &esiBinary, std::uint16_t address)
Definition ESI.cpp:112
std::vector< ESISyncMElement > ESISyncM
Definition ESI.h:135
std::vector< std::uint8_t > ESIFMMU
Definition ESI.h:123
Holds ESI data that can be read from slaves via SII.
Definition ESI.h:167
std::uint16_t physicalMemoryAddress
Definition ESI.h:118
std::uint16_t bootstrapReceiveMailboxOffset
Definition ESI.h:78
std::uint16_t standardReceiveMailboxSize
Definition ESI.h:83
std::uint16_t standardReceiveMailboxOffset
Definition ESI.h:82
std::uint16_t standardSendMailboxSize
Definition ESI.h:85
std::uint16_t pdiConfiguration2
Definition ESI.h:71
std::uint16_t standardSendMailboxOffset
Definition ESI.h:84
std::uint16_t bootstrapReceiveMailboxSize
Definition ESI.h:79
std::uint16_t bootstrapSendMailboxSize
Definition ESI.h:81
std::uint16_t bootstrapSendMailboxOffset
Definition ESI.h:80
std::vector< ESIPDOEntry > entries
Definition ESI.h:155