Skip to content

Commit a50ed4b

Browse files
committed
Add simple_ng test program
This is a rewrite of `simple_test` with the following major changes: * portable codebase, so no splitting between different OS; * no threads: they are not needed for such simple task; * use newer APIs (i.e., ecx_... functions).
1 parent d548e99 commit a50ed4b

File tree

5 files changed

+324
-2
lines changed

5 files changed

+324
-2
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ install(FILES
9797
DESTINATION ${SOEM_INCLUDE_INSTALL_DIR})
9898

9999
if(BUILD_TESTS)
100+
add_subdirectory(test/simple_ng)
100101
add_subdirectory(test/linux/slaveinfo)
101102
add_subdirectory(test/linux/eepromtool)
102103
add_subdirectory(test/linux/simple_test)

Doxyfile

+1
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ WARN_LOGFILE =
668668
INPUT = doc/tutorial.txt \
669669
doc/soem.dox \
670670
soem \
671+
test/simple_ng \
671672
test/linux/ebox \
672673
test/linux/eepromtool \
673674
test/linux/red_test \

doc/soem.dox

+2-2
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@
127127
*
128128
* \section start Getting started
129129
*
130-
* For examples see simple_test.c in ~/test/linux/simple_test.
131-
* First try (assume EtherCAT on eth0): sudo ./simple_test eth0
130+
* For examples see simple_ng.c in ~/test/simple_ng.
131+
* First try (assume EtherCAT on eth0): sudo ./simple_ng eth0
132132
* As SOEM uses RAW sockets it will need to run as root.
133133
*
134134
* \section bugs Squashed bugs

test/simple_ng/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
set(SOURCES simple_ng.c)
2+
add_executable(simple_ng ${SOURCES})
3+
target_link_libraries(simple_ng soem)
4+
install(TARGETS simple_ng DESTINATION bin)

test/simple_ng/simple_ng.c

