2020-01-25 17:38:22 +01:00
|
|
|
/** Node type: Ethercat
|
|
|
|
*
|
|
|
|
* @author Niklas Eiling <niklas.eiling@eonerc.rwth-aachen.de>
|
2022-12-14 17:41:58 +01:00
|
|
|
* @author Steffen Vogel <post@steffenvogel.de>
|
2020-01-25 17:38:22 +01:00
|
|
|
* @author Divya Laxetti <divya.laxetti@rwth-aachen.de>
|
|
|
|
* @copyright 2018-2020, Institute for Automation of Complex Power Systems, EONERC
|
2022-07-04 18:20:03 +02:00
|
|
|
* @license Apache 2.0
|
2020-01-25 17:38:22 +01:00
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
#include <villas/super_node.hpp>
|
|
|
|
#include <villas/utils.hpp>
|
|
|
|
#include <villas/exceptions.hpp>
|
2021-08-10 10:12:48 -04:00
|
|
|
#include <villas/node_compat.hpp>
|
2020-01-25 17:38:22 +01:00
|
|
|
#include <villas/nodes/ethercat.hpp>
|
|
|
|
|
|
|
|
using namespace villas;
|
2021-05-10 00:12:30 +02:00
|
|
|
using namespace villas::node;
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Forward declartions */
|
2021-08-10 10:12:48 -04:00
|
|
|
static NodeCompatType p;
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Constants */
|
|
|
|
#define NSEC_PER_SEC (1000000000)
|
|
|
|
#define FREQUENCY (NSEC_PER_SEC / PERIOD_NS)
|
|
|
|
|
|
|
|
/* Global state and config */
|
|
|
|
int master_id = 0;
|
|
|
|
int alias = 0;
|
|
|
|
|
|
|
|
static ec_master_t *master = nullptr;
|
|
|
|
|
|
|
|
struct coupler {
|
|
|
|
int position;
|
|
|
|
int vendor_id;
|
|
|
|
int product_code;
|
|
|
|
|
|
|
|
ec_slave_config_t *sc;
|
|
|
|
} coupler = {
|
|
|
|
.position = 0,
|
|
|
|
.vendor_id = ETHERCAT_VID_BECKHOFF,
|
|
|
|
.product_code = ETHERCAT_PID_EK1100,
|
|
|
|
.sc = nullptr
|
|
|
|
};
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static
|
|
|
|
void ethercat_cyclic_task(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2020-09-10 11:11:42 +02:00
|
|
|
int ret;
|
2021-08-10 10:12:48 -04:00
|
|
|
struct Sample *smp;
|
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
while (true) {
|
2020-03-04 13:06:28 +01:00
|
|
|
w->task.wait();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Receive process data */
|
|
|
|
ecrt_master_receive(master);
|
|
|
|
ecrt_domain_process(w->domain);
|
|
|
|
|
|
|
|
/* Receive process data */
|
|
|
|
smp = sample_alloc(&w->pool);
|
|
|
|
if (!smp) {
|
2021-02-16 14:15:14 +01:00
|
|
|
n->logger->warn("Pool underrun");
|
2020-01-25 17:38:22 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
smp->length = MIN(w->in.num_channels, smp->capacity);
|
|
|
|
smp->flags = (int) SampleFlags::HAS_DATA;
|
2021-08-10 10:12:48 -04:00
|
|
|
smp->signals = n->getInputSignals(false);
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Read process data */
|
|
|
|
for (unsigned i = 0; i < smp->length; i++) {
|
|
|
|
int16_t ain_value = EC_READ_S16(w->domain_pd + w->in.offsets[i]);
|
|
|
|
|
|
|
|
smp->data[i].f = w->in.range * (float) ain_value / INT16_MAX;
|
|
|
|
}
|
|
|
|
|
2020-09-10 11:11:42 +02:00
|
|
|
ret = queue_signalled_push(&w->queue, smp);
|
|
|
|
if (ret)
|
2021-02-16 14:15:14 +01:00
|
|
|
n->logger->warn("Failed to enqueue samples");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Write process data */
|
|
|
|
smp = w->send.exchange(nullptr);
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < w->out.num_channels; i++) {
|
|
|
|
int16_t aout_value = (smp->data[i].f / w->out.range) * INT16_MAX;
|
|
|
|
|
|
|
|
EC_WRITE_S16(w->domain_pd + w->out.offsets[i], aout_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
sample_decref(smp);
|
|
|
|
|
|
|
|
// send process data
|
|
|
|
ecrt_domain_queue(w->domain);
|
|
|
|
ecrt_master_send(master);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_type_start(villas::node::SuperNode *sn)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
json_error_t err;
|
|
|
|
|
2022-12-24 15:02:05 +01:00
|
|
|
if (sn == nullptr)
|
|
|
|
throw RuntimeError("EtherCAT node-type requires super-node");
|
|
|
|
|
2021-02-16 14:15:14 +01:00
|
|
|
json_t *json = sn->getConfig();
|
|
|
|
if (json) {
|
|
|
|
ret = json_unpack_ex(json, &err, 0, "{ s?: i, s?:i, s?: { s?: { s?: i, s?: i, s?: i } } }",
|
2020-01-25 17:38:22 +01:00
|
|
|
"ethernet",
|
|
|
|
"master", &master_id,
|
|
|
|
"alias", &alias,
|
|
|
|
"coupler",
|
|
|
|
"position", &coupler.position,
|
|
|
|
"product_code", &coupler.product_code,
|
|
|
|
"vendor_id", &coupler.vendor_id
|
|
|
|
);
|
|
|
|
if (ret)
|
2021-02-16 14:15:14 +01:00
|
|
|
throw ConfigError(json, err, "node-config-node-ethercat");
|
2020-01-25 17:38:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
master = ecrt_request_master(master_id);
|
|
|
|
if (!master)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Create configuration for bus coupler */
|
|
|
|
coupler.sc = ecrt_master_slave_config(master, alias, coupler.position, coupler.vendor_id, coupler.product_code);
|
|
|
|
if (!coupler.sc)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_type_stop()
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-02-16 14:15:14 +01:00
|
|
|
auto logger = logging.get("node:ethercat");
|
|
|
|
|
|
|
|
logger->info("Releasing EtherCAT master");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
ecrt_release_master(master);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_parse(NodeCompat *n, json_t *json)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
int ret;
|
|
|
|
json_error_t err;
|
|
|
|
|
2021-02-16 14:15:14 +01:00
|
|
|
ret = json_unpack_ex(json, &err, 0, "{ s?: F, s?: { s: i, s?: F, s?: i, s?: i, s?: i }, s?: { s: i, s?: F, s?: i, s?: i, s?: i } }",
|
2020-01-25 17:38:22 +01:00
|
|
|
"rate", &w->rate,
|
|
|
|
"out",
|
|
|
|
"num_channels", &w->out.num_channels,
|
|
|
|
"range", &w->out.range,
|
|
|
|
"position", &w->out.position,
|
|
|
|
"product_code", &w->out.product_code,
|
|
|
|
"vendor_id", &w->out.vendor_id,
|
|
|
|
"in",
|
|
|
|
"num_channels", &w->in.num_channels,
|
|
|
|
"range", &w->in.range,
|
|
|
|
"position", &w->in.position,
|
|
|
|
"product_code", &w->in.product_code,
|
|
|
|
"vendor_id", &w->in.vendor_id
|
|
|
|
);
|
|
|
|
if (ret)
|
2021-02-16 14:15:14 +01:00
|
|
|
throw ConfigError(json, err, "node-config-node-ethercat");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
char * villas::node::ethercat_print(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
std::stringstream ss;
|
|
|
|
|
|
|
|
ss << "alias=" << alias;
|
|
|
|
ss << ", in.range=" << w->in.range << ", in.num_channels=" << w->in.num_channels;
|
|
|
|
ss << ", out.range=" << w->out.range << ", out.num_channels=" << w->out.num_channels;
|
|
|
|
|
|
|
|
return strdup(ss.str().c_str());
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_check(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Some parts of the configuration are still hard-coded for this specific setup */
|
|
|
|
if (w->in.product_code != ETHERCAT_PID_EL3008 ||
|
|
|
|
w->in.vendor_id != ETHERCAT_VID_BECKHOFF ||
|
|
|
|
w->out.product_code != ETHERCAT_PID_EL4038 ||
|
|
|
|
w->out.vendor_id != ETHERCAT_VID_BECKHOFF ||
|
|
|
|
coupler.product_code != ETHERCAT_PID_EK1100 ||
|
|
|
|
coupler.vendor_id != ETHERCAT_VID_BECKHOFF
|
|
|
|
)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_prepare(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
int ret;
|
|
|
|
auto *w = n->getData<struct ethercat>();
|
|
|
|
|
|
|
|
ret = pool_init(&w->pool, DEFAULT_ETHERCAT_QUEUE_LENGTH, SAMPLE_LENGTH(n->getInputSignals(false)->size()));
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = queue_signalled_init(&w->queue, DEFAULT_ETHERCAT_QUEUE_LENGTH);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
w->in.offsets = new unsigned[w->in.num_channels];
|
|
|
|
if (!w->in.offsets)
|
2020-07-04 16:22:10 +02:00
|
|
|
throw MemoryAllocationError();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
w->out.offsets = new unsigned[w->out.num_channels];
|
|
|
|
if (!w->out.offsets)
|
2020-07-04 16:22:10 +02:00
|
|
|
throw MemoryAllocationError();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
w->domain_regs = new ec_pdo_entry_reg_t[w->in.num_channels + w->out.num_channels + 1];
|
|
|
|
if (!w->domain_regs)
|
2020-07-04 16:22:10 +02:00
|
|
|
throw MemoryAllocationError();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
memset(w->domain_regs, 0, (w->in.num_channels + w->out.num_channels + 1) * sizeof(ec_pdo_entry_reg_t));
|
|
|
|
|
|
|
|
/* Prepare list of domain registers */
|
|
|
|
int o = 0;
|
|
|
|
for (unsigned i = 0; i < w->out.num_channels; i++) {
|
|
|
|
w->out.offsets[i] = 0;
|
|
|
|
|
|
|
|
w->domain_regs[o].alias = alias;
|
|
|
|
w->domain_regs[o].position = w->out.position;
|
|
|
|
w->domain_regs[o].vendor_id = w->out.vendor_id;
|
|
|
|
w->domain_regs[o].product_code = w->out.product_code;
|
|
|
|
w->domain_regs[o].index = 0x7000 + i * 0x10;
|
|
|
|
w->domain_regs[o].subindex = 0x1;
|
|
|
|
w->domain_regs[o].offset = w->out.offsets + i;
|
|
|
|
|
|
|
|
o++;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Prepare list of domain registers */
|
|
|
|
for (unsigned i = 0; i < w->in.num_channels; i++) {
|
|
|
|
w->in.offsets[i] = 0;
|
|
|
|
|
|
|
|
w->domain_regs[o].alias = alias;
|
|
|
|
w->domain_regs[o].position = w->in.position;
|
|
|
|
w->domain_regs[o].vendor_id = w->in.vendor_id;
|
|
|
|
w->domain_regs[o].product_code = w->in.product_code;
|
|
|
|
w->domain_regs[o].index = 0x6000 + i * 0x10;
|
|
|
|
w->domain_regs[o].subindex = 0x11;
|
|
|
|
w->domain_regs[o].offset = w->in.offsets + i;
|
|
|
|
|
|
|
|
o++;
|
|
|
|
};
|
|
|
|
|
|
|
|
w->domain = ecrt_master_create_domain(master);
|
|
|
|
if (!w->domain)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_start(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
|
|
|
int ret;
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Configure analog in */
|
|
|
|
w->in.sc = ecrt_master_slave_config(master, alias, w->in.position, w->in.vendor_id, w->in.product_code);
|
2021-02-16 14:15:14 +01:00
|
|
|
if (!w->in.sc)
|
|
|
|
throw RuntimeError("Failed to get slave configuration.");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
ret = ecrt_slave_config_pdos(w->in.sc, EC_END, slave_4_syncs);
|
2021-02-16 14:15:14 +01:00
|
|
|
if (ret)
|
|
|
|
throw RuntimeError("Failed to configure PDOs.");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Configure analog out */
|
|
|
|
w->out.sc = ecrt_master_slave_config(master, alias, w->out.position, w->out.vendor_id, w->out.product_code);
|
2021-02-16 14:15:14 +01:00
|
|
|
if (!w->out.sc)
|
|
|
|
throw RuntimeError("Failed to get slave configuration.");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
ret = ecrt_slave_config_pdos(w->out.sc, EC_END, slave_3_syncs);
|
2021-02-16 14:15:14 +01:00
|
|
|
if (ret)
|
|
|
|
throw RuntimeError("Failed to configure PDOs.");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
ret = ecrt_domain_reg_pdo_entry_list(w->domain, w->domain_regs);
|
2021-02-16 14:15:14 +01:00
|
|
|
if (ret)
|
|
|
|
throw RuntimeError("PDO entry registration failed!");
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/** @todo Check that master is not already active... */
|
|
|
|
ret = ecrt_master_activate(master);
|
|
|
|
if (ret)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
w->domain_pd = ecrt_domain_data(w->domain);
|
|
|
|
if (!w->domain_pd)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* Start cyclic timer */
|
2020-03-04 13:06:28 +01:00
|
|
|
w->task.setRate(w->rate);
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Start cyclic task */
|
|
|
|
w->thread = std::thread(ethercat_cyclic_task, n);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_stop(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
int ret;
|
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
w->thread.join();
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
ret = queue_signalled_close(&w->queue);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-01-25 17:38:22 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_read(NodeCompat *n, struct Sample * const smps[], unsigned cnt)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
int avail;
|
2021-08-10 10:12:48 -04:00
|
|
|
struct Sample *cpys[cnt];
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
avail = queue_signalled_pull_many(&w->queue, (void **) cpys, cnt);
|
|
|
|
if (avail < 0)
|
2021-02-16 14:15:14 +01:00
|
|
|
n->logger->warn("Pool underrun: avail={}", avail);
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
sample_copy_many(smps, cpys, avail);
|
|
|
|
sample_decref_many(cpys, avail);
|
|
|
|
|
|
|
|
return avail;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_write(NodeCompat *n, struct Sample * const smps[], unsigned cnt)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
if (cnt < 1)
|
|
|
|
return cnt;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
struct Sample *smp = smps[0];
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
sample_incref(smp);
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
struct Sample *old = w->send.exchange(smp);
|
2020-01-25 17:38:22 +01:00
|
|
|
if (old)
|
|
|
|
sample_decref(old);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_init(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
/* Default values */
|
|
|
|
w->rate = 1000;
|
|
|
|
|
|
|
|
w->in.num_channels = 8;
|
|
|
|
w->in.range = 10.0;
|
|
|
|
w->in.position = 2;
|
|
|
|
w->in.product_code = ETHERCAT_PID_EL3008;
|
|
|
|
w->in.vendor_id = ETHERCAT_VID_BECKHOFF;
|
|
|
|
w->in.sc = nullptr;
|
|
|
|
w->in.offsets = nullptr;
|
|
|
|
|
|
|
|
w->out.num_channels = 8;
|
|
|
|
w->out.range = 10.0;
|
|
|
|
w->out.position = 1;
|
|
|
|
w->out.product_code = ETHERCAT_PID_EL4038;
|
|
|
|
w->out.vendor_id = ETHERCAT_VID_BECKHOFF;
|
|
|
|
w->out.sc = nullptr;
|
|
|
|
w->out.offsets = nullptr;
|
|
|
|
|
|
|
|
w->domain = nullptr;
|
|
|
|
w->domain_pd = nullptr;
|
|
|
|
w->domain_regs = nullptr;
|
|
|
|
|
|
|
|
/* Placement new for C++ objects */
|
2021-08-10 10:12:48 -04:00
|
|
|
new (&w->send) std::atomic<struct Sample *>();
|
2020-01-25 17:38:22 +01:00
|
|
|
new (&w->thread) std::thread();
|
2020-03-04 13:06:28 +01:00
|
|
|
new (&w->task) Task(CLOCK_REALTIME);
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_destroy(NodeCompat *n)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
|
|
|
int ret;
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
if (w->domain_regs)
|
|
|
|
delete[] w->domain_regs;
|
|
|
|
|
|
|
|
if (w->in.offsets)
|
|
|
|
delete[] w->in.offsets;
|
|
|
|
|
|
|
|
if (w->out.offsets)
|
|
|
|
delete[] w->out.offsets;
|
|
|
|
|
|
|
|
ret = queue_signalled_destroy(&w->queue);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = pool_destroy(&w->pool);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2020-03-04 13:06:28 +01:00
|
|
|
w->task.~Task();
|
|
|
|
|
2020-01-25 17:38:22 +01:00
|
|
|
/** @todo Destroy domain? */
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
int villas::node::ethercat_poll_fds(NodeCompat *n, int *fds)
|
2020-01-25 17:38:22 +01:00
|
|
|
{
|
2021-08-10 10:12:48 -04:00
|
|
|
auto *w = n->getData<struct ethercat>();
|
2020-01-25 17:38:22 +01:00
|
|
|
|
|
|
|
fds[0] = queue_signalled_fd(&w->queue);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
__attribute__((constructor(110)))
|
|
|
|
static void register_plugin() {
|
2021-06-21 16:11:42 -04:00
|
|
|
p.name = "ethercat";
|
|
|
|
p.description = "Send and receive samples of an ethercat connection";
|
|
|
|
p.vectorize = 1; /* we only process a single sample per call */
|
|
|
|
p.size = sizeof(struct ethercat);
|
|
|
|
p.type.start = ethercat_type_start;
|
|
|
|
p.type.stop = ethercat_type_stop;
|
|
|
|
p.parse = ethercat_parse;
|
|
|
|
p.print = ethercat_print;
|
|
|
|
p.check = ethercat_check;
|
|
|
|
p.init = ethercat_init;
|
|
|
|
p.destroy = ethercat_destroy;
|
|
|
|
p.prepare = ethercat_prepare;
|
|
|
|
p.start = ethercat_start;
|
|
|
|
p.stop = ethercat_stop;
|
|
|
|
p.read = ethercat_read;
|
|
|
|
p.write = ethercat_write;
|
|
|
|
p.poll_fds = ethercat_poll_fds;
|
|
|
|
|
2021-08-10 10:12:48 -04:00
|
|
|
static NodeCompatFactory ncp(&p);
|
2020-01-25 17:38:22 +01:00
|
|
|
}
|
|
|
|
|