Motion Master
Loading...
Searching...
No Matches
soem.h
Go to the documentation of this file.
1#pragma once
2
3#include <chrono>
4#include <cstdint>
5#include <functional>
6#include <map>
7#include <memory>
8#include <mutex>
9#include <sstream>
10#include <string>
11#include <type_traits>
12#include <unordered_map>
13#include <utility>
14#include <variant>
15#include <vector>
16
17#include <tl/expected.hpp>
18
19#include "base.h"
20#include "ethercat.h"
21#include "loguru.h"
22
23namespace mm::comm::soem {
24
36std::map<std::string, std::string> mapMacAddressesToInterfaces();
37
53inline std::string uiConfigWithDefaultPdoMapping = R"(
54{
55 "pdoMapping": {
56 "rx": {
57 "0x1600": [
58 "0x60400010",
59 "0x60600008",
60 "0x60710010",
61 "0x607a0020",
62 "0x60ff0020",
63 "0x60b20010",
64 "0x27010020"
65 ],
66 "0x1601": [
67 "0x60fe0120",
68 "0x60fe0220"
69 ],
70 "0x1602": [
71 "0x27030020",
72 "0x60b10020",
73 "0x20120120"
74 ]
75 },
76 "tx": {
77 "0x1a00": [
78 "0x60410010",
79 "0x60610008",
80 "0x60640020",
81 "0x606c0020",
82 "0x60770010",
83 "0x60f40020",
84 "0x21110220",
85 "0x21130220"
86 ],
87 "0x1a01": [
88 "0x24010010",
89 "0x24020010",
90 "0x24030010",
91 "0x24040010",
92 "0x27020020"
93 ],
94 "0x1a02": [
95 "0x60fd0020"
96 ],
97 "0x1a03": [
98 "0x27040020",
99 "0x20f00020",
100 "0x60fc0020",
101 "0x606b0020",
102 "0x60740010",
103 "0x60790020"
104 ]
105 }
106 }
107})";
108
133int setPdoMappingFromUiConfig(ecx_contextt* context, uint16 slave);
134
152void checkPdoMapping(ecx_contextt* context, uint16 slave);
153
167uint16_t getSlaveState(ecx_contextt* context, uint16_t slave);
168
188std::string convertSlaveStateToString(int state);
189
212void updateMailboxSyncManagersOnNextState(ecx_contextt* context, uint16_t slave,
213 int targetState);
214
230void configureDetectedSmmModule(ecx_contextt* context, uint16_t slave);
231
262void transitionSlaves(ecx_contextt* context,
263 const std::vector<uint16_t>& positions,
264 uint16_t requiredState, uint16_t targetState,
265 const char* targetName,
266 std::chrono::steady_clock::duration timeout,
267 std::chrono::steady_clock::duration resendInterval =
268 std::chrono::seconds(2));
269
282bool setSlaveState(ecx_contextt* context, uint16_t slave, uint16_t targetState,
283 std::chrono::steady_clock::duration timeout =
284 std::chrono::seconds(5));
285
319std::vector<uint8_t> readSii(ecx_contextt* context, uint16 position);
320
332mm::comm::base::PdoMappings readPdoMappings(ecx_contextt* context,
333 uint16_t slave);
334
350 const std::vector<mm::comm::base::PdoMappingEntry>& entries);
351
363void logIoMap(uint8_t* ioMap, size_t totalBytes);
364
379struct Fieldbus {
380 ecx_contextt context;
381 std::string iface;
382 uint8 group;
384
385 /* Used by the context */
386 uint8 map[4096];
387 ecx_portt port;
388 ec_slavet slavelist[EC_MAXSLAVE];
390 ec_groupt grouplist[EC_MAXGROUP];
391 uint8 esibuf[EC_MAXEEPBUF];
392 uint32 esimap[EC_MAXEEPBITMAP];
393 ec_eringt elist;
394 ec_idxstackT idxstack;
395 boolean ecaterror;
396 int64 DCtime;
397 ec_SMcommtypet SMcommtype[EC_MAX_MAPT];
398 ec_PDOassignt PDOassign[EC_MAX_MAPT];
399 ec_PDOdesct PDOdesc[EC_MAX_MAPT];
400 ec_eepromSMt eepSM;
401 ec_eepromFMMUt eepFMMU;
402};
403
418 uint16_t readLength;
419
426 uint16_t readOffset;
427
439
446 uint16_t writeLength;
447
454 uint16_t writeOffset;
455};
456
458 supportedProtocols, writeLength, writeOffset)
459
460
467struct SlaveInfoSyncManager {
476 uint32_t flags;
477
484 uint16_t length;
485
491 uint16_t startAddress;
492
503 uint8_t type;
504};
505
506NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SlaveInfoSyncManager, flags, length,
507 startAddress, type)
508
509
516struct SlaveInfoFmmu {
522 uint8_t active;
523
530 uint8_t logicalEndBit;
531
538 uint8_t logicalStartBit;
539
545 uint32_t logicalStartAddress;
546
552 uint16_t logicalLength;
553
560 uint8_t physicalStartBit;
561
567 uint16_t physicalStartAddress;
568
575 uint8_t type;
576};
577
578NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SlaveInfoFmmu, active, logicalEndBit,
579 logicalStartBit, logicalStartAddress,
580 logicalLength, physicalStartBit,
581 physicalStartAddress, type)
582
583
589struct SlaveInfo {
596 uint16_t alStatusCode;
597
604 uint16_t aliasAddress;
605
611 std::vector<SlaveInfoFmmu> fmmus;
612
619 SlaveInfoMailbox mailbox;
620
628 uint32_t productCode;
629
635 uint32_t position;
636
644 uint32_t revisionNumber;
645
652 std::vector<SlaveInfoSyncManager> syncManagers;
653
659 std::string name;
660
669 uint32_t vendorId;
670};
671
672NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SlaveInfo, alStatusCode, aliasAddress, fmmus,
673 mailbox, productCode, position,
674 revisionNumber, syncManagers, name, vendorId)
675
676
688SlaveInfo getSlaveInfo(ecx_contextt* context, uint16 position);
689
706void logSlaveInfo(ecx_contextt* context, uint16 position);
707
725void initFieldbus(std::unique_ptr<Fieldbus>& fieldbus,
726 const std::string& iface);
727
732class Slave {
733 public:
744 explicit Slave(ecx_contextt& context, uint16_t position);
745
754 uint16_t position() const;
755
764 uint32_t vendorId() const;
765
774 uint32_t productCode() const;
775
784 uint32_t revisionNumber() const;
785
791 int getState() const;
792
812 bool setState(int targetState, int timeoutMs = 0, int intervalMs = 100);
813
835 tl::expected<void, std::string> loadParameters(bool readValues = false);
836
849 void logParameters() const;
850
867
884
891 void clearParameters();
892
901 std::unordered_map<mm::comm::base::Parameter::Address,
904
917 mm::comm::base::Parameter& findParameter(uint16_t index, uint8_t subindex);
918
946 template <typename T = std::vector<uint8_t>>
947 T upload(uint16_t index, uint8_t subindex, bool refresh = true) {
948 // Lock the mailbox mutex to ensure thread safety
949 std::lock_guard<std::mutex> lock(mailboxMutex_);
950
951 // Check if the device is in a valid state for uploading
952 auto state = context_.slavelist[position_].state;
953 if (state == EC_STATE_INIT || state == EC_STATE_BOOT) {
954 throw std::runtime_error(
955 "To upload object dictionary entries, the device must be in the (2) "
956 "PRE-OPERATIONAL, (4) SAFE-OPERATIONAL, or (8) OPERATIONAL state. "
957 "The current state is " +
958 convertSlaveStateToString(state) + ".");
959 }
960
961 // Find the previously loaded parameter
962 auto& parameter = findParameter(index, subindex);
963
964 // Refresh data if needed (SDO read)
965 if (refresh) {
966 const std::unique_ptr<std::uint8_t[]> data =
967 std::make_unique<std::uint8_t[]>(parameter.byteLength);
968 auto wkc = ecx_SDOread(&context_, position_, parameter.index,
969 parameter.subindex, false, &parameter.byteLength,
970 data.get(), EC_TIMEOUTRXM * 3);
971 if (wkc <= 0) {
972 LOG_F(ERROR, "Device %d: SDO read failed for %#04x:%02x! WKC: %d",
973 position_, parameter.index, parameter.subindex, wkc);
974 auto id = mm::comm::base::makeParameterId(parameter.index,
975 parameter.subindex);
976 throw std::runtime_error("Device " + std::to_string(position_) +
977 ": SDO read failed for " + id +
978 "! WKC: " + std::to_string(wkc));
979 } else {
980 std::vector<uint8_t> receivedData(data.get(),
981 data.get() + parameter.byteLength);
982 if (!parameter.trySetValue(receivedData)) {
983 LOG_F(ERROR,
984 "Device %d: Failed to set the value of parameter %#04x:%02x!",
985 position_, parameter.index, parameter.subindex);
986 }
987 }
988 }
989
990 // Return the appropriate type
991 if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
992 return parameter.data; // parameter.data should be std::vector<uint8_t>
993 } else {
994 // Otherwise, try to get the value as the requested type T
995 auto value = parameter.getValue();
996 if (auto ptr = std::get_if<T>(&value)) {
997 return *ptr;
998 } else {
999 throw std::bad_variant_access(); // Type mismatch
1000 }
1001 }
1002 }
1003
1028 template <typename T = std::vector<uint8_t>>
1029 bool download(uint16_t index, uint8_t subindex, const T& value) {
1030 std::lock_guard<std::mutex> lock(mailboxMutex_);
1031
1032 // Check if device is in valid state for download
1033 auto state = context_.slavelist[position_].state;
1034 if (state == EC_STATE_INIT || state == EC_STATE_BOOT) {
1035 throw std::runtime_error(
1036 "To download object dictionary entries, the device must be in the "
1037 "(2) PRE-OPERATIONAL, (4) SAFE-OPERATIONAL, or (8) OPERATIONAL "
1038 "state. The current state is " +
1039 convertSlaveStateToString(state) + ".");
1040 }
1041
1042 auto& parameter = findParameter(index, subindex);
1043
1044 // Convert value to raw byte data
1045 std::vector<uint8_t> data;
1046 if constexpr (std::is_same_v<T, std::vector<uint8_t>>) {
1047 data = value;
1048 } else {
1049 // Set the value in the parameter model
1050 if (!parameter.trySetValue(value)) {
1051 LOG_F(ERROR,
1052 "Device %d: Failed to set the value of parameter %#04x:%02x!",
1053 position_, parameter.index, parameter.subindex);
1054 return false;
1055 }
1056 data = parameter.data; // Use the parameter's byte representation
1057 }
1058
1059 // Check if data size matches the expected parameter size
1060 if (data.size() != parameter.byteLength) {
1061 LOG_F(ERROR, "Data size mismatch: expected %d, got %zu",
1062 parameter.byteLength, data.size());
1063 throw std::runtime_error("Data size does not match the parameter size!");
1064 }
1065
1066 // Perform the SDO write
1067 auto wkc = ecx_SDOwrite(&context_, position_, parameter.index,
1068 parameter.subindex, false, parameter.byteLength,
1069 data.data(), EC_TIMEOUTRXM * 3);
1070
1071 // Check write result
1072 if (wkc <= 0) {
1073 LOG_F(ERROR, "Device %d: SDO write failed for %#04x:%02x! WKC: %d",
1074 position_, parameter.index, parameter.subindex, wkc);
1075 return false;
1076 }
1077
1078 return true;
1079 }
1080
1090
1099 void updatePdoMappings();
1100
1112 void logPdoMappings() const;
1113
1119 mm::comm::base::PdoMappings& getPdoMappings();
1120
1143 void updateOutputs();
1144
1166 void updateParametersFromInputs();
1167
1193 tl::expected<std::vector<uint8_t>, std::string> readFile(
1194 const std::string& filename,
1195 size_t maxFileSize = 1 * 1024 * 1024, // 1 MB default max file size
1196 int retries = 5,
1197 std::chrono::steady_clock::duration retryWait =
1198 std::chrono::milliseconds(100));
1199
1213 tl::expected<std::vector<std::uint8_t>, std::string> readSdo(
1214 uint16_t index, uint8_t subindex);
1215
1229 tl::expected<ec_ODlistt, std::string> readODList(
1230 int retries = 5, std::chrono::steady_clock::duration retryWait =
1231 std::chrono::milliseconds(100));
1232
1251 tl::expected<void, std::string> readODDescription(
1252 uint16_t item, ec_ODlistt& odList, int retries = 5,
1253 std::chrono::steady_clock::duration retryWait =
1254 std::chrono::milliseconds(100));
1255
1272 tl::expected<ec_OElistt, std::string> readOE(
1273 uint16_t item, ec_ODlistt& odList, int retries = 5,
1274 std::chrono::steady_clock::duration retryWait =
1275 std::chrono::milliseconds(100));
1276
1277 private:
1278 ecx_contextt& context_;
1279 const uint16_t position_;
1282 std::mutex mailboxMutex_;
1283 std::unordered_map<mm::comm::base::Parameter::Address,
1285 parametersMap_;
1288 pdoMappings_;
1289};
1290
1291class Master {
1292 public:
1299 explicit Master();
1300
1311 explicit Master(const std::string& iface);
1312
1364 void init(const std::string& iface);
1365
1376 void deinit();
1377
1384 ~Master();
1385
1395 Fieldbus& getFieldbus() const;
1396
1405 std::string getInterfaceName() const;
1406
1415 size_t getIoMapSize() const;
1416
1479 tl::expected<int, std::string> initSlaves();
1480
1492 const std::vector<std::unique_ptr<Slave>>& slaves() const;
1493
1503 inline int expectedWkc() const {
1504 return fieldbus_->context.grouplist[fieldbus_->group].outputsWKC * 2 +
1505 fieldbus_->context.grouplist[fieldbus_->group].inputsWKC;
1506 }
1507
1517 int roundtrip();
1518
1548
1559 bool start();
1560
1561 private:
1575 std::unique_ptr<Fieldbus> fieldbus_;
1576
1588 std::vector<std::unique_ptr<Slave>> slaves_;
1589};
1590}; // namespace mm::comm::soem
Represents a device parameter identified by index and subindex.
Definition base.h:533
std::pair< uint16_t, uint8_t > Address
Alias for a pair of uint16_t and uint8_t representing a parameter address.
Definition base.h:543
int roundtrip()
Performs a complete send/receive process data cycle on the fieldbus.
Definition soem.cc:1351
Master()
Default constructor for the Master class.
Definition soem.cc:1272
const std::vector< std::unique_ptr< Slave > > & slaves() const
Returns a reference to the list of slaves.
Definition soem.cc:1347
tl::expected< int, std::string > initSlaves()
Initializes the slave devices on the EtherCAT network.
Definition soem.cc:1324
void exchangeProcessDataAndUpdateParameters()
Exchanges process data with slaves and updates parameters based on the response.
Definition soem.cc:1356
size_t getIoMapSize() const
Returns the total size of the I/O map for the current group.
Definition soem.cc:1319
Fieldbus & getFieldbus() const
Returns a reference to the Fieldbus instance.
Definition soem.cc:1315
void init(const std::string &iface)
Initializes the fieldbus using the specified network interface.
Definition soem.cc:1279
std::string getInterfaceName() const
Retrieves the interface name used by the fieldbus.
Definition soem.cc:1317
void deinit()
Deinitializes the fieldbus and releases associated resources.
Definition soem.cc:1297
bool start()
Initializes and starts the fieldbus master.
Definition soem.cc:1379
~Master()
Destructor for the Master class.
Definition soem.cc:1313
int expectedWkc() const
Calculates the expected working counter (WKC) for the current fieldbus group.
Definition soem.h:1503
uint32_t productCode() const
Gets the Product Code of the slave device.
Definition soem.cc:774
uint32_t vendorId() const
Gets the Vendor ID of the slave device.
Definition soem.cc:770
std::unordered_map< mm::comm::base::Parameter::Address, mm::comm::base::Parameter > & parametersMap()
Returns a reference to the internal parameters map.
Definition soem.cc:1068
mm::comm::base::Parameter & findParameter(uint16_t index, uint8_t subindex)
Finds and returns a reference to a Parameter in the parameters map.
Definition soem.cc:1072
uint32_t revisionNumber() const
Gets the Revision Number of the slave device.
Definition soem.cc:778
T upload(uint16_t index, uint8_t subindex, bool refresh=true)
Uploads a parameter value from the device's object dictionary.
Definition soem.h:947
tl::expected< void, std::string > loadParameters(bool readValues=false)
Loads the parameters (object dictionary entries) from the EtherCAT slave device.
Definition soem.cc:879
void clearParameters()
Clears all loaded object dictionary parameters.
Definition soem.cc:1064
void logTxPdoMappedParameters()
Logs all mapped TxPDO parameters with their current values.
Definition soem.cc:1049
Slave(ecx_contextt &context, uint16_t position)
Constructs a Slave object with a given context and position.
Definition soem.cc:765
void logRxPdoMappedParameters()
Logs all mapped RxPDO parameters with their current values.
Definition soem.cc:1034
bool setState(int targetState, int timeoutMs=0, int intervalMs=100)
Attempts to set the slave to the specified EtherCAT state and optionally waits until it is reached.
Definition soem.cc:784
void logParameters() const
Logs all loaded object dictionary parameters.
Definition soem.cc:1028
int getState() const
Reads the current state of the slave directly from the device.
Definition soem.cc:782
uint16_t position() const
Gets the position of the slave.
Definition soem.cc:768
bool download(uint16_t index, uint8_t subindex, const T &value)
Downloads a value to the specified object dictionary entry via SDO.
Definition soem.h:1029
uint8_t type
Definition co_dictionary.h:1
const char ** name
Definition co_dictionary.h:7
uint8_t * value
Definition co_dictionary.h:9
uint16_t index
Definition co_dictionary.h:0
std::string makeParameterId(int index, int subindex)
Formats the given index and subindex into a parameter identifier string.
Definition base.h:1654
Definition soem.cc:40
mm::comm::base::PdoMappings readPdoMappings(ecx_contextt *context, uint16_t slave)
Read and parse all mapped PDO entries for a given slave.
Definition soem.cc:672
uint16_t getSlaveState(ecx_contextt *context, uint16_t slave)
Reads the current state of a specific EtherCAT slave directly from the device via an FPRD datagram.
Definition soem.cc:398
void logPdoMappingEntries(const std::vector< mm::comm::base::PdoMappingEntry > &entries)
Logs PDO mapping entries grouped by PDO index.
Definition soem.cc:734
std::string convertSlaveStateToString(int state)
Converts a numeric EtherCAT state value into a human-readable string.
Definition soem.cc:413
void transitionSlaves(ecx_contextt *context, const std::vector< uint16_t > &positions, uint16_t requiredState, uint16_t targetState, const char *targetName, std::chrono::steady_clock::duration timeout, std::chrono::steady_clock::duration resendInterval)
Transitions multiple EtherCAT slaves to a target state using a single-threaded fire-then-poll pattern...
Definition soem.cc:442
std::string uiConfigWithDefaultPdoMapping
Default UI configuration used for PDO mapping.
Definition soem.h:53
void initFieldbus(std::unique_ptr< Fieldbus > &fieldbus, const std::string &iface)
Initializes a Fieldbus instance for EtherCAT communication.
Definition soem.cc:1239
void updateMailboxSyncManagersOnNextState(ecx_contextt *context, uint16_t slave, int targetState)
Updates the mailbox and sync manager (SM) configurations for an EtherCAT slave when transitioning to ...
Definition soem.cc:512
void logIoMap(uint8_t *ioMap, size_t totalBytes)
Logs the I/O map as a formatted hexadecimal string.
Definition soem.cc:755
void checkPdoMapping(ecx_contextt *context, uint16 slave)
Diagnoses and prints the PDO mapping for a given slave.
Definition soem.cc:316
void configureDetectedSmmModule(ecx_contextt *context, uint16_t slave)
Reads the detected SMM module ID from the slave device and configures the module identification objec...
Definition soem.cc:599
std::map< std::string, std::string > mapMacAddressesToInterfaces()
Maps MAC addresses to their corresponding network interface names.
Definition soem.cc:42
int setPdoMappingFromUiConfig(ecx_contextt *context, uint16 slave)
Sets PDO mapping for a specified EtherCAT slave using UI configuration.
Definition soem.cc:221
uint16 position
Definition soem.h:688
void logSlaveInfo(ecx_contextt *context, uint16 position)
Logs detailed information about a specific EtherCAT slave in JSON format.
Definition soem.cc:215
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(SlaveInfoMailbox, readLength, readOffset, supportedProtocols, writeLength, writeOffset) struct SlaveInfoSyncManager
Information about a slave's Sync Manager (SM) configuration.
Definition soem.h:457
SlaveInfo getSlaveInfo(ecx_contextt *context, uint16 position)
Definition soem.cc:163
std::vector< uint8_t > readSii(ecx_contextt *context, uint16 position)
Read the complete Slave Information Interface (SII) EEPROM from an EtherCAT slave device.
Definition soem.cc:629
bool setSlaveState(ecx_contextt *context, uint16_t slave, uint16_t targetState, std::chrono::steady_clock::duration timeout)
Transitions a single EtherCAT slave to targetState.
Definition soem.cc:504
Specialization of std::hash for std::pair<uint16_t, uint8_t>.
Definition util.h:612
Represents the mapped PDO entries for a slave device.
Definition base.h:175
Structure representing the Fieldbus configuration and context for EtherCAT communication.
Definition soem.h:379
boolean ecaterror
Definition soem.h:395
ec_idxstackT idxstack
Definition soem.h:394
ec_eepromFMMUt eepFMMU
Definition soem.h:401
ec_groupt grouplist[EC_MAXGROUP]
Definition soem.h:390
std::string iface
Definition soem.h:381
uint8 group
Definition soem.h:382
int slavecount
Definition soem.h:389
uint8 esibuf[EC_MAXEEPBUF]
Definition soem.h:391
uint32 esimap[EC_MAXEEPBITMAP]
Definition soem.h:392
int64 DCtime
Definition soem.h:396
ec_eepromSMt eepSM
Definition soem.h:400
ec_PDOassignt PDOassign[EC_MAX_MAPT]
Definition soem.h:398
uint8 map[4096]
Definition soem.h:386
ecx_contextt context
Definition soem.h:380
int roundtripTime
Definition soem.h:383
ec_PDOdesct PDOdesc[EC_MAX_MAPT]
Definition soem.h:399
ecx_portt port
Definition soem.h:387
ec_slavet slavelist[EC_MAXSLAVE]
Definition soem.h:388
ec_SMcommtypet SMcommtype[EC_MAX_MAPT]
Definition soem.h:397
ec_eringt elist
Definition soem.h:393
Information about a slave's mailbox configuration.
Definition soem.h:411
uint16_t writeOffset
Offset of the mailbox write area within the slave's memory.
Definition soem.h:454
uint16_t supportedProtocols
Supported mailbox protocols as a bitmask.
Definition soem.h:438
uint16_t readOffset
Offset of the mailbox read area within the slave's memory.
Definition soem.h:426
uint16_t readLength
Length of the mailbox read area (in bytes).
Definition soem.h:418
uint16_t writeLength
Length of the mailbox write area (in bytes).
Definition soem.h:446