+316
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
/** \file
2+
* \brief Example code for Simple Open EtherCAT master
3+
*
4+
* Usage: simple_ng IFNAME1
5+
* IFNAME1 is the NIC interface name, e.g. 'eth0'
6+
*
7+
* This is a minimal test.
8+
*/
9+
10+
#include "ethercat.h"
11+
#include <stdio.h>
12+
#include <stdlib.h>
13+
#include <string.h>
14+
15+
16+
typedef struct {
17+
ecx_contextt context;
18+
char * iface;
19+
uint8 group;
20+
int roundtrip_time;
21+
22+
/* Used by the context */
23+
uint8 map[4096];
24+
ecx_portt port;
25+
ec_slavet slavelist[EC_MAXSLAVE];
26+
int slavecount;
27+
ec_groupt grouplist[EC_MAXGROUP];
28+
uint8 esibuf[EC_MAXEEPBUF];
29+
uint32 esimap[EC_MAXEEPBITMAP];
30+
ec_eringt elist;
31+
ec_idxstackT idxstack;
32+
boolean ecaterror;
33+
int64 DCtime;
34+
ec_SMcommtypet SMcommtype[EC_MAX_MAPT];
35+
ec_PDOassignt PDOassign[EC_MAX_MAPT];
36+
ec_PDOdesct PDOdesc[EC_MAX_MAPT];
37+
ec_eepromSMt eepSM;
38+
ec_eepromFMMUt eepFMMU;
39+
} Fieldbus;
40+
41+
42+
static void
43+
fieldbus_initialize(Fieldbus *fieldbus, char *iface)
44+
{
45+
ecx_contextt *context;
46+
47+
/* Let's start by 0-filling `fieldbus` to avoid surprises */
48+
memset(fieldbus, 0, sizeof(*fieldbus));
49+
50+
fieldbus->iface = iface;
51+
fieldbus->group = 0;
52+
fieldbus->roundtrip_time = 0;
53+
fieldbus->ecaterror = FALSE;
54+
55+
/* Initialize the ecx_contextt data structure */
56+
context = &fieldbus->context;
57+
context->port = &fieldbus->port;
58+
context->slavelist = fieldbus->slavelist;
59+
context->slavecount = &fieldbus->slavecount;
60+
context->maxslave = EC_MAXSLAVE;
61+
context->grouplist = fieldbus->grouplist;
62+
context->maxgroup = EC_MAXGROUP;
63+
context->esibuf = fieldbus->esibuf;
64+
context->esimap = fieldbus->esimap;
65+
context->esislave = 0;
66+
context->elist = &fieldbus->elist;
67+
context->idxstack = &fieldbus->idxstack;
68+
context->ecaterror = &fieldbus->ecaterror;
69+
context->DCtime = &fieldbus->DCtime;
70+
context->SMcommtype = fieldbus->SMcommtype;
71+
context->PDOassign = fieldbus->PDOassign;
72+
context->PDOdesc = fieldbus->PDOdesc;
73+
context->eepSM = &fieldbus->eepSM;
74+
context->eepFMMU = &fieldbus->eepFMMU;
75+
context->FOEhook = NULL;
76+
context->EOEhook = NULL;
77+
context->manualstatechange = 0;
78+
}
79+
80+
static int
81+
fieldbus_roundtrip(Fieldbus *fieldbus)
82+
{
83+
ecx_contextt *context;
84+
ec_timet start, end, diff;
85+
int wkc;
86+
87+
context = &fieldbus->context;
88+
89+
start = osal_current_time();
90+
ecx_send_processdata(context);
91+
wkc = ecx_receive_processdata(context, EC_TIMEOUTRET);
92+
end = osal_current_time();
93+
osal_time_diff(&start, &end, &diff);
94+
fieldbus->roundtrip_time = diff.sec * 1000000 + diff.usec;
95+
96+
return wkc;
97+
}
98+
99+
static boolean
100+
fieldbus_start(Fieldbus *fieldbus)
101+
{
102+
ecx_contextt *context;
103+
ec_groupt *grp;
104+
ec_slavet *slave;
105+
int i;
106+
107+
context = &fieldbus->context;
108+
grp = fieldbus->grouplist + fieldbus->group;
109+
110+
printf("Initializing SOEM on '%s'... ", fieldbus->iface);
111+
if (! ecx_init(context, fieldbus->iface)) {
112+
printf("no socket connection\n");
113+
return FALSE;
114+
}
115+
printf("done\n");
116+
117+
printf("Finding autoconfig slaves... ");
118+
if (ecx_config_init(context, FALSE) <= 0) {
119+
printf("no slaves found\n");
120+
return FALSE;
121+
}
122+
printf("%d slaves found\n", fieldbus->slavecount);
123+
124+
printf("Sequential mapping of I/O... ");
125+
ecx_config_map_group(context, fieldbus->map, fieldbus->group);
126+
printf("mapped %dO+%dI bytes from %d segments",
127+
grp->Obytes, grp->Ibytes, grp->nsegments);
128+
if (grp->nsegments > 1) {
129+
/* Show how slaves are distrubuted */
130+
for (i = 0; i < grp->nsegments; ++i) {
131+
printf("%s%d", i == 0 ? " (" : "+", grp->IOsegment[i]);
132+
}
133+
printf(" slaves)");
134+
}
135+
printf("\n");
136+
137+
printf("Configuring distributed clock... ");
138+
ecx_configdc(context);
139+
printf("done\n");
140+
141+
printf("Waiting for all slaves in safe operational... ");
142+
ecx_statecheck(context, 0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4);
143+
printf("done\n");
144+
145+
printf("Send a roundtrip to make outputs in slaves happy... ");
146+
fieldbus_roundtrip(fieldbus);
147+
printf("done\n");
148+
149+
printf("Setting operational state..");
150+
/* Act on slave 0 (a virtual slave used for broadcasting) */
151+
slave = fieldbus->slavelist;
152+
slave->state = EC_STATE_OPERATIONAL;
153+
ecx_writestate(context, 0);
154+
/* Poll the result ten times before giving up */
155+
for (i = 0; i < 10; ++i) {
156+
printf(".");
157+
fieldbus_roundtrip(fieldbus);
158+
ecx_statecheck(context, 0, EC_STATE_OPERATIONAL, EC_TIMEOUTSTATE / 10);
159+
if (slave->state == EC_STATE_OPERATIONAL) {
160+
printf(" all slaves are now operational\n");
161+
return TRUE;
162+
}
163+
}
164+
165+
printf(" failed,");
166+
ecx_readstate(context);
167+
for (i = 1; i <= fieldbus->slavecount; ++i) {
168+
slave = fieldbus->slavelist + i;
169+
if (slave->state != EC_STATE_OPERATIONAL) {
170+
printf(" slave %d is 0x%04X (AL-status=0x%04X %s)",
171+
i, slave->state, slave->ALstatuscode,
172+
ec_ALstatuscode2string(slave->ALstatuscode));
173+
}
174+
}
175+
printf("\n");
176+
177+
return FALSE;
178+
}
179+
180+
static void
181+
fieldbus_stop(Fieldbus *fieldbus)
182+
{
183+
ecx_contextt *context;
184+
ec_slavet *slave;
185+
186+
context = &fieldbus->context;
187+
/* Act on slave 0 (a virtual slave used for broadcasting) */
188+
slave = fieldbus->slavelist;
189+
190+
printf("Requesting init state on all slaves... ");
191+
slave->state = EC_STATE_INIT;
192+
ecx_writestate(context, 0);
193+
printf("done\n");
194+
195+
printf("Close socket... ");
196+
ecx_close(context);
197+
printf("done\n");
198+
}
199+
200+
static boolean
201+
fieldbus_dump(Fieldbus *fieldbus)
202+
{
203+
ec_groupt *grp;
204+
uint32 n;
205+
int wkc, expected_wkc;
206+
207+
grp = fieldbus->grouplist + fieldbus->group;
208+
209+
wkc = fieldbus_roundtrip(fieldbus);
210+
expected_wkc = grp->outputsWKC * 2 + grp->inputsWKC;
211+
printf("%6d usec WKC %d", fieldbus->roundtrip_time, wkc);
212+
if (wkc < expected_wkc) {
213+
printf(" wrong (expected %d)\n", expected_wkc);
214+
return FALSE;
215+
}
216+
217+
printf(" O:");
218+
for (n = 0; n < grp->Obytes; ++n) {
219+
printf(" %02X", grp->outputs[n]);
220+
}
221+
printf(" I:");
222+
for (n = 0; n < grp->Ibytes; ++n) {
223+
printf(" %02X", grp->inputs[n]);
224+
}
225+
printf(" T: %lld\r", (long long) fieldbus->DCtime);
226+
return TRUE;
227+
}
228+
229+
static void
230+
fieldbus_check_state(Fieldbus *fieldbus)
231+
{
232+
ecx_contextt *context;
233+
ec_groupt *grp;
234+
ec_slavet *slave;
235+
int i;
236+
237+
context = &fieldbus->context;
238+
grp = context->grouplist + fieldbus->group;
239+
grp->docheckstate = FALSE;
240+
ecx_readstate(context);
241+
for (i = 1; i <= fieldbus->slavecount; ++i) {
242+
slave = context->slavelist + i;
243+
if (slave->group != fieldbus->group) {
244+
/* This slave is part of another group: do nothing */
245+
} else if (slave->state != EC_STATE_OPERATIONAL) {
246+
grp->docheckstate = TRUE;
247+
if (slave->state == EC_STATE_SAFE_OP + EC_STATE_ERROR) {
248+
printf("* Slave %d is in SAFE_OP+ERROR, attempting ACK\n", i);
249+
slave->state = EC_STATE_SAFE_OP + EC_STATE_ACK;
250+
ecx_writestate(context, i);
251+
} else if(slave->state == EC_STATE_SAFE_OP) {
252+
printf("* Slave %d is in SAFE_OP, change to OPERATIONAL\n", i);
253+
slave->state = EC_STATE_OPERATIONAL;
254+
ecx_writestate(context, i);
255+
} else if(slave->state > EC_STATE_NONE) {
256+
if (ecx_reconfig_slave(context, i, EC_TIMEOUTRET)) {
257+
slave->islost = FALSE;
258+
printf("* Slave %d reconfigured\n", i);
259+
}
260+
} else if(! slave->islost) {
261+
ecx_statecheck(context, i, EC_STATE_OPERATIONAL, EC_TIMEOUTRET);
262+
if (slave->state == EC_STATE_NONE) {
263+
slave->islost = TRUE;
264+
printf("* Slave %d lost\n", i);
265+
}
266+
}
267+
} else if (slave->islost) {
268+
if(slave->state != EC_STATE_NONE) {
269+
slave->islost = FALSE;
270+
printf("* Slave %d found\n", i);
271+
} else if (ecx_recover_slave(context, i, EC_TIMEOUTRET)) {
272+
slave->islost = FALSE;
273+
printf("* Slave %d recovered\n", i);
274+
}
275+
}
276+
}
277+
278+
if (! grp->docheckstate) {
279+
printf("All slaves resumed OPERATIONAL\n");
280+
}
281+
}
282+
283+
int
284+
main(int argc, char *argv[])
285+
{
286+
Fieldbus fieldbus;
287+
288+
if (argc != 2) {
289+
printf("Usage: simple_ng IFNAME1\n"
290+
"IFNAME1 is the NIC interface name, e.g. 'eth0'\n");
291+
return 1;
292+
}
293+
294+
fieldbus_initialize(&fieldbus, argv[1]);
295+
if (fieldbus_start(&fieldbus)) {
296+
int i, min_time, max_time;
297+
min_time = max_time = 0;
298+
for (i = 1; i <= 10000; ++i) {
299+
printf("Iteration %4d:", i);
300+
if (! fieldbus_dump(&fieldbus)) {
301+
fieldbus_check_state(&fieldbus);
302+
} else if (i == 1) {
303+
min_time = max_time = fieldbus.roundtrip_time;
304+
} else if (fieldbus.roundtrip_time < min_time) {
305+
min_time = fieldbus.roundtrip_time;
306+
} else if (fieldbus.roundtrip_time > max_time) {
307+
max_time = fieldbus.roundtrip_time;
308+
}
309+
osal_usleep(5000);
310+
}
311+
printf("\nRoundtrip time (usec): min %d max %d\n", min_time, max_time);
312+
fieldbus_stop(&fieldbus);
313+
}
314+
315+
return 0;
316+
}

0 commit comments

Comments
 (0)