#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <csignal>
#include <cstdint>
#include <iostream>
#include <limits>
#include <string>
#include <thread>
#include <vector>
#include <lxmaster/lxmaster.hpp>
static std::atomic<bool> g_interrupted{false};
static void onSignal(int) { g_interrupted = true; }
static void installSignalHandler() {
struct sigaction sa{};
sa.sa_handler = onSignal;
sigemptyset(&sa.sa_mask);
sigaction(SIGINT, &sa, nullptr);
sigaction(SIGTERM, &sa, nullptr);
}
constexpr std::int32_t kAmplitudeCountsPerSec = 100000;
constexpr double kFrequencyHz = 0.5;
constexpr int kCycles = 5;
constexpr double kTwoPi = 6.283185307179586476925286766559;
static double elapsedS(std::chrono::steady_clock::time_point now,
std::chrono::steady_clock::time_point start) {
return std::chrono::duration<double>(now - start).count();
}
static std::int32_t clampI32(double v) {
return static_cast<std::int32_t>(std::max(
static_cast<double>(std::numeric_limits<std::int32_t>::min()),
std::min(static_cast<double>(std::numeric_limits<std::int32_t>::max()), v)));
}
static std::int32_t velocityAt(std::chrono::steady_clock::time_point now,
std::chrono::steady_clock::time_point start) {
const double omega_t = kTwoPi * kFrequencyHz * elapsedS(now, start);
return clampI32(static_cast<double>(kAmplitudeCountsPerSec) * std::sin(omega_t));
}
int main() {
installSignalHandler();
lxmaster::NetworkConfig cfg = lxmaster::NetworkConfig::defaults();
if (cfg.bus.ifname.empty()) {
std::cerr << "No EtherCAT interface: set LXMASTER_RT_IFACE in /etc/profile.d/lxmaster-config.sh "
"(run lxmaster host setup first).\n";
return 1;
}
cfg.eni.eni_path = "/home/user/myenifolder/myenifile.xml";
lxmaster::EcNetwork net(cfg);
if (!net.prepare()) {
std::cerr << "EcNetwork::prepare() failed: " << net.lastError() << "\n";
return 1;
}
std::vector<lxmaster::Axis*> axes = net.axes();
if (axes.empty()) {
std::cerr << "ENI produced no motion axes to command.\n";
return 1;
}
for (lxmaster::Axis* ax : axes) {
ax->setDriveMode(lxmaster::DriveOpMode::Csv);
ax->configure();
}
lxmaster::Axis* drive = axes.front();
if (!net.start()) {
std::cerr << "EcNetwork::start() failed: " << net.lastError() << "\n";
return 1;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
const double period_s = 1.0 / kFrequencyHz;
const double total_s = period_s * static_cast<double>(kCycles);
std::cout << "Operational. CSV velocity sine profile:\n"
<< " amplitude (cnt/s) = " << kAmplitudeCountsPerSec << "\n"
<< " frequency (Hz) = " << kFrequencyHz << "\n"
<< " cycles = " << kCycles
<< " -> duration " << total_s << " s\n"
<< " cycle period = " << net.cycleTimeNs() << " ns (from ENI)\n";
drive->moveAtVelocity(0);
const auto update_period = std::chrono::nanoseconds(net.cycleTimeNs());
std::this_thread::sleep_for(update_period);
const auto start = std::chrono::steady_clock::now();
const auto deadline = start + std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::duration<double>(total_s));
while (net.isRunning() && !g_interrupted) {
const auto now = std::chrono::steady_clock::now();
if (now >= deadline) break;
drive->moveAtVelocity(velocityAt(now, start));
std::this_thread::sleep_for(update_period);
}
const bool stopped_early = !net.isRunning();
if (!stopped_early) {
drive->moveAtVelocity(0);
std::this_thread::sleep_for(update_period);
}
net.stop();
if (stopped_early) {
std::cerr << '\n';
net.reportDeviceStatus(std::cerr);
const std::string reason = net.lastError();
std::cerr << "\nRun ended early (velocity sine profile incomplete).\n";
if (!reason.empty()) {
std::cerr << " Reason: " << reason << "\n";
} else {
std::cerr << " Reason: (none reported)\n";
}
if (drive->isFaulted()) {
std::cerr << " Fault: axis '" << drive->name() << "' faulted (statusword=0x" << std::hex
<< drive->statusword() << std::dec
<< "); see the device status and [cia402-fault] blocks above.\n";
}
return 2;
}
std::cout << "\n";
net.reportDeviceStatus(std::cout);
std::cout << "Done.\n";
return 0;
}