SOEM/soem/ecat.c

655 lines
18 KiB
C

#include "ecat.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
ecat_net_t * ecat_net_init(char * ifname)
{
/* allocate memory */
ecat_net_t * net = calloc (1, sizeof(ecat_net_t));
if (!net)
{
printf ("[%s] Could not allocate memory for ecat_net_t\n", ifname);
return NULL;
}
/* TODO: fix */
net->ifname = strdup (ifname);
net->ctx.port = calloc (1, sizeof(ecx_portt));
net->ctx.slavelist = calloc (1, sizeof(ec_slavet) * EC_MAXSLAVE);
net->ctx.slavecount = calloc (1, sizeof(int));
net->ctx.grouplist = calloc (1, sizeof(ec_groupt) * EC_MAXGROUP);
net->ctx.esibuf = calloc (1, sizeof(uint8) * EC_MAXEEPBUF);
net->ctx.esimap = calloc (1, sizeof(uint32) * EC_MAXEEPBITMAP);
net->ctx.elist = calloc (1, sizeof(ec_eringt));
net->ctx.idxstack = calloc (1, sizeof(ec_idxstackT));
net->ctx.ecaterror = calloc (1, sizeof(boolean));
net->ctx.DCtime = calloc (1, sizeof(int64));
net->ctx.SMcommtype = calloc (1, sizeof(ec_SMcommtypet));
net->ctx.PDOassign = calloc (1, sizeof(ec_PDOassignt));
net->ctx.PDOdesc = calloc (1, sizeof(ec_PDOdesct));
net->ctx.eepSM = calloc (1, sizeof(ec_eepromSMt));
net->ctx.eepFMMU = calloc (1, sizeof(ec_eepromFMMUt));
net->ctx.maxslave = EC_MAXSLAVE;
net->ctx.maxgroup = EC_MAXGROUP;
net->ctx.esislave = 0;
net->ctx.DCtO = 0;
net->ctx.DCl = 0;
net->ctx.FOEhook = 0;
net->io_map = calloc (1, 4096);
if (!net->io_map)
{
printf ("[%s] Could not allocate memory for IO map\n", ifname);
goto cleanup;
}
return net;
cleanup:
// TODO: cleanup
if (net->io_map)
{
free (net->io_map);
}
if (net)
{
free (net);
}
return NULL;
}
int ecat_net_scan(ecat_net_t * net)
{
int status, i;
//int i, chk;
status = ecx_init (&net->ctx, net->ifname);
if (status <= 0)
{
printf ("[%s] ecx_init failed\n", net->ifname);
return -1;
}
status = ecx_config_init (&net->ctx, FALSE);
if (status <= 0)
{
printf ("[%s] no slaves found\n", net->ifname);
return -1;
}
ecx_config_map_group (&net->ctx, net->io_map, 0);
ecx_configdc (&net->ctx);
while (*(net->ctx.ecaterror))
{
printf ("[%s] error: %s\n", net->ifname, ecx_elist2string (&net->ctx));
}
printf ("[%s] %d slaves found and configured\n", net->ifname, *(net->ctx.slavecount));
net->expected_wc = (net->ctx.grouplist[0].outputsWKC * 2) +
net->ctx.grouplist[0].inputsWKC;
printf ("[%s] calculated workcounter %d\n", net->ifname, net->expected_wc);
/* wait for all slaves to reach SAFE_OP state */
ecx_statecheck (&net->ctx, 0, EC_STATE_SAFE_OP, EC_TIMEOUTSTATE * 3);
if (net->ctx.slavelist[0].state != EC_STATE_SAFE_OP )
{
// TODO: fix
printf ("[%s] not all slaves reached safe operational state.\n", net->ifname);
ecx_readstate (&net->ctx);
for (i = 1; i <= *(net->ctx.slavecount) ; i++)
{
if (net->ctx.slavelist[i].state != EC_STATE_SAFE_OP)
{
printf ("[%s] slave:%d, state:%2x, statuscode:%4x : %s\n", net->ifname,
i, net->ctx.slavelist[i].state, net->ctx.slavelist[i].ALstatuscode,
ec_ALstatuscode2string (net->ctx.slavelist[i].ALstatuscode));
}
}
}
/* ecx_readstate (&net->ctx); */
/* for(i = 1; i <= *(net->ctx.slavecount); i++) */
/* { */
/* printf ("[%s] slave:%d, name:%s, output:%d, input:%d\n", ifname, i, */
/* net->ctx.slavelist[i].name, net->ctx.slavelist[i].Obits, */
/* net->ctx.slavelist[i].Ibits); */
/* } */
#if 0
printf ("[%s] Request operational state for all slaves\n", ifname);
net->expected_wc = (net->ctx.grouplist[0].outputsWKC * 2) +
net->ctx.grouplist[0].inputsWKC;
printf ("[%s] calculated workcounter %d\n", ifname, net->expected_wc);
net->ctx.slavelist[0].state = EC_STATE_OPERATIONAL;
/* send one valid process data to make outputs in slaves happy*/
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
/* request OP state for all slaves */
ecx_writestate (&net->ctx, 0);
chk = 40;
/* wait for all slaves to reach OP state */
do
{
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
ecx_statecheck (&net->ctx, 0, EC_STATE_OPERATIONAL, 50000);
} while (chk-- && (net->ctx.slavelist[0].state != EC_STATE_OPERATIONAL));
if (net->ctx.slavelist[0].state != EC_STATE_OPERATIONAL)
{
printf ("[%s] not all slaves reached safe operational state.\n", ifname);
ecx_readstate (&net->ctx);
for(i = 1; i <= *(net->ctx.slavecount); i++)
{
if(net->ctx.slavelist[i].state != EC_STATE_SAFE_OP)
{
printf ("[%s] slave:%d, state:%2x, statuscode:%4x : %s\n", ifname,
i, net->ctx.slavelist[i].state, net->ctx.slavelist[i].ALstatuscode,
ec_ALstatuscode2string(net->ctx.slavelist[i].ALstatuscode));
}
}
goto cleanup;
}
printf("[%s] operational state reached for all slaves\n", ifname);
#endif
return 0;
}
/* //if, size, callbacks, link_cb, etc.); */
/* int ecat_net_scan(ecat_net_t * net); */
/* //int ecat_net_verify(...); */
int ecat_net_set_state(ecat_net_t * net, ecat_state_t state)
{
int ret = -1;
ecx_readstate (&net->ctx);
printf ("ecat_net_set_state: %s -> %s\n",
ecat_util_state_to_str (net->ctx.slavelist[0].state),
ecat_util_state_to_str (state));
// TODO: clear errors?
/* check if it's a valid transision */
switch (net->ctx.slavelist[0].state) {
/*
case EC_STATE_INIT:
if (state == ECAT_STATE_PRE_OP)
{
net->ctx.slavelist[0].state = EC_STATE_PRE_OP;
ecx_writestate (&net->ctx, 0);
ret = 0;
}
break;
case EC_STATE_PRE_OP:
if (state == ECAT_STATE_SAFE_OP)
{
net->ctx.slavelist[0].state = EC_STATE_SAFE_OP;
ecx_writestate (&net->ctx, 0);
ret = 0;
}
else if (state == ECAT_STATE_INIT)
{
net->ctx.slavelist[0].state = EC_STATE_INIT;
ecx_writestate (&net->ctx, 0);
ret = 0;
}
break;
*/
case EC_STATE_SAFE_OP:
if (state == ECAT_STATE_OP)
{
int chk = 40;
net->ctx.slavelist[0].state = EC_STATE_OPERATIONAL;
/* send one valid process data to make outputs in slaves happy*/
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
ecx_writestate (&net->ctx, 0);
do
{
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
ecx_statecheck (&net->ctx, 0, EC_STATE_OPERATIONAL, 50000);
} while (chk-- && (net->ctx.slavelist[0].state != EC_STATE_OPERATIONAL));
/* if (net->ctx.slavelist[0].state != EC_STATE_OPERATIONAL) */
/* { */
/* printf ("[%s] not all slaves reached safe operational state.\n", net->ifname); */
/* ecx_readstate (&net->ctx); */
/* for(i = 1; i <= *(net->ctx.slavecount); i++) */
/* { */
/* if(net->ctx.slavelist[i].state != EC_STATE_SAFE_OP) */
/* { */
/* printf ("[%s] slave:%d, state:%2x, statuscode:%4x : %s\n", net->ifname, */
/* i, net->ctx.slavelist[i].state, net->ctx.slavelist[i].ALstatuscode, */
/* ec_ALstatuscode2string(net->ctx.slavelist[i].ALstatuscode)); */
/* } */
/* } */
/* ret = -1; */
/* } */
/* else { */
/* ret = 0; */
/* } */
ret = 0;
}
break;
case EC_STATE_OPERATIONAL:
if (state == ECAT_STATE_SAFE_OP)
{
int chk = 40;
net->ctx.slavelist[0].state = EC_STATE_SAFE_OP;
ecx_writestate (&net->ctx, 0);
do
{
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
ecx_statecheck (&net->ctx, 0, EC_STATE_SAFE_OP, 50000);
} while (chk-- && (net->ctx.slavelist[0].state != EC_STATE_SAFE_OP));
ret = 0;
}
break;
default:
break;
}
return ret;
}
ecat_state_t ecat_net_get_state(ecat_net_t * net) //, ecat_net_state_t * state)
{
ecx_readstate (&net->ctx);
switch (net->ctx.slavelist[0].state)
{
case EC_STATE_INIT:
return ECAT_STATE_INIT;
case EC_STATE_PRE_OP:
return ECAT_STATE_PRE_OP;
case EC_STATE_SAFE_OP:
return ECAT_STATE_SAFE_OP;
case EC_STATE_OPERATIONAL:
return ECAT_STATE_OP;
case EC_STATE_BOOT:
return ECAT_STATE_BOOT;
default:
return -1;
}
}
/* int ecat_net_send(ecat_net_t * net); */
/* int ecat_net_receive(ecat_net_t * net); */
void ecat_net_print_slave_states (ecat_net_t * net)
{
int i;
ecx_readstate (&net->ctx);
printf ("[%s] ix. status name outp inpb error\n", net->ifname);
printf ("[%s] - %-7s system - - %s\n", net->ifname,
ecat_util_state_to_str (net->ctx.slavelist[0].state),
net->ctx.slavelist[0].ALstatuscode ? "yes" : "no");
for (i = 1; i <= *(net->ctx.slavecount) ; i++)
{
if (net->ctx.slavelist[i].ALstatuscode)
{
printf ("[%s] %2u. %-7s %-15s %4u %4d %s (%x)\n", net->ifname,
i, ecat_util_state_to_str (net->ctx.slavelist[i].state),
net->ctx.slavelist[i].name,
net->ctx.slavelist[i].Obits,
net->ctx.slavelist[i].Ibits,
ec_ALstatuscode2string (net->ctx.slavelist[i].ALstatuscode),
net->ctx.slavelist[i].ALstatuscode);
}
else
{
printf ("[%s] %2u. %-7s %-15s %4u %4u\n", net->ifname,
i, ecat_util_state_to_str (net->ctx.slavelist[i].state),
net->ctx.slavelist[i].name,
net->ctx.slavelist[i].Obits,
net->ctx.slavelist[i].Ibits);
}
}
}
typedef struct {
uint8_t state;
char name[8];
} _ecat_state_name_t;
#define ECAT_END_MARKER 0xffff
#define ECAT_ERR 0x10
const _ecat_state_name_t _ecat_state_name[] = {
{ ECAT_STATE_INIT & 0xff, "INIT" },
{ ECAT_STATE_PRE_OP & 0xff, "PRE" },
{ ECAT_STATE_SAFE_OP & 0xff, "SAFE" },
{ ECAT_STATE_OP & 0xff, "OPER" },
{ ECAT_STATE_BOOT & 0xff, "BOOT" },
{ ECAT_STATE_INIT | ECAT_STATE_ERROR & 0xff, "INIT(E)" },
{ ECAT_STATE_PRE_OP | ECAT_STATE_ERROR & 0xff, "PRE (E)" },
{ ECAT_STATE_SAFE_OP | ECAT_STATE_ERROR & 0xff, "SAFE(E)" },
{ ECAT_STATE_OP | ECAT_STATE_ERROR & 0xff, "OPER(E)" },
{ ECAT_STATE_BOOT | ECAT_STATE_ERROR & 0xff, "BOOT(E)" },
{ ECAT_END_MARKER & 0xff, "UNKN" }
};
char * ecat_util_state_to_str(ecat_state_t state)
{
int i = 0;
while ((_ecat_state_name[i].state != (uint8_t)ECAT_END_MARKER) &&
(_ecat_state_name[i].state != (uint8_t)state) )
{
i++;
}
return (char *) _ecat_state_name[i].name;
}
int ecat_net_send(ecat_net_t * net)
{
int status;
status = ecx_send_processdata(&net->ctx);
if (status >= 0) {
// TODO: increase send error counter
// failed to send frame
return -1;
}
else
{
// TODO: increase number of sent messages
}
return 0;
}
int ecat_net_receive(ecat_net_t * net, unsigned int timeout_us)
{
int wc;
wc = ecx_receive_processdata(&net->ctx, timeout_us);
if (wc <= EC_NOFRAME)
{
// TODO: increase missing frame counter
}
else if (wc != net->expected_wc)
{
// TODO: increase invalid message counter
}
else
{
// TODO: increase received messages counter
}
return wc;
}
int ecat_slave_set_state(ecat_net_t * net, unsigned int slave, ecat_state_t state)
{
int ret = -1;
ecx_readstate (&net->ctx);
printf ("ecat_net_set_state: %s -> %s\n",
ecat_util_state_to_str (net->ctx.slavelist[0].state),
ecat_util_state_to_str (state));
// TODO: clear errors?
/* check if it's a valid transision */
switch (net->ctx.slavelist[0].state) {
/*
case EC_STATE_INIT:
if (state == ECAT_STATE_PRE_OP)
{
net->ctx.slavelist[0].state = EC_STATE_PRE_OP;
ecx_writestate (&net->ctx, 0);
ret = 0;
}
break;
case EC_STATE_PRE_OP:
if (state == ECAT_STATE_SAFE_OP)
{
net->ctx.slavelist[0].state = EC_STATE_SAFE_OP;
ecx_writestate (&net->ctx, 0);
ret = 0;
}
else if (state == ECAT_STATE_INIT)
{
net->ctx.slavelist[0].state = EC_STATE_INIT;
ecx_writestate (&net->ctx, 0);
ret = 0;
}
break;
*/
case EC_STATE_SAFE_OP:
if (state == ECAT_STATE_OP)
{
int chk = 40;
net->ctx.slavelist[0].state = EC_STATE_OPERATIONAL;
/* send one valid process data to make outputs in slaves happy*/
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
ecx_writestate (&net->ctx, 0);
do
{
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
ecx_statecheck (&net->ctx, 0, EC_STATE_OPERATIONAL, 50000);
} while (chk-- && (net->ctx.slavelist[0].state != EC_STATE_OPERATIONAL));
/* if (net->ctx.slavelist[0].state != EC_STATE_OPERATIONAL) */
/* { */
/* printf ("[%s] not all slaves reached safe operational state.\n", net->ifname); */
/* ecx_readstate (&net->ctx); */
/* for(i = 1; i <= *(net->ctx.slavecount); i++) */
/* { */
/* if(net->ctx.slavelist[i].state != EC_STATE_SAFE_OP) */
/* { */
/* printf ("[%s] slave:%d, state:%2x, statuscode:%4x : %s\n", net->ifname, */
/* i, net->ctx.slavelist[i].state, net->ctx.slavelist[i].ALstatuscode, */
/* ec_ALstatuscode2string(net->ctx.slavelist[i].ALstatuscode)); */
/* } */
/* } */
/* ret = -1; */
/* } */
/* else { */
/* ret = 0; */
/* } */
ret = 0;
}
break;
case EC_STATE_OPERATIONAL:
if (state == ECAT_STATE_SAFE_OP)
{
int chk = 40;
net->ctx.slavelist[0].state = EC_STATE_SAFE_OP;
ecx_writestate (&net->ctx, 0);
do
{
ecx_send_processdata (&net->ctx);
ecx_receive_processdata (&net->ctx, EC_TIMEOUTRET);
ecx_statecheck (&net->ctx, 0, EC_STATE_SAFE_OP, 50000);
} while (chk-- && (net->ctx.slavelist[0].state != EC_STATE_SAFE_OP));
ret = 0;
}
break;
default:
break;
}
return ret;
}
int ecat_slave_get_dc_status(ecat_net_t * net, uint32_t slave,
ecat_slave_dc_status_t * status)
{
uint8_t sys_time[32];
/* Read System Time registers */
if (ecx_FPRD(net->ctx.port, net->ctx.slavelist[slave].configadr,
ECT_REG_DCSYSTIME, sizeof(sys_time), sys_time, EC_TIMEOUTRET3))
{
/* System Time */
memcpy(&status->time,
&sys_time[ECT_REG_DCSYSTIME - ECT_REG_DCSYSTIME],
sizeof(status->time));
/* System Time Offset */
memcpy(&status->offset,
&sys_time[ECT_REG_DCSYSOFFSET - ECT_REG_DCSYSTIME],
sizeof(status->offset));
/* System Time Delay */
memcpy(&status->delay,
&sys_time[ECT_REG_DCSYSDELAY - ECT_REG_DCSYSTIME],
sizeof(status->delay));
/* System Time Difference */
memcpy(&status->difference,
&sys_time[ECT_REG_DCSYSDIFF - ECT_REG_DCSYSTIME],
sizeof(status->difference));
if (status->difference & 0x80000000) {
status->difference =
0 - (status->difference & 0x7FFFFFFF);
}
return 0;
}
return -1;
}
int ecat_slave_get_status(ecat_net_t * net, uint32_t slave,
ecat_slave_status_t * status)
{
int ret = 0x0;
uint16_t dlstat;
uint16_t wdcntr;
uint8_t err_cntrs[20];
uint8_t p;
/* Read ESC DL status register */
if (ecx_FPRD(net->ctx.port, net->ctx.slavelist[slave].configadr,
ECT_REG_DLSTAT, sizeof(dlstat), &dlstat, EC_TIMEOUTRET3))
{
/* Link detected */
for (p=0; p <= 3; p++)
{
status->port[p].link_detected =
dlstat & (0x0010 << p)
? ECAT_SLAVE_LINK_DETECTED:ECAT_SLAVE_NO_LINK;
}
/* Loop port */
for (p=0; p <= 3; p++)
{
status->port[p].loop_port =
dlstat & (0x0100 << (p * 2))
? ECAT_SLAVE_PORT_CLOSED:ECAT_SLAVE_PORT_OPEN;
}
/* Stable communication */
for (p=0; p <= 3; p++)
{
status->port[p].stable_com =
dlstat & (0x0200 << (p * 2))
? ECAT_SLAVE_COM_ESTABLISHED:ECAT_SLAVE_NO_STABLE_COM;
}
}
else
{
ret |= 0x1;
}
/* Read Error Counters registers */
if (ecx_FPRD(net->ctx.port, net->ctx.slavelist[slave].configadr,
ECT_REG_RXERR, sizeof(err_cntrs), err_cntrs, EC_TIMEOUTRET3))
{
/* Lost Link Counter */
for (p=0; p <= 3; p++)
{
status->port[p].lost_link_counter =
err_cntrs[ECT_REG_LLCNT - ECT_REG_RXERR + p];
}
/* RX Error Counter */
for (p=0; p <= 3; p++)
{
status->port[p].invalid_frame_counter =
err_cntrs[ECT_REG_RXERR + (p * 2)];
status->port[p].rx_error_counter =
err_cntrs[ECT_REG_RXERR + (p * 2) + 1];
}
/* Forwarded RX Error Counter */
for (p=0; p <= 3; p++)
{
status->port[p].forwarded_rx_error_counter =
err_cntrs[ECT_REG_FRXERR - ECT_REG_RXERR + p];
}
/* ECAT Processing Unit Error Counter */
status->ecat_processing_unit_error_counter =
err_cntrs[ECT_REG_EPUECNT - ECT_REG_RXERR];
/* PDI Error Counter */
status->pdi_error_counter =
err_cntrs[ECT_REG_PECNT - ECT_REG_RXERR];
/* PDI Error Code */
status->pdi_error_code =
err_cntrs[ECT_REG_PECODE - ECT_REG_RXERR];
}
else
{
ret |= 0x2;
}
/* Read Watchdog register */
if (ecx_FPRD(net->ctx.port, net->ctx.slavelist[slave].configadr,
ECT_REG_WDCNT, sizeof(wdcntr), &wdcntr, EC_TIMEOUTRET3))
{
/* Watchdog counter process data and pdi */
status->wd_cnt.watchdog_counter_process_data = wdcntr & 0x00FF;
status->wd_cnt.watchdog_counter_pdi = wdcntr & 0xFF00;
}
else
{
ret |= 0x4;
}
return ret > 0 ? -ret:ret;
}