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
|
||||
)
|
||||
|
||||
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
|
||||
PUBLIC
|
||||
@ -12,9 +23,28 @@ target_link_libraries(motor_bus_runtime
|
||||
cmvr_es::device::motor_core
|
||||
cmvr_es::mujoco_world
|
||||
PRIVATE
|
||||
ethercat
|
||||
cmvr_es::device::canbus
|
||||
glog
|
||||
)
|
||||
|
||||
add_library(cmvr_es::device::motor_bus_runtime ALIAS motor_bus_runtime)
|
||||
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
|
||||
#define CMVR_ES_ETHERCAT_MOTOR_BUS_RUNTIME_H
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#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 {
|
||||
|
||||
@ -17,12 +29,130 @@ public:
|
||||
|
||||
const std::string& id() const { return id_; }
|
||||
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;
|
||||
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:
|
||||
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_;
|
||||
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};
|
||||
};
|
||||
|
||||
|
||||
@ -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 <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#include "common/base/logging/logger.h"
|
||||
|
||||
#include <ecrt.h>
|
||||
|
||||
namespace cmvr::device {
|
||||
|
||||
bool EthercatMotorBusRuntime::init(const config::MotorGroupConfig& group_cfg)
|
||||
@ -21,14 +31,15 @@ bool EthercatMotorBusRuntime::init(const config::MotorGroupConfig& group_cfg)
|
||||
}
|
||||
|
||||
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) {
|
||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] cycle_us must be positive: " << id_;
|
||||
return false;
|
||||
}
|
||||
if (!hasValidPdoMapping_()) {
|
||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] PDO mapping is not configured: "
|
||||
<< id_;
|
||||
return false;
|
||||
}
|
||||
|
||||
slaves_by_motor_id_.clear();
|
||||
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_;
|
||||
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) {
|
||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] duplicate slave motor_id: "
|
||||
<< slave.motor_id() << " in group: " << id_;
|
||||
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;
|
||||
}
|
||||
|
||||
void EthercatMotorBusRuntime::setPdoMapping(EthercatPdoMapping mapping)
|
||||
{
|
||||
pdo_mapping_ = std::move(mapping);
|
||||
}
|
||||
|
||||
bool EthercatMotorBusRuntime::start()
|
||||
{
|
||||
if (started_) {
|
||||
return true;
|
||||
}
|
||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] EtherCAT master is not implemented yet: " << id_;
|
||||
return false;
|
||||
if (!initialized_ || !master_ || !domain_) {
|
||||
CMVR_LOG(ERROR) << "[EthercatMotorBusRuntime] runtime is not initialized: " << id_;
|
||||
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()
|
||||
{
|
||||
running_.store(false);
|
||||
if (cyclic_thread_.joinable()) {
|
||||
cyclic_thread_.join();
|
||||
}
|
||||
started_ = false;
|
||||
initialized_ = false;
|
||||
domain_data_ = nullptr;
|
||||
releaseMaster_();
|
||||
}
|
||||
|
||||
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()) {
|
||||
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
|
||||
|
||||
@ -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