Thank you Jun, and yes, I owe you a cake!<br><br>To complete this discussion, I'd like to tell the developers how the patch was tested.<br><br>Since Jun's patch about 6~7 months ago, we have been using EtherCAT in our tests using his patch, with the cycle he described. With real data, we have had 2 slaves, ~60 PDOs and ~700 entries, with which we didn't have any problem. The only case where it was unclear what the behavior of the master would be was the following:<br>
<br>Imagine a domain that is so big, it requires multiple packets to be sent and received to read all data from the network. It was unknown what would happen if `ecrt_master_receive` is called while some packets have arrived and others not. To test this, we created the following setup:<br>
<br>2 slaves, each producing 1.2k data, split equally in 2 datagrams. A
bridge placed in the network that introduces a delay between the 2
datagrams. The delays I tested with were 100us, 500us and 1ms. That is,
the bridge makes sure there is at least 1ms delay between passing
frames. I logged the passing times and this is done correctly.<br><br>In this test, using `ecrt_domain_received`,
I could see that it returns true after the correct time. That is, when I
increase the gap in the bridge, the receive time in the network is
increased. With another program I checked the data received from the
slaves and they are correct (both datagrams arrive).<br><br>The only problem I found was that `ecrt_domain_received` should **not** be called after `ecrt_domain_process` (otherwise, there would be a kernel crash).<br><br>
Once again, thank you Jun and I certainly hope the developers of the EtherCAT master would include this patch in the next version.<br>Shahbaz<br><div class="gmail_quote"><br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Date: Mon, 2 Jul 2012 17:43:24 +0200<br>
From: Jun Yuan <<a href="mailto:j.yuan@rtleaders.com">j.yuan@rtleaders.com</a>><br>
Subject: Re: [etherlab-users] Waiting for network receive?<br>
To: <a href="mailto:etherlab-users@etherlab.org">etherlab-users@etherlab.org</a><br>
Message-ID:<br>
<<a href="mailto:CAB0kLgunW_iST0z0U51wWRwZUMJcbO2KVcHBABuGofB1Uks8qw@mail.gmail.com">CAB0kLgunW_iST0z0U51wWRwZUMJcbO2KVcHBABuGofB1Uks8qw@mail.gmail.com</a>><br>
Content-Type: text/plain; charset="utf-8"<br>
<br>
Hi Shahbaz,<br>
<br>
I was just about to delete myself from the mailing list today, then I<br>
saw your email. Thanks for the introduction, Shahbaz. Now I know you<br>
owe me a cake :P<br>
<br>
I didn't send it to the EtherLab developers, because I don't know if<br>
they've already understood the need. So let me express the problem<br>
again. The original master loop is like the following.<br>
<br>
while (true) {<br>
ecrt_master_receive(master); // check my mailbox<br>
ecrt_domain_process(domain); // check those replies from<br>
yesterday, cry if not all the replies have been received<br>
<br>
// read replies, write new mails<br>
<br>
ecrt_domain_queue(domain); // drop them to the sending box<br>
ecrt_master_send(master); // send them out<br>
wait_for_next_cycle(); // call it a day<br>
}<br>
<br>
The problem is that, the replies may already lie in our mailbox long<br>
before the next day. Some may argue that we can make the day shorter<br>
(decrease the cycle time). Well, we cannot always do that. Some slaves<br>
need a certain cycle time.<br>
<br>
And there is another drawback in the original loop: the<br>
ecrt_master_send() comes after the data processing, which's duration<br>
may vary. This would cause jitter in datagram sending interval, which<br>
causes further problem in some realtime application. We should have<br>
the sending happens before our data processing.<br>
<br>
Since our mailman won't ring the doorbell when he brings mails to us<br>
(There's no interrupt in EtherLab Master, so forget about it), we need<br>
to check our mailbox again and again to see whether new mails have<br>
been arrived.<br>
<br>
while (true) {<br>
// if you have DC sync, do it here<br>
ecrt_master_send(master); // send my mails out<br>
<br>
do {<br>
sleep(a while); // take a nap<br>
ecrt_master_receive(master); // check the mailbox<br>
} while (!ecrt_domain_received(domain)); // until all the replys has<br>
been received<br>
<br>
ecrt_domain_process(domain); // check those replies<br>
<br>
// read replies, write new mails<br>
<br>
ecrt_domain_queue(domain); // drop them to the sending box<br>
wait_for_next_cycle(); // call it a day<br>
}<br>
<br>
<br>
This solution is not ideal, but that's what we can do if we are eager<br>
to read the replies ASAP. An ideal solution would be, if we know how<br>
long does it take to get replies, we just sleep for that long and wake<br>
up, hurra, all the replies have just been arrived. But how can we<br>
determine that time?<br>
<br>
The implementation the function ecrt_domain_received(domain) is quite easy.<br>
<br>
int ecrt_domain_received(const ec_domain_t *domain)<br>
{<br>
ec_datagram_t *datagram;<br>
<br>
list_for_each_entry(datagram, &domain->datagrams, list) {<br>
if (datagram->state != EC_DATAGRAM_RECEIVED) {<br>
return 0;<br>
}<br>
}<br>
return 1;<br>
}<br>
<br>
Please find enclosed the patch for 2292:0f7a243b03e4@stable-1.5. The<br>
patch works for userspace too.<br>
<br>
diff -r 0f7a243b03e4 include/ecrt.h<br>
--- a/include/ecrt.h Mon Jan 30 15:33:54 2012 +0100<br>
+++ b/include/ecrt.h Mon Jul 02 16:37:08 2012 +0200<br>
@@ -1346,6 +1346,16 @@<br>
ec_domain_t *domain /**< Domain. */<br>
);<br>
<br>
+/** Check if all the datagrams of the domain have been received.<br>
+ *<br>
+ * \retval 1 on received.<br>
+ * \retval 0 not received.<br>
+ * \retval <0 Error code.<br>
+ */<br>
+int ecrt_domain_received(<br>
+ const ec_domain_t *domain /**< Domain. */<br>
+ );<br>
+<br>
/** Determines the states of the domain's datagrams.<br>
*<br>
* Evaluates the working counters of the received datagrams and outputs<br>
diff -r 0f7a243b03e4 lib/domain.c<br>
--- a/lib/domain.c Mon Jan 30 15:33:54 2012 +0100<br>
+++ b/lib/domain.c Mon Jul 02 16:37:08 2012 +0200<br>
@@ -99,6 +99,20 @@<br>
<br>
/*****************************************************************************/<br>
<br>
+int ecrt_domain_received(const ec_domain_t *domain)<br>
+{<br>
+ int received = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_RECEIVED,<br>
+ domain->index);<br>
+<br>
+ if (received < 0) {<br>
+ fprintf(stderr, "Failed to process domain: %s\n", strerror(errno));<br>
+ }<br>
+<br>
+ return received;<br>
+}<br>
+<br>
+/*****************************************************************************/<br>
+<br>
void ecrt_domain_process(ec_domain_t *domain)<br>
{<br>
if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_PROCESS,<br>
diff -r 0f7a243b03e4 master/cdev.c<br>
--- a/master/cdev.c Mon Jan 30 15:33:54 2012 +0100<br>
+++ b/master/cdev.c Mon Jul 02 16:37:08 2012 +0200<br>
@@ -2522,6 +2522,31 @@<br>
<br>
/*****************************************************************************/<br>
<br>
+/** Check domain's datagrams have received.<br>
+ */<br>
+int ec_cdev_ioctl_domain_received(<br>
+ ec_master_t *master, /**< EtherCAT master. */<br>
+ unsigned long arg, /**< ioctl() argument. */<br>
+ ec_cdev_priv_t *priv /**< Private data structure of file handle. */<br>
+ )<br>
+{<br>
+ const ec_domain_t *domain;<br>
+<br>
+ if (unlikely(!priv->requested))<br>
+ return -EPERM;<br>
+<br>
+ /* no locking of master_sem needed, because domain will not be deleted in<br>
+ * the meantime. */<br>
+<br>
+ if (!(domain = ec_master_find_domain(master, arg))) {<br>
+ return -ENOENT;<br>
+ }<br>
+<br>
+ return ecrt_domain_received(domain);<br>
+}<br>
+<br>
+/*****************************************************************************/<br>
+<br>
/** Process the domain.<br>
*/<br>
int ec_cdev_ioctl_domain_process(<br>
@@ -3750,6 +3775,9 @@<br>
case EC_IOCTL_DOMAIN_OFFSET:<br>
ret = ec_cdev_ioctl_domain_offset(master, arg, priv);<br>
break;<br>
+ case EC_IOCTL_DOMAIN_RECEIVED:<br>
+ ret = ec_cdev_ioctl_domain_received(master, arg, priv);<br>
+ break;<br>
case EC_IOCTL_DOMAIN_PROCESS:<br>
if (!(filp->f_mode & FMODE_WRITE)) {<br>
ret = -EPERM;<br>
diff -r 0f7a243b03e4 master/domain.c<br>
--- a/master/domain.c Mon Jan 30 15:33:54 2012 +0100<br>
+++ b/master/domain.c Mon Jul 02 16:37:08 2012 +0200<br>
@@ -396,6 +396,20 @@<br>
<br>
/*****************************************************************************/<br>
<br>
+int ecrt_domain_received(const ec_domain_t *domain)<br>
+{<br>
+ ec_datagram_t *datagram;<br>
+<br>
+ list_for_each_entry(datagram, &domain->datagrams, list) {<br>
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {<br>
+ return 0;<br>
+ }<br>
+ }<br>
+ return 1;<br>
+}<br>
+<br>
+/*****************************************************************************/<br>
+<br>
void ecrt_domain_process(ec_domain_t *domain)<br>
{<br>
uint16_t working_counter_sum;<br>
@@ -467,6 +481,7 @@<br>
EXPORT_SYMBOL(ecrt_domain_size);<br>
EXPORT_SYMBOL(ecrt_domain_external_memory);<br>
EXPORT_SYMBOL(ecrt_domain_data);<br>
+EXPORT_SYMBOL(ecrt_domain_received);<br>
EXPORT_SYMBOL(ecrt_domain_process);<br>
EXPORT_SYMBOL(ecrt_domain_queue);<br>
EXPORT_SYMBOL(ecrt_domain_state);<br>
diff -r 0f7a243b03e4 master/ioctl.h<br>
--- a/master/ioctl.h Mon Jan 30 15:33:54 2012 +0100<br>
+++ b/master/ioctl.h Mon Jul 02 16:37:08 2012 +0200<br>
@@ -137,6 +137,7 @@<br>
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x47, ec_ioctl_voe_t)<br>
#define EC_IOCTL_VOE_DATA EC_IOWR(0x48, ec_ioctl_voe_t)<br>
#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x49, size_t)<br>
+#define EC_IOCTL_DOMAIN_RECEIVED EC_IO(0x50)<br>
<br>
/*****************************************************************************/<br>
</blockquote></div><br>