/** \file * \brief Example code for Simple Open EtherCAT master * * Usage : eni_test [ifname1] * ifname is NIC interface, f.e. eth0 * * This is a minimal test. * * (c)Arthur Ketels 2010 - 2011 */ #include #include #include #include "soem/soem.h" #define EC_TIMEOUTMON 500 static uint8 IOmap[4096]; static OSAL_THREAD_HANDLE thread1; static int expectedWKC; static boolean needlf; static volatile int wkc; static boolean inOP; static uint8 currentgroup = 0; extern ec_enit ec_eni; static ecx_contextt ctx = { .ENI = &ec_eni, }; void eni_test(char *ifname) { int i, j, oloop, iloop, chk; needlf = FALSE; inOP = FALSE; printf("Starting eni test\n"); /* initialise SOEM, bind socket to ifname */ if (ecx_init(&ctx, ifname)) { printf("ecx_init on %s succeeded.\n", ifname); /* find and auto-config slaves */ if (ecx_config_init(&ctx) > 0) { ec_groupt *group = &ctx.grouplist[0]; ecx_config_map_group(&ctx, IOmap, 0); ecx_configdc(&ctx); printf("%d slaves found and configured.\n", ctx.slavecount); /* wait for all slaves to reach SAFE_OP state */ ecx_statecheck(&ctx, 0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 4); oloop = group->Obytes; if (oloop > 8) oloop = 8; iloop = group->Ibytes; if (iloop > 8) iloop = 8; printf("segments : %d : %d %d %d %d\n", group->nsegments, group->IOsegment[0], group->IOsegment[1], group->IOsegment[2], group->IOsegment[3]); printf("Request operational state for all slaves\n"); expectedWKC = (group->outputsWKC * 2) + group->inputsWKC; printf("Calculated workcounter %d\n", expectedWKC); ctx.slavelist[0].state = EC_STATE_OPERATIONAL; /* send one valid process data to make outputs in slaves happy*/ ecx_send_processdata(&ctx); ecx_receive_processdata(&ctx, EC_TIMEOUTRET); /* request OP state for all slaves */ ecx_writestate(&ctx, 0); chk = 200; /* wait for all slaves to reach OP state */ do { ecx_send_processdata(&ctx); ecx_receive_processdata(&ctx, EC_TIMEOUTRET); ecx_statecheck(&ctx, 0, EC_STATE_OPERATIONAL, 50000); } while (chk-- && (ctx.slavelist[0].state != EC_STATE_OPERATIONAL)); if (ctx.slavelist[0].state == EC_STATE_OPERATIONAL) { printf("Operational state reached for all slaves.\n"); inOP = TRUE; /* cyclic loop */ for (i = 1; i <= 10000; i++) { ecx_send_processdata(&ctx); wkc = ecx_receive_processdata(&ctx, EC_TIMEOUTRET); if (wkc >= expectedWKC) { printf("Processdata cycle %4d, WKC %d , O:", i, wkc); for (j = 0; j < oloop; j++) { printf(" %2.2x", *(ctx.slavelist[0].outputs + j)); } printf(" I:"); for (j = 0; j < iloop; j++) { printf(" %2.2x", *(ctx.slavelist[0].inputs + j)); } printf(" T:%" PRId64 "\r", ctx.DCtime); needlf = TRUE; } osal_usleep(5000); } inOP = FALSE; } else { printf("Not all slaves reached operational state.\n"); ecx_readstate(&ctx); for (i = 1; i <= ctx.slavecount; i++) { ec_slavet *slave = &ctx.slavelist[i]; if (slave->state != EC_STATE_OPERATIONAL) { printf("Slave %d State=0x%2.2x StatusCode=0x%4.4x : %s\n", i, slave->state, slave->ALstatuscode, ec_ALstatuscode2string(slave->ALstatuscode)); } } } printf("\nRequest init state for all slaves\n"); ctx.slavelist[0].state = EC_STATE_INIT; /* request INIT state for all slaves */ ecx_writestate(&ctx, 0); } else { printf("No slaves found!\n"); } printf("End eni test, close socket\n"); /* stop SOEM, close socket */ ecx_close(&ctx); } else { printf("No socket connection on %s\nExecute as root\n", ifname); } } OSAL_THREAD_FUNC ecatcheck(void *ptr) { int slaveix; (void)ptr; /* Not used */ while (1) { if (inOP && ((wkc < expectedWKC) || ctx.grouplist[currentgroup].docheckstate)) { printf("check wkc=%d/%d docheckstate=%d!\n", wkc, expectedWKC, ctx.grouplist[currentgroup].docheckstate); if (needlf) { needlf = FALSE; printf("\n"); } /* one or more slaves are not responding */ ctx.grouplist[currentgroup].docheckstate = FALSE; ecx_readstate(&ctx); for (slaveix = 1; slaveix <= ctx.slavecount; slaveix++) { ec_slavet *slave = &ctx.slavelist[slaveix]; if ((slave->group == currentgroup) && (slave->state != EC_STATE_OPERATIONAL)) { ctx.grouplist[currentgroup].docheckstate = TRUE; if (slave->state == (EC_STATE_SAFE_OP + EC_STATE_ERROR)) { printf("ERROR : slave %d is in SAFE_OP + ERROR, attempting ack.\n", slaveix); slave->state = (EC_STATE_SAFE_OP + EC_STATE_ACK); ecx_writestate(&ctx, slaveix); } else if (slave->state == EC_STATE_SAFE_OP) { printf("WARNING : slave %d is in SAFE_OP, change to OPERATIONAL.\n", slaveix); slave->state = EC_STATE_OPERATIONAL; if (slave->mbxhandlerstate == ECT_MBXH_LOST) slave->mbxhandlerstate = ECT_MBXH_CYCLIC; ecx_writestate(&ctx, slaveix); } else if (slave->state > EC_STATE_NONE) { if (ecx_reconfig_slave(&ctx, slaveix, EC_TIMEOUTMON) >= EC_STATE_PRE_OP) { slave->islost = FALSE; printf("MESSAGE : slave %d reconfigured\n", slaveix); } } else if (!slave->islost) { /* re-check state */ ecx_statecheck(&ctx, slaveix, EC_STATE_OPERATIONAL, EC_TIMEOUTRET); if (slave->state == EC_STATE_NONE) { slave->islost = TRUE; slave->mbxhandlerstate = ECT_MBXH_LOST; /* zero input data for this slave */ if (slave->Ibytes) { memset(slave->inputs, 0x00, slave->Ibytes); printf("zero inputs %p %d\n\r", slave->inputs, slave->Ibytes); } printf("ERROR : slave %d lost\n", slaveix); } } } if (slave->islost) { if (slave->state <= EC_STATE_INIT) { if (ecx_recover_slave(&ctx, slaveix, EC_TIMEOUTMON)) { slave->islost = FALSE; printf("MESSAGE : slave %d recovered\n", slaveix); } } else { slave->islost = FALSE; printf("MESSAGE : slave %d found\n", slaveix); } } } if (!ctx.grouplist[currentgroup].docheckstate) printf("OK : all slaves resumed OPERATIONAL.\n"); } osal_usleep(10000); } } int main(int argc, char *argv[]) { printf("SOEM (Simple Open EtherCAT Master)\nENI test\n"); if (argc > 1) { /* create thread to handle slave error handling in OP */ osal_thread_create(&thread1, 128000, &ecatcheck, NULL); /* start cyclic part */ eni_test(argv[1]); } else { ec_adaptert *adapter = NULL; ec_adaptert *head = NULL; printf("Usage: eni_test ifname1\nifname = eth0 for example\n"); printf("\nAvailable adapters:\n"); head = adapter = ec_find_adapters(); while (adapter != NULL) { printf(" - %s (%s)\n", adapter->name, adapter->desc); adapter = adapter->next; } ec_free_adapters(head); } printf("End program\n"); return (0); }