feat(ethercat): add motor bus runtime
This commit is contained in:
parent
d292360a8d
commit
c9c7e43a70
@ -4,7 +4,18 @@ add_library(motor_bus_runtime SHARED
|
|||||||
ethercat/src/ethercat_motor_bus_runtime.cpp
|
ethercat/src/ethercat_motor_bus_runtime.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(motor_bus_runtime PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
set(IGH_ETHERCAT_ROOT
|
||||||
|
${CMAKE_SOURCE_DIR}/dependency/x86/third_party/ethercat/v1.7.0
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(motor_bus_runtime
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
PRIVATE
|
||||||
|
${IGH_ETHERCAT_ROOT}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_directories(motor_bus_runtime PRIVATE ${IGH_ETHERCAT_ROOT}/lib)
|
||||||
|
|
||||||
target_link_libraries(motor_bus_runtime
|
target_link_libraries(motor_bus_runtime
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@ -12,9 +23,28 @@ target_link_libraries(motor_bus_runtime
|
|||||||
cmvr_es::device::motor_core
|
cmvr_es::device::motor_core
|
||||||
cmvr_es::mujoco_world
|
cmvr_es::mujoco_world
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
ethercat
|
||||||
cmvr_es::device::canbus
|
cmvr_es::device::canbus
|
||||||
glog
|
glog
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(cmvr_es::device::motor_bus_runtime ALIAS motor_bus_runtime)
|
add_library(cmvr_es::device::motor_bus_runtime ALIAS motor_bus_runtime)
|
||||||
install(TARGETS motor_bus_runtime LIBRARY DESTINATION lib)
|
install(TARGETS motor_bus_runtime LIBRARY DESTINATION lib)
|
||||||
|
|
||||||
|
add_executable(ethercat_motor_bus_runtime_real_test
|
||||||
|
ethercat/src/ethercat_motor_bus_runtime_real_test.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(ethercat_motor_bus_runtime_real_test
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_SOURCE_DIR}/cmvr-es
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(ethercat_motor_bus_runtime_real_test
|
||||||
|
PRIVATE
|
||||||
|
cmvr_es::device::motor_bus_runtime
|
||||||
|
gtest
|
||||||
|
gtest_main
|
||||||
|
pthread
|
||||||
|
glog
|
||||||
|
)
|
||||||
|
|||||||
@ -1,10 +1,22 @@
|
|||||||
#ifndef CMVR_ES_ETHERCAT_MOTOR_BUS_RUNTIME_H
|
#ifndef CMVR_ES_ETHERCAT_MOTOR_BUS_RUNTIME_H
|
||||||
#define CMVR_ES_ETHERCAT_MOTOR_BUS_RUNTIME_H
|
#define CMVR_ES_ETHERCAT_MOTOR_BUS_RUNTIME_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <type_traits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "../../abstract_motor_bus_runtime.h"
|
#include "../../abstract_motor_bus_runtime.h"
|
||||||
|
#include "motor/bus_runtime/ethercat/include/ethercat_pdo_mapping.h"
|
||||||
|
|
||||||
|
typedef struct ec_domain ec_domain_t;
|
||||||
|
typedef struct ec_master ec_master_t;
|
||||||
|
typedef struct ec_slave_config ec_slave_config_t;
|
||||||
|
|
||||||
namespace cmvr::device {
|
namespace cmvr::device {
|
||||||
|
|
||||||
@ -17,12 +29,130 @@ public:
|
|||||||
|
|
||||||
const std::string& id() const { return id_; }
|
const std::string& id() const { return id_; }
|
||||||
const config::EtherCATConfig& config() const { return config_; }
|
const config::EtherCATConfig& config() const { return config_; }
|
||||||
|
void setPdoMapping(EthercatPdoMapping mapping);
|
||||||
|
const EthercatPdoMapping& pdoMapping() const { return pdo_mapping_; }
|
||||||
const config::EthercatSlaveConfig* slaveForMotor(int motor_id) const;
|
const config::EthercatSlaveConfig* slaveForMotor(int motor_id) const;
|
||||||
|
bool hasMotor(int motor_id) const;
|
||||||
|
|
||||||
|
bool hasPdoEntry(int motor_id, std::uint16_t index, std::uint8_t subindex) const;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool writePdo(int motor_id, std::uint16_t index, std::uint8_t subindex, T value)
|
||||||
|
{
|
||||||
|
return writePdoRaw_(motor_id, index, subindex, valueBitLength_<T>(),
|
||||||
|
toRawValue_(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool readPdo(int motor_id, std::uint16_t index, std::uint8_t subindex, T& value) const
|
||||||
|
{
|
||||||
|
std::uint64_t raw = 0;
|
||||||
|
if (!readPdoRaw_(motor_id, index, subindex, valueBitLength_<T>(), raw)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = fromRawValue_<T>(raw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool writeSdo(int motor_id, std::uint16_t index, std::uint8_t subindex, T value)
|
||||||
|
{
|
||||||
|
return writeSdoRaw_(motor_id, index, subindex, valueBitLength_<T>(),
|
||||||
|
toRawValue_(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool readSdo(int motor_id, std::uint16_t index, std::uint8_t subindex, T& value)
|
||||||
|
{
|
||||||
|
std::uint64_t raw = 0;
|
||||||
|
if (!readSdoRaw_(motor_id, index, subindex, valueBitLength_<T>(), raw)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = fromRawValue_<T>(raw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct PdoEntryRuntime {
|
||||||
|
EthercatPdoEntryConfig cfg;
|
||||||
|
unsigned int offset{0};
|
||||||
|
bool rx{false};
|
||||||
|
std::uint64_t value{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SlaveRuntime {
|
||||||
|
config::EthercatSlaveConfig cfg;
|
||||||
|
ec_slave_config_t* slave_config{nullptr};
|
||||||
|
std::unordered_map<std::uint32_t, PdoEntryRuntime> pdo_entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool configureSlave_(SlaveRuntime& slave);
|
||||||
|
void cyclicLoop_();
|
||||||
|
void readFeedbackLocked_();
|
||||||
|
void writeCommandsLocked_();
|
||||||
|
void releaseMaster_();
|
||||||
|
bool hasValidPdoMapping_() const;
|
||||||
|
bool writePdoRaw_(int motor_id, std::uint16_t index, std::uint8_t subindex,
|
||||||
|
std::uint8_t bit_len, std::uint64_t value);
|
||||||
|
bool readPdoRaw_(int motor_id, std::uint16_t index, std::uint8_t subindex,
|
||||||
|
std::uint8_t bit_len, std::uint64_t& value) const;
|
||||||
|
bool writeSdoRaw_(int motor_id, std::uint16_t index, std::uint8_t subindex,
|
||||||
|
std::uint8_t bit_len, std::uint64_t value);
|
||||||
|
bool readSdoRaw_(int motor_id, std::uint16_t index, std::uint8_t subindex,
|
||||||
|
std::uint8_t bit_len, std::uint64_t& value);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr std::uint8_t valueBitLength_()
|
||||||
|
{
|
||||||
|
using ValueType = std::remove_cv_t<T>;
|
||||||
|
static_assert(std::is_integral_v<ValueType>, "EtherCAT object values must be integral");
|
||||||
|
static_assert(!std::is_same_v<ValueType, bool>, "bool is not a valid EtherCAT object value");
|
||||||
|
static_assert(sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4,
|
||||||
|
"only 8/16/32-bit EtherCAT object values are supported");
|
||||||
|
return static_cast<std::uint8_t>(sizeof(ValueType) * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static std::uint64_t toRawValue_(T value)
|
||||||
|
{
|
||||||
|
using ValueType = std::remove_cv_t<T>;
|
||||||
|
using UnsignedType = std::make_unsigned_t<ValueType>;
|
||||||
|
return static_cast<std::uint64_t>(static_cast<UnsignedType>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T fromRawValue_(std::uint64_t raw)
|
||||||
|
{
|
||||||
|
using ValueType = std::remove_cv_t<T>;
|
||||||
|
using UnsignedType = std::make_unsigned_t<ValueType>;
|
||||||
|
const auto unsigned_value = static_cast<UnsignedType>(raw);
|
||||||
|
ValueType value{};
|
||||||
|
std::memcpy(&value, &unsigned_value, sizeof(ValueType));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::uint32_t pdoEntryKey_(std::uint16_t index, std::uint8_t subindex);
|
||||||
|
static std::string hexIndex_(std::uint32_t index);
|
||||||
|
static std::uint64_t maskValue_(std::uint64_t value, std::uint8_t bit_len);
|
||||||
|
static bool isSupportedBitLength_(std::uint8_t bit_len);
|
||||||
|
static std::uint64_t readEntryValue_(const std::uint8_t* domain_data,
|
||||||
|
const PdoEntryRuntime& entry);
|
||||||
|
static void writeEntryValue_(std::uint8_t* domain_data,
|
||||||
|
const PdoEntryRuntime& entry);
|
||||||
|
|
||||||
std::string id_;
|
std::string id_;
|
||||||
config::EtherCATConfig config_;
|
config::EtherCATConfig config_;
|
||||||
std::unordered_map<int, const config::EthercatSlaveConfig*> slaves_by_motor_id_;
|
EthercatPdoMapping pdo_mapping_;
|
||||||
|
std::unordered_map<int, SlaveRuntime> slaves_by_motor_id_;
|
||||||
|
|
||||||
|
ec_master_t* master_{nullptr};
|
||||||
|
ec_domain_t* domain_{nullptr};
|
||||||
|
std::uint8_t* domain_data_{nullptr};
|
||||||
|
|
||||||
|
mutable std::mutex data_mutex_;
|
||||||
|
std::thread cyclic_thread_;
|
||||||
|
std::atomic<bool> running_{false};
|
||||||
|
bool initialized_{false};
|
||||||
bool started_{false};
|
bool started_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef CMVR_ES_ETHERCAT_PDO_MAPPING_H
|
||||||
|
#define CMVR_ES_ETHERCAT_PDO_MAPPING_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace cmvr::device {
|
||||||
|
|
||||||
|
struct EthercatPdoEntryConfig {
|
||||||
|
std::uint16_t index{0};
|
||||||
|
std::uint8_t subindex{0};
|
||||||
|
std::uint8_t bit_len{0};
|
||||||
|
std::string name;
|
||||||
|
bool padding{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EthercatPdoConfig {
|
||||||
|
std::uint16_t index{0};
|
||||||
|
std::uint8_t sync_manager{0};
|
||||||
|
bool rx{false};
|
||||||
|
std::vector<EthercatPdoEntryConfig> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EthercatPdoMapping {
|
||||||
|
std::uint32_t vendor_id{0};
|
||||||
|
std::uint32_t product_code{0};
|
||||||
|
std::string name;
|
||||||
|
std::vector<EthercatPdoConfig> rx_pdos;
|
||||||
|
std::vector<EthercatPdoConfig> tx_pdos;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cmvr::device
|
||||||
|
|
||||||
|
#endif // CMVR_ES_ETHERCAT_PDO_MAPPING_H
|
||||||
@ -1,7 +1,17 @@
|
|||||||
#include "motor/bus_runtime/ethercat/include/ethercat_motor_bus_runtime.h"
|
#include "motor/bus_runtime/ethercat/include/ethercat_motor_bus_runtime.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "common/base/logging/logger.h"
|
#include "common/base/logging/logger.h"
|
||||||
|
|
||||||
|
#include <ecrt.h>
|
||||||
|
|
||||||
namespace cmvr::device {
|
namespace cmvr::device {
|
||||||
|
|
||||||
bool EthercatMotorBusRuntime::init(const config::MotorGroupConfig& group_cfg)
|
bool EthercatMotorBusRuntime::init(const config::MotorGroupConfig& group_cfg)
|
||||||
@ -21,14 +31,15 @@ bool EthercatMotorBusRuntime::init(const config::MotorGroupConfig& group_cfg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
config_ = group_cfg.ethercat();
|
config_ = group_cfg.ethercat();
|
||||||
if (config_.master_id().empty()) {
|
|
||||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] master_id is empty: " << id_;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (config_.cycle_us() <= 0) {
|
if (config_.cycle_us() <= 0) {
|
||||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] cycle_us must be positive: " << id_;
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] cycle_us must be positive: " << id_;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!hasValidPdoMapping_()) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] PDO mapping is not configured: "
|
||||||
|
<< id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
slaves_by_motor_id_.clear();
|
slaves_by_motor_id_.clear();
|
||||||
for (const auto& slave : config_.slaves()) {
|
for (const auto& slave : config_.slaves()) {
|
||||||
@ -36,34 +47,95 @@ bool EthercatMotorBusRuntime::init(const config::MotorGroupConfig& group_cfg)
|
|||||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] invalid motor_id in slave config: " << id_;
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] invalid motor_id in slave config: " << id_;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (slave.slave_index() < 0) {
|
|
||||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] invalid slave_index for motor "
|
|
||||||
<< slave.motor_id() << " in group: " << id_;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (slaves_by_motor_id_.count(slave.motor_id()) > 0) {
|
if (slaves_by_motor_id_.count(slave.motor_id()) > 0) {
|
||||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] duplicate slave motor_id: "
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] duplicate slave motor_id: "
|
||||||
<< slave.motor_id() << " in group: " << id_;
|
<< slave.motor_id() << " in group: " << id_;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
slaves_by_motor_id_[slave.motor_id()] = &slave;
|
SlaveRuntime runtime;
|
||||||
|
runtime.cfg = slave;
|
||||||
|
slaves_by_motor_id_.emplace(slave.motor_id(), std::move(runtime));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (slaves_by_motor_id_.empty()) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] no EtherCAT slaves configured: " << id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
master_ = ecrt_request_master(config_.master_index());
|
||||||
|
if (!master_) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to request EtherCAT master "
|
||||||
|
<< config_.master_index() << ": " << id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain_ = ecrt_master_create_domain(master_);
|
||||||
|
if (!domain_) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to create EtherCAT domain: " << id_;
|
||||||
|
releaseMaster_();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& [motor_id, slave] : slaves_by_motor_id_) {
|
||||||
|
(void)motor_id;
|
||||||
|
if (!configureSlave_(slave)) {
|
||||||
|
releaseMaster_();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized_ = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EthercatMotorBusRuntime::setPdoMapping(EthercatPdoMapping mapping)
|
||||||
|
{
|
||||||
|
pdo_mapping_ = std::move(mapping);
|
||||||
|
}
|
||||||
|
|
||||||
bool EthercatMotorBusRuntime::start()
|
bool EthercatMotorBusRuntime::start()
|
||||||
{
|
{
|
||||||
if (started_) {
|
if (started_) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] EtherCAT master is not implemented yet: " << id_;
|
if (!initialized_ || !master_ || !domain_) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] runtime is not initialized: " << id_;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ecrt_master_activate(master_) != 0) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to activate EtherCAT master: " << id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
domain_data_ = ecrt_domain_data(domain_);
|
||||||
|
if (!domain_data_) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to get EtherCAT domain data: " << id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
writeCommandsLocked_();
|
||||||
|
}
|
||||||
|
|
||||||
|
running_.store(true);
|
||||||
|
cyclic_thread_ = std::thread(&EthercatMotorBusRuntime::cyclicLoop_, this);
|
||||||
|
started_ = true;
|
||||||
|
CMVR_LOG(INFO) << "[EthercatMotorBusRuntime] started EtherCAT runtime: " << id_;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void EthercatMotorBusRuntime::stop()
|
void EthercatMotorBusRuntime::stop()
|
||||||
{
|
{
|
||||||
|
running_.store(false);
|
||||||
|
if (cyclic_thread_.joinable()) {
|
||||||
|
cyclic_thread_.join();
|
||||||
|
}
|
||||||
started_ = false;
|
started_ = false;
|
||||||
|
initialized_ = false;
|
||||||
|
domain_data_ = nullptr;
|
||||||
|
releaseMaster_();
|
||||||
}
|
}
|
||||||
|
|
||||||
const config::EthercatSlaveConfig* EthercatMotorBusRuntime::slaveForMotor(const int motor_id) const
|
const config::EthercatSlaveConfig* EthercatMotorBusRuntime::slaveForMotor(const int motor_id) const
|
||||||
@ -72,7 +144,532 @@ const config::EthercatSlaveConfig* EthercatMotorBusRuntime::slaveForMotor(const
|
|||||||
if (it == slaves_by_motor_id_.end()) {
|
if (it == slaves_by_motor_id_.end()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return it->second;
|
return &it->second.cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::hasMotor(const int motor_id) const
|
||||||
|
{
|
||||||
|
return slaves_by_motor_id_.count(motor_id) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::hasPdoEntry(const int motor_id,
|
||||||
|
const std::uint16_t index,
|
||||||
|
const std::uint8_t subindex) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
const auto slave_it = slaves_by_motor_id_.find(motor_id);
|
||||||
|
if (slave_it == slaves_by_motor_id_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return slave_it->second.pdo_entries.count(pdoEntryKey_(index, subindex)) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::configureSlave_(SlaveRuntime& slave)
|
||||||
|
{
|
||||||
|
slave.slave_config = ecrt_master_slave_config(master_,
|
||||||
|
static_cast<std::uint16_t>(slave.cfg.alias()),
|
||||||
|
static_cast<std::uint16_t>(slave.cfg.position()),
|
||||||
|
pdo_mapping_.vendor_id,
|
||||||
|
pdo_mapping_.product_code);
|
||||||
|
if (!slave.slave_config) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to get slave config: group=" << id_
|
||||||
|
<< ", motor_id=" << slave.cfg.motor_id()
|
||||||
|
<< ", alias=" << slave.cfg.alias()
|
||||||
|
<< ", position=" << slave.cfg.position()
|
||||||
|
<< ", vendor=" << hexIndex_(pdo_mapping_.vendor_id)
|
||||||
|
<< ", product=" << hexIndex_(pdo_mapping_.product_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SyncBuild {
|
||||||
|
bool rx{false};
|
||||||
|
std::vector<std::uint16_t> pdo_indices;
|
||||||
|
std::vector<std::vector<ec_pdo_entry_info_t>> entry_storage;
|
||||||
|
std::vector<ec_pdo_info_t> pdo_infos;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto add_pdo_to_sync_build = [](std::map<std::uint8_t, SyncBuild>& builds,
|
||||||
|
const EthercatPdoConfig& pdo) {
|
||||||
|
auto& build = builds[pdo.sync_manager];
|
||||||
|
build.rx = pdo.rx;
|
||||||
|
build.pdo_indices.push_back(pdo.index);
|
||||||
|
auto& entries = build.entry_storage.emplace_back();
|
||||||
|
entries.reserve(pdo.entries.size());
|
||||||
|
for (const auto& entry : pdo.entries) {
|
||||||
|
entries.push_back({entry.index, entry.subindex, entry.bit_len});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::uint8_t, SyncBuild> sync_builds;
|
||||||
|
for (const auto& pdo : pdo_mapping_.rx_pdos) {
|
||||||
|
add_pdo_to_sync_build(sync_builds, pdo);
|
||||||
|
}
|
||||||
|
for (const auto& pdo : pdo_mapping_.tx_pdos) {
|
||||||
|
add_pdo_to_sync_build(sync_builds, pdo);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& [sync_manager, build] : sync_builds) {
|
||||||
|
(void)sync_manager;
|
||||||
|
build.pdo_infos.reserve(build.entry_storage.size());
|
||||||
|
for (std::size_t i = 0; i < build.entry_storage.size(); ++i) {
|
||||||
|
auto& entries = build.entry_storage[i];
|
||||||
|
build.pdo_infos.push_back({
|
||||||
|
build.pdo_indices[i],
|
||||||
|
static_cast<unsigned int>(entries.size()),
|
||||||
|
entries.data(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ec_sync_info_t> sync_infos;
|
||||||
|
sync_infos.push_back({0, EC_DIR_OUTPUT, 0, nullptr, EC_WD_DISABLE});
|
||||||
|
sync_infos.push_back({1, EC_DIR_INPUT, 0, nullptr, EC_WD_DISABLE});
|
||||||
|
for (auto& [sync_manager, build] : sync_builds) {
|
||||||
|
const auto direction = build.rx ? EC_DIR_OUTPUT : EC_DIR_INPUT;
|
||||||
|
const auto watchdog = build.rx ? EC_WD_ENABLE : EC_WD_DISABLE;
|
||||||
|
sync_infos.push_back({
|
||||||
|
sync_manager,
|
||||||
|
direction,
|
||||||
|
static_cast<unsigned int>(build.pdo_infos.size()),
|
||||||
|
build.pdo_infos.data(),
|
||||||
|
watchdog,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sync_infos.push_back({0xff});
|
||||||
|
|
||||||
|
if (ecrt_slave_config_pdos(slave.slave_config, EC_END, sync_infos.data()) != 0) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to configure PDOs: group=" << id_
|
||||||
|
<< ", motor_id=" << slave.cfg.motor_id()
|
||||||
|
<< ", position=" << slave.cfg.position();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto register_entry = [&](const EthercatPdoEntryConfig& entry, const bool rx) -> bool {
|
||||||
|
if (entry.padding) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!isSupportedBitLength_(entry.bit_len)) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] unsupported PDO entry bit length: "
|
||||||
|
<< static_cast<int>(entry.bit_len)
|
||||||
|
<< ", entry=" << hexIndex_(entry.index)
|
||||||
|
<< ":" << static_cast<int>(entry.subindex)
|
||||||
|
<< ", motor_id=" << slave.cfg.motor_id()
|
||||||
|
<< ", group=" << id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = pdoEntryKey_(entry.index, entry.subindex);
|
||||||
|
if (slave.pdo_entries.count(key) > 0) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] duplicate PDO entry: "
|
||||||
|
<< hexIndex_(entry.index)
|
||||||
|
<< ":" << static_cast<int>(entry.subindex)
|
||||||
|
<< ", motor_id=" << slave.cfg.motor_id()
|
||||||
|
<< ", group=" << id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int result = ecrt_slave_config_reg_pdo_entry(
|
||||||
|
slave.slave_config, entry.index, entry.subindex, domain_, nullptr);
|
||||||
|
if (result < 0) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to register PDO entry "
|
||||||
|
<< hexIndex_(entry.index) << ":" << static_cast<int>(entry.subindex)
|
||||||
|
<< ", motor_id=" << slave.cfg.motor_id()
|
||||||
|
<< ", group=" << id_;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PdoEntryRuntime runtime;
|
||||||
|
runtime.cfg = entry;
|
||||||
|
runtime.offset = static_cast<unsigned int>(result);
|
||||||
|
runtime.rx = rx;
|
||||||
|
slave.pdo_entries.emplace(key, std::move(runtime));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& pdo : pdo_mapping_.rx_pdos) {
|
||||||
|
for (const auto& entry : pdo.entries) {
|
||||||
|
if (!register_entry(entry, true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& pdo : pdo_mapping_.tx_pdos) {
|
||||||
|
for (const auto& entry : pdo.entries) {
|
||||||
|
if (!register_entry(entry, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthercatMotorBusRuntime::cyclicLoop_()
|
||||||
|
{
|
||||||
|
const auto period = std::chrono::microseconds(config_.cycle_us());
|
||||||
|
auto next_time = std::chrono::steady_clock::now();
|
||||||
|
while (running_.load()) {
|
||||||
|
next_time += period;
|
||||||
|
|
||||||
|
ecrt_master_receive(master_);
|
||||||
|
ecrt_domain_process(domain_);
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
readFeedbackLocked_();
|
||||||
|
writeCommandsLocked_();
|
||||||
|
}
|
||||||
|
ecrt_domain_queue(domain_);
|
||||||
|
ecrt_master_send(master_);
|
||||||
|
|
||||||
|
std::this_thread::sleep_until(next_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthercatMotorBusRuntime::readFeedbackLocked_()
|
||||||
|
{
|
||||||
|
if (!domain_data_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (auto& [motor_id, slave] : slaves_by_motor_id_) {
|
||||||
|
(void)motor_id;
|
||||||
|
for (auto& [key, entry] : slave.pdo_entries) {
|
||||||
|
(void)key;
|
||||||
|
if (!entry.rx) {
|
||||||
|
entry.value = readEntryValue_(domain_data_, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthercatMotorBusRuntime::writeCommandsLocked_()
|
||||||
|
{
|
||||||
|
if (!domain_data_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto& [motor_id, slave] : slaves_by_motor_id_) {
|
||||||
|
(void)motor_id;
|
||||||
|
for (const auto& [key, entry] : slave.pdo_entries) {
|
||||||
|
(void)key;
|
||||||
|
if (entry.rx) {
|
||||||
|
writeEntryValue_(domain_data_, entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthercatMotorBusRuntime::releaseMaster_()
|
||||||
|
{
|
||||||
|
if (master_) {
|
||||||
|
ecrt_release_master(master_);
|
||||||
|
}
|
||||||
|
master_ = nullptr;
|
||||||
|
domain_ = nullptr;
|
||||||
|
domain_data_ = nullptr;
|
||||||
|
for (auto& [motor_id, slave] : slaves_by_motor_id_) {
|
||||||
|
(void)motor_id;
|
||||||
|
slave.slave_config = nullptr;
|
||||||
|
slave.pdo_entries.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::hasValidPdoMapping_() const
|
||||||
|
{
|
||||||
|
return pdo_mapping_.vendor_id != 0 &&
|
||||||
|
pdo_mapping_.product_code != 0 &&
|
||||||
|
!pdo_mapping_.rx_pdos.empty() &&
|
||||||
|
!pdo_mapping_.tx_pdos.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::writePdoRaw_(const int motor_id,
|
||||||
|
const std::uint16_t index,
|
||||||
|
const std::uint8_t subindex,
|
||||||
|
const std::uint8_t bit_len,
|
||||||
|
const std::uint64_t value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
auto slave_it = slaves_by_motor_id_.find(motor_id);
|
||||||
|
if (slave_it == slaves_by_motor_id_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto entry_it = slave_it->second.pdo_entries.find(pdoEntryKey_(index, subindex));
|
||||||
|
if (entry_it == slave_it->second.pdo_entries.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto& entry = entry_it->second;
|
||||||
|
if (!entry.rx || entry.cfg.bit_len != bit_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
entry.value = maskValue_(value, entry.cfg.bit_len);
|
||||||
|
if (domain_data_) {
|
||||||
|
writeEntryValue_(domain_data_, entry);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::readPdoRaw_(const int motor_id,
|
||||||
|
const std::uint16_t index,
|
||||||
|
const std::uint8_t subindex,
|
||||||
|
const std::uint8_t bit_len,
|
||||||
|
std::uint64_t& value) const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||||
|
const auto slave_it = slaves_by_motor_id_.find(motor_id);
|
||||||
|
if (slave_it == slaves_by_motor_id_.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto entry_it = slave_it->second.pdo_entries.find(pdoEntryKey_(index, subindex));
|
||||||
|
if (entry_it == slave_it->second.pdo_entries.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& entry = entry_it->second;
|
||||||
|
if (entry.rx || entry.cfg.bit_len != bit_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = maskValue_(entry.value, entry.cfg.bit_len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::writeSdoRaw_(const int motor_id,
|
||||||
|
const std::uint16_t index,
|
||||||
|
const std::uint8_t subindex,
|
||||||
|
const std::uint8_t bit_len,
|
||||||
|
const std::uint64_t value)
|
||||||
|
{
|
||||||
|
if (!isSupportedBitLength_(bit_len)) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] unsupported SDO write bit length: "
|
||||||
|
<< static_cast<int>(bit_len)
|
||||||
|
<< ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex)
|
||||||
|
<< ", motor_id=" << motor_id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto slave_it = slaves_by_motor_id_.find(motor_id);
|
||||||
|
if (slave_it == slaves_by_motor_id_.end()) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] missing motor for SDO write: "
|
||||||
|
<< motor_id << ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto& slave = slave_it->second;
|
||||||
|
if (!slave.slave_config || !master_) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] runtime is not initialized for SDO write: "
|
||||||
|
<< id_ << ", motor_id=" << motor_id
|
||||||
|
<< ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto raw_value = maskValue_(value, bit_len);
|
||||||
|
if (!started_) {
|
||||||
|
int result = -1;
|
||||||
|
switch (bit_len) {
|
||||||
|
case 8:
|
||||||
|
result = ecrt_slave_config_sdo8(slave.slave_config, index, subindex,
|
||||||
|
static_cast<std::uint8_t>(raw_value));
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
result = ecrt_slave_config_sdo16(slave.slave_config, index, subindex,
|
||||||
|
static_cast<std::uint16_t>(raw_value));
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
result = ecrt_slave_config_sdo32(slave.slave_config, index, subindex,
|
||||||
|
static_cast<std::uint32_t>(raw_value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (result != 0) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to configure startup SDO: "
|
||||||
|
<< "group=" << id_ << ", motor_id=" << motor_id
|
||||||
|
<< ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex)
|
||||||
|
<< ", bit_len=" << static_cast<int>(bit_len)
|
||||||
|
<< ", value=" << raw_value
|
||||||
|
<< ", result=" << result;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<std::uint8_t, 4> data{};
|
||||||
|
switch (bit_len) {
|
||||||
|
case 8:
|
||||||
|
EC_WRITE_U8(data.data(), static_cast<std::uint8_t>(raw_value));
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
EC_WRITE_U16(data.data(), static_cast<std::uint16_t>(raw_value));
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
EC_WRITE_U32(data.data(), static_cast<std::uint32_t>(raw_value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto data_size = static_cast<std::size_t>(bit_len / 8);
|
||||||
|
std::uint32_t abort_code = 0;
|
||||||
|
const int result = ecrt_master_sdo_download(
|
||||||
|
master_,
|
||||||
|
static_cast<std::uint16_t>(slave.cfg.position()),
|
||||||
|
index,
|
||||||
|
subindex,
|
||||||
|
data.data(),
|
||||||
|
data_size,
|
||||||
|
&abort_code);
|
||||||
|
if (result != 0) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to write SDO: "
|
||||||
|
<< "group=" << id_ << ", motor_id=" << motor_id
|
||||||
|
<< ", slave_position=" << slave.cfg.position()
|
||||||
|
<< ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex)
|
||||||
|
<< ", bit_len=" << static_cast<int>(bit_len)
|
||||||
|
<< ", value=" << raw_value
|
||||||
|
<< ", result=" << result
|
||||||
|
<< ", abort_code=" << hexIndex_(abort_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::readSdoRaw_(const int motor_id,
|
||||||
|
const std::uint16_t index,
|
||||||
|
const std::uint8_t subindex,
|
||||||
|
const std::uint8_t bit_len,
|
||||||
|
std::uint64_t& value)
|
||||||
|
{
|
||||||
|
if (!isSupportedBitLength_(bit_len)) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] unsupported SDO read bit length: "
|
||||||
|
<< static_cast<int>(bit_len)
|
||||||
|
<< ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex)
|
||||||
|
<< ", motor_id=" << motor_id;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto slave_it = slaves_by_motor_id_.find(motor_id);
|
||||||
|
if (slave_it == slaves_by_motor_id_.end()) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] missing motor for SDO read: "
|
||||||
|
<< motor_id << ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& slave = slave_it->second;
|
||||||
|
if (!master_) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] runtime is not initialized for SDO read: "
|
||||||
|
<< id_ << ", motor_id=" << motor_id
|
||||||
|
<< ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<std::uint8_t, 4> data{};
|
||||||
|
const auto data_size = static_cast<std::size_t>(bit_len / 8);
|
||||||
|
std::size_t result_size = 0;
|
||||||
|
std::uint32_t abort_code = 0;
|
||||||
|
const int result = ecrt_master_sdo_upload(
|
||||||
|
master_,
|
||||||
|
static_cast<std::uint16_t>(slave.cfg.position()),
|
||||||
|
index,
|
||||||
|
subindex,
|
||||||
|
data.data(),
|
||||||
|
data_size,
|
||||||
|
&result_size,
|
||||||
|
&abort_code);
|
||||||
|
if (result != 0 || result_size != data_size) {
|
||||||
|
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] failed to read SDO: "
|
||||||
|
<< "group=" << id_ << ", motor_id=" << motor_id
|
||||||
|
<< ", slave_position=" << slave.cfg.position()
|
||||||
|
<< ", object=" << hexIndex_(index)
|
||||||
|
<< ":" << static_cast<int>(subindex)
|
||||||
|
<< ", bit_len=" << static_cast<int>(bit_len)
|
||||||
|
<< ", result=" << result
|
||||||
|
<< ", result_size=" << result_size
|
||||||
|
<< ", abort_code=" << hexIndex_(abort_code);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (bit_len) {
|
||||||
|
case 8:
|
||||||
|
value = EC_READ_U8(data.data());
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
value = EC_READ_U16(data.data());
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
value = EC_READ_U32(data.data());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
value = maskValue_(value, bit_len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t EthercatMotorBusRuntime::pdoEntryKey_(const std::uint16_t index,
|
||||||
|
const std::uint8_t subindex)
|
||||||
|
{
|
||||||
|
return (static_cast<std::uint32_t>(index) << 8U) | subindex;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string EthercatMotorBusRuntime::hexIndex_(const std::uint32_t index)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "0x" << std::hex << std::uppercase << index;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t EthercatMotorBusRuntime::maskValue_(const std::uint64_t value,
|
||||||
|
const std::uint8_t bit_len)
|
||||||
|
{
|
||||||
|
switch (bit_len) {
|
||||||
|
case 8:
|
||||||
|
return value & 0xFFU;
|
||||||
|
case 16:
|
||||||
|
return value & 0xFFFFU;
|
||||||
|
case 32:
|
||||||
|
return value & 0xFFFFFFFFULL;
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EthercatMotorBusRuntime::isSupportedBitLength_(const std::uint8_t bit_len)
|
||||||
|
{
|
||||||
|
return bit_len == 8 || bit_len == 16 || bit_len == 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint64_t EthercatMotorBusRuntime::readEntryValue_(const std::uint8_t* domain_data,
|
||||||
|
const PdoEntryRuntime& entry)
|
||||||
|
{
|
||||||
|
switch (entry.cfg.bit_len) {
|
||||||
|
case 8:
|
||||||
|
return EC_READ_U8(domain_data + entry.offset);
|
||||||
|
case 16:
|
||||||
|
return EC_READ_U16(domain_data + entry.offset);
|
||||||
|
case 32:
|
||||||
|
return EC_READ_U32(domain_data + entry.offset);
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EthercatMotorBusRuntime::writeEntryValue_(std::uint8_t* domain_data,
|
||||||
|
const PdoEntryRuntime& entry)
|
||||||
|
{
|
||||||
|
switch (entry.cfg.bit_len) {
|
||||||
|
case 8:
|
||||||
|
EC_WRITE_U8(domain_data + entry.offset, static_cast<std::uint8_t>(entry.value));
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
EC_WRITE_U16(domain_data + entry.offset, static_cast<std::uint16_t>(entry.value));
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
EC_WRITE_U32(domain_data + entry.offset, static_cast<std::uint32_t>(entry.value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace cmvr::device
|
} // namespace cmvr::device
|
||||||
|
|||||||
@ -0,0 +1,91 @@
|
|||||||
|
#include "devices/motor/bus_runtime/ethercat/include/ethercat_motor_bus_runtime.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "cmvr/msgs/cia402.pb.h"
|
||||||
|
#include "devices/motor/drivers/ethercat_motor/include/vendor/eyou/eyou_cia402_pdo_mapping.h"
|
||||||
|
|
||||||
|
namespace cmvr::device {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
config::MotorGroupConfig createSingleSlaveGroup()
|
||||||
|
{
|
||||||
|
config::MotorGroupConfig group;
|
||||||
|
group.set_id("ethercat_real_test");
|
||||||
|
group.set_bus_type(config::MOTOR_BUS_ETHERCAT);
|
||||||
|
group.set_vendor(config::MOTOR_VENDOR_EYOU);
|
||||||
|
group.set_protocol(config::MOTOR_PROTOCOL_ETHERCAT_CIA402);
|
||||||
|
|
||||||
|
auto* ethercat = group.mutable_ethercat();
|
||||||
|
ethercat->set_master_index(0);
|
||||||
|
ethercat->set_cycle_us(1000);
|
||||||
|
|
||||||
|
auto* slave = ethercat->add_slaves();
|
||||||
|
slave->set_motor_id(1);
|
||||||
|
slave->set_alias(0);
|
||||||
|
slave->set_position(0);
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TEST(EthercatMotorBusRuntimeRealTest, InitStartAndReadStatusword)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
EthercatMotorBusRuntime runtime;
|
||||||
|
runtime.setPdoMapping(createEyouCia402PdoMapping());
|
||||||
|
|
||||||
|
ASSERT_TRUE(runtime.init(createSingleSlaveGroup()));
|
||||||
|
EXPECT_EQ(runtime.busType(), config::MOTOR_BUS_ETHERCAT);
|
||||||
|
EXPECT_TRUE(runtime.hasMotor(1));
|
||||||
|
EXPECT_NE(runtime.slaveForMotor(1), nullptr);
|
||||||
|
EXPECT_TRUE(runtime.hasPdoEntry(1, msgs::CIA402_CONTROL_WORD_6040, 0x00));
|
||||||
|
EXPECT_TRUE(runtime.hasPdoEntry(1, msgs::CIA402_STATUS_WORD_6041, 0x00));
|
||||||
|
EXPECT_TRUE(runtime.hasPdoEntry(1, msgs::CIA402_OPERATION_MODE_6060, 0x00));
|
||||||
|
EXPECT_TRUE(runtime.hasPdoEntry(1, msgs::CIA402_MODE_DISPLAY_6061, 0x00));
|
||||||
|
|
||||||
|
const bool started = runtime.start();
|
||||||
|
EXPECT_TRUE(started);
|
||||||
|
if (!started) {
|
||||||
|
runtime.stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_TRUE(runtime.writePdo<std::uint16_t>(1, msgs::CIA402_CONTROL_WORD_6040, 0x00, 0x0000));
|
||||||
|
EXPECT_TRUE(runtime.writePdo<std::int8_t>(1, msgs::CIA402_OPERATION_MODE_6060, 0x00, 0));
|
||||||
|
|
||||||
|
const int settle_ms = 1000;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(settle_ms));
|
||||||
|
|
||||||
|
std::uint16_t statusword = 0;
|
||||||
|
EXPECT_TRUE(runtime.readPdo<std::uint16_t>(1, msgs::CIA402_STATUS_WORD_6041, 0x00, statusword));
|
||||||
|
|
||||||
|
std::int8_t mode_display = 0;
|
||||||
|
EXPECT_TRUE(runtime.readPdo<std::int8_t>(1, msgs::CIA402_MODE_DISPLAY_6061, 0x00, mode_display));
|
||||||
|
|
||||||
|
std::cout << "CIA402 statusword: 0x" << std::hex << statusword
|
||||||
|
<< ", mode display: " << std::dec << static_cast<int>(mode_display)
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
const int hold_ms = 10000;
|
||||||
|
if (hold_ms > 0) {
|
||||||
|
std::cout << "Holding EtherCAT runtime for " << hold_ms
|
||||||
|
<< " ms. Check slave state in another terminal." << std::endl;
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(hold_ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace cmvr::device
|
||||||
BIN
dependency/x86/third_party/ethercat/v1.7.0/bin/ethercat
vendored
Executable file
BIN
dependency/x86/third_party/ethercat/v1.7.0/bin/ethercat
vendored
Executable file
Binary file not shown.
3
dependency/x86/third_party/ethercat/v1.7.0/etc/ethercat.conf
vendored
Normal file
3
dependency/x86/third_party/ethercat/v1.7.0/etc/ethercat.conf
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
MASTER0_DEVICE="a0:ad:9f:c4:c2:2c"
|
||||||
|
DEVICE_MODULES="generic"
|
||||||
|
UPDOWN_INTERFACES="eno1"
|
||||||
113
dependency/x86/third_party/ethercat/v1.7.0/etc/ethercat.conf.bak.20260702_140412
vendored
Normal file
113
dependency/x86/third_party/ethercat/v1.7.0/etc/ethercat.conf.bak.20260702_140412
vendored
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# EtherCAT master configuration file for use with ethercatctl.
|
||||||
|
#
|
||||||
|
# vim: spelllang=en spell tw=78
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#
|
||||||
|
# Main Ethernet devices.
|
||||||
|
#
|
||||||
|
# The MASTER<X>_DEVICE variable specifies the Ethernet device for a master
|
||||||
|
# with index 'X'.
|
||||||
|
#
|
||||||
|
# Specify the MAC address (hexadecimal with colons) of the Ethernet device to
|
||||||
|
# use. Example: "00:00:08:44:ab:66"
|
||||||
|
#
|
||||||
|
# Alternatively, a network interface name can be specified. The interface
|
||||||
|
# name will be resolved to a MAC address using the 'ip' command.
|
||||||
|
# Example: "eth0"
|
||||||
|
#
|
||||||
|
# The broadcast address "ff:ff:ff:ff:ff:ff" has a special meaning: It tells
|
||||||
|
# the master to accept the first device offered by any Ethernet driver.
|
||||||
|
#
|
||||||
|
# The MASTER<X>_DEVICE variables also determine, how many masters will be
|
||||||
|
# created: A non-empty variable MASTER0_DEVICE will create one master, adding
|
||||||
|
# a non-empty variable MASTER1_DEVICE will create a second master, and so on.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# MASTER0_DEVICE="00:00:08:44:ab:66"
|
||||||
|
# MASTER0_DEVICE="eth0"
|
||||||
|
#
|
||||||
|
MASTER0_DEVICE=""
|
||||||
|
#MASTER1_DEVICE=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Backup Ethernet devices
|
||||||
|
#
|
||||||
|
# The MASTER<X>_BACKUP variables specify the devices used for redundancy. They
|
||||||
|
# behaves nearly the same as the MASTER<X>_DEVICE variable, except that it
|
||||||
|
# does not interpret the ff:ff:ff:ff:ff:ff address.
|
||||||
|
#
|
||||||
|
#MASTER0_BACKUP=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ethernet driver modules to use for EtherCAT operation.
|
||||||
|
#
|
||||||
|
# Specify a non-empty list of Ethernet drivers, that shall be used for
|
||||||
|
# EtherCAT operation.
|
||||||
|
#
|
||||||
|
# Except for the generic Ethernet driver module, the init script will try to
|
||||||
|
# unload the usual Ethernet driver modules in the list and replace them with
|
||||||
|
# the EtherCAT-capable ones. If a certain (EtherCAT-capable) driver is not
|
||||||
|
# found, a warning will appear.
|
||||||
|
#
|
||||||
|
# Possible values: 8139too, e100, e1000, e1000e, r8169, generic, ccat, igb,
|
||||||
|
# igc, genet, dwmac-intel, stmmac-pci.
|
||||||
|
# Separate multiple drivers with spaces.
|
||||||
|
# A list of all matching kernel versions can be found here:
|
||||||
|
# https://docs.etherlab.org/ethercat/1.6/doxygen/devicedrivers.html
|
||||||
|
#
|
||||||
|
# Note: The e100, e1000, e1000e, r8169, ccat, igb and igc drivers are not
|
||||||
|
# built by default. Enable them with the --enable-<driver> configure switches.
|
||||||
|
#
|
||||||
|
DEVICE_MODULES=""
|
||||||
|
|
||||||
|
# If you have any issues about network interfaces not being configured
|
||||||
|
# properly, systemd may need some additional infos about your setup.
|
||||||
|
# Have a look at the service file, you'll find some details there.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of interfaces to bring up and down automatically.
|
||||||
|
#
|
||||||
|
# Specify a space-separated list of interface names (such as eth0 or
|
||||||
|
# enp0s1) that shall be brought up on `ethercatctl start` and down on
|
||||||
|
# `ethercatctl stop`.
|
||||||
|
#
|
||||||
|
# When using the generic driver, the corresponding Ethernet device has to be
|
||||||
|
# activated before the master is started, otherwise all frames will time out.
|
||||||
|
# This the perfect use-case for `UPDOWN_INTERFACES`.
|
||||||
|
#
|
||||||
|
UPDOWN_INTERFACES=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default SII caching method.
|
||||||
|
#
|
||||||
|
# Set the start-up caching method for all masters. The integer value
|
||||||
|
# determines, which fields are used to look up a cached SII page. It is a
|
||||||
|
# bit-field consisting of the following flags. A value of zero disables SII
|
||||||
|
# caching (default). A typical value is 7 (use vendor ID, product code and
|
||||||
|
# revision number for lookup).
|
||||||
|
#
|
||||||
|
# - 1: Vendor ID (always used)
|
||||||
|
# - 2: Product code (always used)
|
||||||
|
# - 4: Revision number
|
||||||
|
# - 8: Serial number
|
||||||
|
# - 16: Alias address
|
||||||
|
#
|
||||||
|
# Please keep in mind that in case serial number or alias address is enabled,
|
||||||
|
# only slaves with a non-zero serial number or alias benefit from caching.
|
||||||
|
#
|
||||||
|
SII_CACHING=0
|
||||||
|
|
||||||
|
#
|
||||||
|
# Flags for loading kernel modules.
|
||||||
|
#
|
||||||
|
# This can usually be left empty. Adjust this variable, if you have problems
|
||||||
|
# with module loading.
|
||||||
|
#
|
||||||
|
#MODPROBE_FLAGS="-b"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
3
dependency/x86/third_party/ethercat/v1.7.0/etc/ethercat.conf.bak.20260702_155334
vendored
Normal file
3
dependency/x86/third_party/ethercat/v1.7.0/etc/ethercat.conf.bak.20260702_155334
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
MASTER0_DEVICE="a0:ad:9f:c4:c2:2c"
|
||||||
|
DEVICE_MODULES="generic"
|
||||||
|
UPDOWN_INTERFACES="eno1"
|
||||||
123
dependency/x86/third_party/ethercat/v1.7.0/etc/init.d/ethercat
vendored
Executable file
123
dependency/x86/third_party/ethercat/v1.7.0/etc/init.d/ethercat
vendored
Executable file
@ -0,0 +1,123 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Init script for EtherCAT
|
||||||
|
#
|
||||||
|
# Copyright (C) 2006-2021 Florian Pose, Ingenieurgemeinschaft IgH
|
||||||
|
#
|
||||||
|
# This file is part of the IgH EtherCAT Master.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT Master is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License version 2, as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT Master is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
# Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with the IgH EtherCAT Master; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# vim: expandtab
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
### BEGIN INIT INFO
|
||||||
|
# Provides: ethercat
|
||||||
|
# Required-Start: $local_fs $syslog $network
|
||||||
|
# Should-Start: $time ntp
|
||||||
|
# Required-Stop: $local_fs $syslog $network
|
||||||
|
# Should-Stop: $time ntp
|
||||||
|
# Default-Start: 3 5
|
||||||
|
# Default-Stop: 0 1 2 6
|
||||||
|
# Short-Description: EtherCAT master
|
||||||
|
# Description: EtherCAT master 1.7.0
|
||||||
|
### END INIT INFO
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ETHERCATCTL="/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/sbin/ethercatctl -c /home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/etc/sysconfig/ethercat"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
exit_success() {
|
||||||
|
if [ -r /etc/rc.status ]; then
|
||||||
|
rc_reset
|
||||||
|
rc_status -v
|
||||||
|
rc_exit
|
||||||
|
else
|
||||||
|
echo " done"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
exit_fail() {
|
||||||
|
if [ -r /etc/rc.status ]; then
|
||||||
|
rc_failed
|
||||||
|
rc_status -v
|
||||||
|
rc_exit
|
||||||
|
else
|
||||||
|
echo " failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ -r /etc/rc.status ]; then
|
||||||
|
. /etc/rc.status
|
||||||
|
rc_reset
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "${1}" in
|
||||||
|
|
||||||
|
start)
|
||||||
|
echo -n "Starting EtherCAT master 1.7.0 "
|
||||||
|
|
||||||
|
if $ETHERCATCTL start; then
|
||||||
|
exit_success
|
||||||
|
else
|
||||||
|
exit_fail
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
stop)
|
||||||
|
echo -n "Shutting down EtherCAT master 1.7.0 "
|
||||||
|
|
||||||
|
if $ETHERCATCTL stop; then
|
||||||
|
exit_success
|
||||||
|
else
|
||||||
|
exit_fail
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
restart)
|
||||||
|
$0 stop || exit 1
|
||||||
|
sleep 1
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
|
||||||
|
status)
|
||||||
|
$ETHERCATCTL status
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "USAGE: $0 {start|stop|restart|status}"
|
||||||
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -r /etc/rc.status ]; then
|
||||||
|
rc_exit
|
||||||
|
else
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
113
dependency/x86/third_party/ethercat/v1.7.0/etc/sysconfig/ethercat
vendored
Normal file
113
dependency/x86/third_party/ethercat/v1.7.0/etc/sysconfig/ethercat
vendored
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# EtherCAT master configuration file for use with init.d.
|
||||||
|
#
|
||||||
|
# vim: spelllang=en spell tw=78
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#
|
||||||
|
# Main Ethernet devices.
|
||||||
|
#
|
||||||
|
# The MASTER<X>_DEVICE variable specifies the Ethernet device for a master
|
||||||
|
# with index 'X'.
|
||||||
|
#
|
||||||
|
# Specify the MAC address (hexadecimal with colons) of the Ethernet device to
|
||||||
|
# use. Example: "00:00:08:44:ab:66"
|
||||||
|
#
|
||||||
|
# Alternatively, a network interface name can be specified. The interface
|
||||||
|
# name will be resolved to a MAC address using the 'ip' command.
|
||||||
|
# Example: "eth0"
|
||||||
|
#
|
||||||
|
# The broadcast address "ff:ff:ff:ff:ff:ff" has a special meaning: It tells
|
||||||
|
# the master to accept the first device offered by any Ethernet driver.
|
||||||
|
#
|
||||||
|
# The MASTER<X>_DEVICE variables also determine, how many masters will be
|
||||||
|
# created: A non-empty variable MASTER0_DEVICE will create one master, adding
|
||||||
|
# a non-empty variable MASTER1_DEVICE will create a second master, and so on.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# MASTER0_DEVICE="00:00:08:44:ab:66"
|
||||||
|
# MASTER0_DEVICE="eth0"
|
||||||
|
#
|
||||||
|
MASTER0_DEVICE=""
|
||||||
|
#MASTER1_DEVICE=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Backup Ethernet devices
|
||||||
|
#
|
||||||
|
# The MASTER<X>_BACKUP variables specify the devices used for redundancy. They
|
||||||
|
# behaves nearly the same as the MASTER<X>_DEVICE variable, except that it
|
||||||
|
# does not interpret the ff:ff:ff:ff:ff:ff address.
|
||||||
|
#
|
||||||
|
#MASTER0_BACKUP=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Ethernet driver modules to use for EtherCAT operation.
|
||||||
|
#
|
||||||
|
# Specify a non-empty list of Ethernet drivers, that shall be used for
|
||||||
|
# EtherCAT operation.
|
||||||
|
#
|
||||||
|
# Except for the generic Ethernet driver module, the init script will try to
|
||||||
|
# unload the usual Ethernet driver modules in the list and replace them with
|
||||||
|
# the EtherCAT-capable ones. If a certain (EtherCAT-capable) driver is not
|
||||||
|
# found, a warning will appear.
|
||||||
|
#
|
||||||
|
# Possible values: 8139too, e100, e1000, e1000e, r8169, generic, ccat, igb,
|
||||||
|
# igc, genet, dwmac-intel, stmmac-pci.
|
||||||
|
# Separate multiple drivers with spaces.
|
||||||
|
# A list of all matching kernel versions can be found here:
|
||||||
|
# https://docs.etherlab.org/ethercat/1.6/doxygen/devicedrivers.html
|
||||||
|
#
|
||||||
|
# Note: The e100, e1000, e1000e, r8169, ccat, igb and igc drivers are not
|
||||||
|
# built by default. Enable them with the --enable-<driver> configure switches.
|
||||||
|
#
|
||||||
|
DEVICE_MODULES=""
|
||||||
|
|
||||||
|
# If you have any issues about network interfaces not being configured
|
||||||
|
# properly, systemd may need some additional infos about your setup.
|
||||||
|
# Have a look at the service file, you'll find some details there.
|
||||||
|
#
|
||||||
|
|
||||||
|
#
|
||||||
|
# List of interfaces to bring up and down automatically.
|
||||||
|
#
|
||||||
|
# Specify a space-separated list of interface names (such as eth0 or
|
||||||
|
# enp0s1) that shall be brought up on `ethercatctl start` and down on
|
||||||
|
# `ethercatctl stop`.
|
||||||
|
#
|
||||||
|
# When using the generic driver, the corresponding Ethernet device has to be
|
||||||
|
# activated before the master is started, otherwise all frames will time out.
|
||||||
|
# This the perfect use-case for `UPDOWN_INTERFACES`.
|
||||||
|
#
|
||||||
|
UPDOWN_INTERFACES=""
|
||||||
|
|
||||||
|
#
|
||||||
|
# Default SII caching method.
|
||||||
|
#
|
||||||
|
# Set the start-up caching method for all masters. The integer value
|
||||||
|
# determines, which fields are used to look up a cached SII page. It is a
|
||||||
|
# bit-field consisting of the following flags. A value of zero disables SII
|
||||||
|
# caching (default). A typical value is 7 (use vendor ID, product code and
|
||||||
|
# revision number for lookup).
|
||||||
|
#
|
||||||
|
# - 1: Vendor ID (always used)
|
||||||
|
# - 2: Product code (always used)
|
||||||
|
# - 4: Revision number
|
||||||
|
# - 8: Serial number
|
||||||
|
# - 16: Alias address
|
||||||
|
#
|
||||||
|
# Please keep in mind that in case serial number or alias address is enabled,
|
||||||
|
# only slaves with a non-zero serial number or alias benefit from caching.
|
||||||
|
#
|
||||||
|
SII_CACHING=0
|
||||||
|
|
||||||
|
#
|
||||||
|
# Flags for loading kernel modules.
|
||||||
|
#
|
||||||
|
# This can usually be left empty. Adjust this variable, if you have problems
|
||||||
|
# with module loading.
|
||||||
|
#
|
||||||
|
#MODPROBE_FLAGS="-b"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
3216
dependency/x86/third_party/ethercat/v1.7.0/include/ecrt.h
vendored
Normal file
3216
dependency/x86/third_party/ethercat/v1.7.0/include/ecrt.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
106
dependency/x86/third_party/ethercat/v1.7.0/include/ectty.h
vendored
Normal file
106
dependency/x86/third_party/ethercat/v1.7.0/include/ectty.h
vendored
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
*
|
||||||
|
* Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH
|
||||||
|
*
|
||||||
|
* This file is part of the IgH EtherCAT master userspace library.
|
||||||
|
*
|
||||||
|
* The IgH EtherCAT master userspace library is free software; you can
|
||||||
|
* redistribute it and/or modify it under the terms of the GNU Lesser General
|
||||||
|
* Public License as published by the Free Software Foundation; version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* The IgH EtherCAT master userspace library is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with the IgH EtherCAT master userspace library. If not, see
|
||||||
|
* <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
*
|
||||||
|
* EtherCAT virtual TTY interface.
|
||||||
|
*
|
||||||
|
* \defgroup TTYInterface EtherCAT Virtual TTY Interface
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef __ECTTY_H__
|
||||||
|
#define __ECTTY_H__
|
||||||
|
|
||||||
|
#include <linux/termios.h>
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Data types
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
struct ec_tty;
|
||||||
|
typedef struct ec_tty ec_tty_t; /**< \see ec_tty */
|
||||||
|
|
||||||
|
/** Operations on the virtual TTY interface.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int (*cflag_changed)(void *, tcflag_t); /**< Called when the serial
|
||||||
|
* settings shall be changed. The
|
||||||
|
* \a cflag argument contains the
|
||||||
|
* new settings. */
|
||||||
|
} ec_tty_operations_t;
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Global functions
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/** Create a virtual TTY interface.
|
||||||
|
*
|
||||||
|
* \param ops Set of callbacks.
|
||||||
|
* \param cb_data Arbitrary data, that is passed to any callback.
|
||||||
|
*
|
||||||
|
* \return Pointer to the interface object, otherwise an ERR_PTR value.
|
||||||
|
*/
|
||||||
|
ec_tty_t *ectty_create(
|
||||||
|
const ec_tty_operations_t *ops,
|
||||||
|
void *cb_data
|
||||||
|
);
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* TTY interface methods
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/** Releases a virtual TTY interface.
|
||||||
|
*/
|
||||||
|
void ectty_free(
|
||||||
|
ec_tty_t *tty /**< TTY interface. */
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Reads data to send from the TTY interface.
|
||||||
|
*
|
||||||
|
* If there are data to send, they are copied into the \a buffer. At maximum,
|
||||||
|
* \a size bytes are copied. The actual number of bytes copied is returned.
|
||||||
|
*
|
||||||
|
* \return Number of bytes copied.
|
||||||
|
*/
|
||||||
|
unsigned int ectty_tx_data(
|
||||||
|
ec_tty_t *tty, /**< TTY interface. */
|
||||||
|
uint8_t *buffer, /**< Buffer for data to transmit. */
|
||||||
|
size_t size /**< Available space in \a buffer. */
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Pushes received data to the TTY interface.
|
||||||
|
*/
|
||||||
|
void ectty_rx_data(
|
||||||
|
ec_tty_t *tty, /**< TTY interface. */
|
||||||
|
const uint8_t *buffer, /**< Buffer with received data. */
|
||||||
|
size_t size /**< Number of bytes in \a buffer. */
|
||||||
|
);
|
||||||
|
|
||||||
|
/****************************************************************************/
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#endif
|
||||||
43
dependency/x86/third_party/ethercat/v1.7.0/lib/cmake/ethercat/ethercat-config.cmake
vendored
Normal file
43
dependency/x86/third_party/ethercat/v1.7.0/lib/cmake/ethercat/ethercat-config.cmake
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021 Bjarne von Horn, Ingenieurgemeinschaft IgH
|
||||||
|
#
|
||||||
|
# This file is part of the IgH EtherCAT Master.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT Master is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License version 2, as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT Master is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
# Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with the IgH EtherCAT Master; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# vim: tw=78
|
||||||
|
#
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
find_library(EtherCAT_LIBRARY
|
||||||
|
NAMES ethercat
|
||||||
|
PATHS /home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
find_path(EtherCAT_INCLUDE_DIR
|
||||||
|
NAMES ecrt.h
|
||||||
|
PATHS /home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/include
|
||||||
|
)
|
||||||
|
|
||||||
|
mark_as_advanced(EtherCAT_LIBRARY EtherCAT_INCLUDE_DIR)
|
||||||
|
|
||||||
|
if(NOT TARGET EtherLab::ethercat)
|
||||||
|
add_library(EtherLab::ethercat SHARED IMPORTED)
|
||||||
|
set_target_properties(EtherLab::ethercat PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${EtherCAT_INCLUDE_DIR}"
|
||||||
|
IMPORTED_LOCATION "${EtherCAT_LIBRARY}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
BIN
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.a
vendored
Normal file
BIN
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.a
vendored
Normal file
Binary file not shown.
41
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.la
vendored
Executable file
41
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.la
vendored
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
# libethercat.la - a libtool library file
|
||||||
|
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-15build2
|
||||||
|
#
|
||||||
|
# Please DO NOT delete this file!
|
||||||
|
# It is necessary for linking the library.
|
||||||
|
|
||||||
|
# The name that we can dlopen(3).
|
||||||
|
dlname='libethercat.so.1'
|
||||||
|
|
||||||
|
# Names of this library.
|
||||||
|
library_names='libethercat.so.1.2.0 libethercat.so.1 libethercat.so'
|
||||||
|
|
||||||
|
# The name of the static archive.
|
||||||
|
old_library='libethercat.a'
|
||||||
|
|
||||||
|
# Linker flags that cannot go in dependency_libs.
|
||||||
|
inherited_linker_flags=''
|
||||||
|
|
||||||
|
# Libraries that this one depends upon.
|
||||||
|
dependency_libs=''
|
||||||
|
|
||||||
|
# Names of additional weak libraries provided by this library
|
||||||
|
weak_library_names=''
|
||||||
|
|
||||||
|
# Version information for libethercat.
|
||||||
|
current=3
|
||||||
|
age=2
|
||||||
|
revision=0
|
||||||
|
|
||||||
|
# Is this an already installed library?
|
||||||
|
installed=yes
|
||||||
|
|
||||||
|
# Should we warn about portability when linking against -modules?
|
||||||
|
shouldnotlink=no
|
||||||
|
|
||||||
|
# Files to dlopen/dlpreopen
|
||||||
|
dlopen=''
|
||||||
|
dlpreopen=''
|
||||||
|
|
||||||
|
# Directory that this library needs to be installed in:
|
||||||
|
libdir='/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/lib'
|
||||||
1
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.so
vendored
Symbolic link
1
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.so
vendored
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
libethercat.so.1.2.0
|
||||||
1
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.so.1
vendored
Symbolic link
1
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.so.1
vendored
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
libethercat.so.1.2.0
|
||||||
BIN
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.so.1.2.0
vendored
Executable file
BIN
dependency/x86/third_party/ethercat/v1.7.0/lib/libethercat.so.1.2.0
vendored
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
34
dependency/x86/third_party/ethercat/v1.7.0/lib/pkgconfig/libethercat.pc
vendored
Normal file
34
dependency/x86/third_party/ethercat/v1.7.0/lib/pkgconfig/libethercat.pc
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#
|
||||||
|
# pkgconfig file for ethercat library
|
||||||
|
#
|
||||||
|
# Copyright 2021 Bjarne von Horn (vh at igh dot de)
|
||||||
|
#
|
||||||
|
# This file is part of the ethercat library.
|
||||||
|
#
|
||||||
|
# The ethercat library is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
# option) any later version.
|
||||||
|
#
|
||||||
|
# The ethercat library is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
# License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with the ethercat library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# vim: tw=78 noexpandtab
|
||||||
|
#
|
||||||
|
|
||||||
|
prefix=/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0
|
||||||
|
exec_prefix=${prefix}
|
||||||
|
libdir=/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/lib
|
||||||
|
includedir=/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/include
|
||||||
|
|
||||||
|
Name: libethercat
|
||||||
|
Description: Client support library for the EtherCAT Master
|
||||||
|
URL: http://www.etherlab.org
|
||||||
|
Version: 1.7.0
|
||||||
|
Libs: -L${libdir} -lethercat
|
||||||
|
Cflags: -I${includedir}
|
||||||
38
dependency/x86/third_party/ethercat/v1.7.0/lib/systemd/system/ethercat.service
vendored
Normal file
38
dependency/x86/third_party/ethercat/v1.7.0/lib/systemd/system/ethercat.service
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#
|
||||||
|
# EtherCAT master kernel modules
|
||||||
|
#
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=EtherCAT Master Kernel Modules
|
||||||
|
|
||||||
|
# Fine tuning of the startup dependencies below are recommended
|
||||||
|
# to provide a reliable startup routine.
|
||||||
|
# The dependencies below can be either uncommented after copying
|
||||||
|
# this file to /etc/systemd/system or by creating overrides:
|
||||||
|
# Copy the needed dependencies into
|
||||||
|
# /etc/systemd/system/ethercat.service.d/50-dependencies.conf
|
||||||
|
# in a [Unit] section.
|
||||||
|
|
||||||
|
#
|
||||||
|
# Uncomment this, if the generic Ethernet driver is used. It assures, that the
|
||||||
|
# network interfaces are configured, before the master starts.
|
||||||
|
#
|
||||||
|
#Requires=network.target # Stop master, if network is stopped
|
||||||
|
#After=network.target # Start master, after network is ready
|
||||||
|
|
||||||
|
#
|
||||||
|
# Uncomment this, if a native Ethernet driver is used. It assures, that the
|
||||||
|
# network interfaces are configured, after the Ethernet drivers have been
|
||||||
|
# replaced. Otherwise, the networking configuration tools could be confused.
|
||||||
|
#
|
||||||
|
#Before=network-pre.target
|
||||||
|
#Wants=network-pre.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
|
ExecStart=/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/sbin/ethercatctl start
|
||||||
|
ExecStop=/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/sbin/ethercatctl stop
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
253
dependency/x86/third_party/ethercat/v1.7.0/sbin/ethercatctl
vendored
Executable file
253
dependency/x86/third_party/ethercat/v1.7.0/sbin/ethercatctl
vendored
Executable file
@ -0,0 +1,253 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Start script for EtherCAT to use with systemd
|
||||||
|
#
|
||||||
|
# Copyright (C) 2006-2021 Florian Pose, Ingenieurgemeinschaft IgH
|
||||||
|
#
|
||||||
|
# This file is part of the IgH EtherCAT Master.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT Master is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License version 2, as
|
||||||
|
# published by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT Master is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||||
|
# Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with the IgH EtherCAT Master; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
# vim: expandtab sw=4 tw=78
|
||||||
|
#
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
LSMOD="/sbin/lsmod"
|
||||||
|
MODPROBE="/sbin/modprobe"
|
||||||
|
RMMOD="/sbin/rmmod"
|
||||||
|
MODINFO="/sbin/modinfo"
|
||||||
|
IP="/sbin/ip"
|
||||||
|
|
||||||
|
ETHERCAT="/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/bin/ethercat"
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ "$1" = "-c" ]; then
|
||||||
|
ETHERCAT_CONFIG="$2"
|
||||||
|
COMMAND="$3"
|
||||||
|
else
|
||||||
|
ETHERCAT_CONFIG="/home/lgv/cmvr/0-workspace/cmvr-es/dependency/x86/third_party/ethercat/v1.7.0/etc/ethercat.conf"
|
||||||
|
COMMAND="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
if [ ! -r ${ETHERCAT_CONFIG} ]; then
|
||||||
|
echo ${ETHERCAT_CONFIG} not existing;
|
||||||
|
exit 6
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck source=/etc/ethercat.conf
|
||||||
|
. ${ETHERCAT_CONFIG}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
is_mac_address() {
|
||||||
|
local x='[0-9a-fA-F]'
|
||||||
|
echo "$1" | grep -qE "^($x$x:){5}$x$x\$" -
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
parse_mac_address() {
|
||||||
|
local DEVICENAMETOMAC
|
||||||
|
if [ -z "${1}" ] || is_mac_address "${1}"; then
|
||||||
|
MAC="${1}"
|
||||||
|
else
|
||||||
|
DEVICENAMETOMAC=$("${IP}" address show dev "${1}" |
|
||||||
|
awk '/link\/ether/ { print $2; }')
|
||||||
|
if is_mac_address "${DEVICENAMETOMAC}"; then
|
||||||
|
MAC="${DEVICENAMETOMAC}"
|
||||||
|
else
|
||||||
|
echo Invalid MAC address or interface name \""${1}"\" \
|
||||||
|
in ${ETHERCAT_CONFIG}
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
case "$COMMAND" in
|
||||||
|
|
||||||
|
start)
|
||||||
|
# bring up all updown interfaces before anything else
|
||||||
|
for interface in $UPDOWN_INTERFACES; do
|
||||||
|
$IP link set dev $interface up
|
||||||
|
done
|
||||||
|
|
||||||
|
# construct DEVICES and BACKUPS from configuration variables
|
||||||
|
DEVICES=""
|
||||||
|
BACKUPS=""
|
||||||
|
MASTER_INDEX=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
DEVICE=$(eval echo "\${MASTER${MASTER_INDEX}_DEVICE}")
|
||||||
|
BACKUP=$(eval echo "\${MASTER${MASTER_INDEX}_BACKUP}")
|
||||||
|
if [ -z "${DEVICE}" ]; then break; fi
|
||||||
|
|
||||||
|
if [ ${MASTER_INDEX} -gt 0 ]; then
|
||||||
|
DEVICES=${DEVICES},
|
||||||
|
BACKUPS=${BACKUPS},
|
||||||
|
fi
|
||||||
|
|
||||||
|
parse_mac_address "${DEVICE}"
|
||||||
|
DEVICES=${DEVICES}${MAC}
|
||||||
|
|
||||||
|
parse_mac_address "${BACKUP}"
|
||||||
|
BACKUPS=${BACKUPS}${MAC}
|
||||||
|
|
||||||
|
MASTER_INDEX=$((${MASTER_INDEX} + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "${DEVICES}" ]; then
|
||||||
|
echo "ERROR: No network cards for EtherCAT specified."
|
||||||
|
echo -n "Please edit ${ETHERCAT_CONFIG} with root permissions"
|
||||||
|
echo -n " and set MASTER0_DEVICE variable to either a "
|
||||||
|
echo "network interface name (like eth0) or to a MAC address."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
MODULE_PARAMS=(
|
||||||
|
main_devices="${DEVICES}"
|
||||||
|
backup_devices="${BACKUPS}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ -n "$SII_CACHING" ]; then
|
||||||
|
MODULE_PARAMS+=(sii_caching="$SII_CACHING")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# load master module
|
||||||
|
if ! ${MODPROBE} ${MODPROBE_FLAGS} ec_master "${MODULE_PARAMS[@]}"; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOADED_MODULES=ec_master
|
||||||
|
|
||||||
|
# check for modules to replace
|
||||||
|
for MODULE in ${DEVICE_MODULES}; do
|
||||||
|
ECMODULE=ec_${MODULE}
|
||||||
|
if ! ${MODINFO} "${ECMODULE}" > /dev/null; then
|
||||||
|
continue # ec_* module not found
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${MODULE}" != "generic" ] && [ "${MODULE}" != "ccat" ]; then
|
||||||
|
# unload standard module and check if unloading was successful
|
||||||
|
${RMMOD} "${MODULE}" 2> /dev/null || true
|
||||||
|
if ${LSMOD} | grep "^${MODULE//-/_} " > /dev/null; then
|
||||||
|
# could not unload module
|
||||||
|
${RMMOD} ${LOADED_MODULES}
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ${MODPROBE} ${MODPROBE_FLAGS} "${ECMODULE}"; then
|
||||||
|
if [ "${MODULE}" != "generic" ] && [ "${MODULE}" != "ccat" ]; then
|
||||||
|
${MODPROBE} ${MODPROBE_FLAGS} "${MODULE}" # try to restore
|
||||||
|
fi
|
||||||
|
${RMMOD} ${LOADED_MODULES}
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
LOADED_MODULES="${ECMODULE} ${LOADED_MODULES}"
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
stop)
|
||||||
|
# unload EtherCAT device modules
|
||||||
|
for MODULE in ${DEVICE_MODULES} master; do
|
||||||
|
ECMODULE=ec_${MODULE}
|
||||||
|
if ! ${LSMOD} | grep -q "^${ECMODULE//-/_} "; then
|
||||||
|
continue # ec_* module not loaded
|
||||||
|
fi
|
||||||
|
if ! ${RMMOD} "${ECMODULE}"; then
|
||||||
|
exit 1
|
||||||
|
fi;
|
||||||
|
done
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
|
||||||
|
# load standard modules again
|
||||||
|
for MODULE in ${DEVICE_MODULES}; do
|
||||||
|
if [ "${MODULE}" == "generic" ] || [ "${MODULE}" == "ccat" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
${MODPROBE} ${MODPROBE_FLAGS} "${MODULE}"
|
||||||
|
done
|
||||||
|
|
||||||
|
# bring down all updown interfaces
|
||||||
|
for interface in $UPDOWN_INTERFACES; do
|
||||||
|
$IP link set dev $interface down
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
restart)
|
||||||
|
$0 stop || exit 1
|
||||||
|
sleep 1
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
status)
|
||||||
|
echo "Checking for EtherCAT master 1.7.0 "
|
||||||
|
|
||||||
|
# count masters in configuration file
|
||||||
|
MASTER_COUNT=0
|
||||||
|
while true; do
|
||||||
|
DEVICE=$(eval echo "\${MASTER${MASTER_COUNT}_DEVICE}")
|
||||||
|
if [ -z "${DEVICE}" ]; then break; fi
|
||||||
|
MASTER_COUNT=$((${MASTER_COUNT} + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
RESULT=0
|
||||||
|
|
||||||
|
for i in $(seq 0 "$((${MASTER_COUNT} - 1))"); do
|
||||||
|
echo -n "Master${i} "
|
||||||
|
|
||||||
|
# Check if the master is in idle or operation phase
|
||||||
|
${ETHERCAT} master --master "${i}" 2>/dev/null | \
|
||||||
|
grep -qE 'Phase:[[:space:]]*Idle|Phase:[[:space:]]*Operation'
|
||||||
|
EXITCODE=$?
|
||||||
|
|
||||||
|
if [ ${EXITCODE} -eq 0 ]; then
|
||||||
|
echo " running"
|
||||||
|
else
|
||||||
|
echo " dead"
|
||||||
|
RESULT=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
exit ${RESULT}
|
||||||
|
;;
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo "USAGE: $0 [-c path/to/ethercat.conf] {start|stop|restart|status}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
80
dependency/x86/third_party/ethercat/v1.7.0/share/bash-completion/completions/ethercat
vendored
Normal file
80
dependency/x86/third_party/ethercat/v1.7.0/share/bash-completion/completions/ethercat
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# Copyright (C) 2022 Bjarne von Horn, Ingenieurgemeinschaft IgH
|
||||||
|
#
|
||||||
|
# This file is part of the IgH EtherCAT master userspace library.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT master userspace library is free software; you can
|
||||||
|
# redistribute it and/or modify it under the terms of the GNU Lesser General
|
||||||
|
# Public License as published by the Free Software Foundation; version 2.1
|
||||||
|
# of the License.
|
||||||
|
#
|
||||||
|
# The IgH EtherCAT master userspace library is distributed in the hope that
|
||||||
|
# it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with the IgH EtherCAT master userspace library. If not, see
|
||||||
|
# <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
_ethercat_completions()
|
||||||
|
{
|
||||||
|
local ethercat_commands="alias config crc cstruct data debug domains download eoe foe_read foe_write graph master pdos reg_read reg_write rescan sdos sii_read sii_write slaves soe_read soe_write states upload version xml"
|
||||||
|
local options="--help --force --quiet --verbose --master "
|
||||||
|
if [ "$COMP_CWORD" -eq 1 ] ; then
|
||||||
|
COMPREPLY=($(compgen -W "$ethercat_commands --help" -- "${COMP_WORDS[1]}"))
|
||||||
|
elif [[ "${COMP_WORDS[1]}" != "--help" && ! "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-a|-p|--alias|--position$ ]] ; then
|
||||||
|
case "${COMP_WORDS[1]}" in
|
||||||
|
"alias" | "config" | "cstruct" | "slaves" | "sdos" | "sii_read" | "upload" | "xml")
|
||||||
|
options+="--alias --position"
|
||||||
|
;;
|
||||||
|
"crc")
|
||||||
|
options+="reset"
|
||||||
|
;;
|
||||||
|
"debug")
|
||||||
|
options+="0 1 2"
|
||||||
|
;;
|
||||||
|
"domains")
|
||||||
|
options+="--domain"
|
||||||
|
;;
|
||||||
|
"download" | "reg_read" | "soe_read" | "soe_write")
|
||||||
|
if [[ "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-t|--type$ ]] ; then
|
||||||
|
options="bool int8 int16 int32 int64 uint8 uint16 uint32 uint64 float double string octet_string unicode_string sm8 sm16 sm32 sm64"
|
||||||
|
else
|
||||||
|
options+="--alias --position --type"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"foe_read" | "foe_write")
|
||||||
|
if [[ "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-o|--output-file$ ]] ; then
|
||||||
|
COMPREPLY=($(compgen -o filenames -A file -- "${COMP_WORDS[$COMP_CWORD]}"))
|
||||||
|
else
|
||||||
|
options+="--alias --position --output-file"
|
||||||
|
COMPREPLY=($(compgen -o filenames -A file -W "$options" -- "${COMP_WORDS[$COMP_CWORD]}"))
|
||||||
|
fi
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
"graph")
|
||||||
|
options+="DC CRC"
|
||||||
|
;;
|
||||||
|
"pdos")
|
||||||
|
if [[ "${COMP_WORDS[COMP_CWORD-1]}" =~ ^-s|--skin$ ]] ; then
|
||||||
|
options="default etherlab"
|
||||||
|
else
|
||||||
|
options+="--alias --position --skin"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"sii_write")
|
||||||
|
options+="--alias --position"
|
||||||
|
COMPREPLY=($(compgen -o filenames -A file -W "$options" -- "${COMP_WORDS[$COMP_CWORD]}"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
|
"states")
|
||||||
|
options+="--alias --position INIT PREOP BOOT SAFEOP OP"
|
||||||
|
;;
|
||||||
|
|
||||||
|
esac
|
||||||
|
COMPREPLY+=($(compgen -W "$options" -- "${COMP_WORDS[$COMP_CWORD]}"))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _ethercat_completions ethercat
|
||||||
Loading…
Reference in New Issue
Block a user