Commit ce6ba54e authored by Franksen, Benjamin's avatar Franksen, Benjamin
Browse files

scan: implement auto-reconnect

parent bfd0e911
......@@ -186,6 +186,7 @@ struct ellsi_state {
unsigned unread; /* number of as yet unread telegrams in packet */
int sock; /* socket to communicate over */
struct sockaddr_in addr; /* socket address info */
int baudrate;
};
static ssize_t ellsi_write(void *pvt, const struct can_frame *frame) {
......@@ -297,7 +298,11 @@ static int ellsi_check(int sock) {
memset(&ack, 0, sizeof(ack));
r = recv(sock, &ack, sizeof(ack), MSG_WAITALL);
if (r < 0) {
fprintf(stderr, "ellsi_check: recv failed\n"); return r;
if (errno != EAGAIN && errno != EWOULDBLOCK) {
errbuf_t buf;
fprintf(stderr, "ellsi_check: recv failed (%s)\n", ERRNO_STR(buf));
}
return r;
}
debug("ellsi_check: got back magic %#x cmd %#x subcmd %#x payloadLen %d\n",
ntohl(ack.header.magic),
......@@ -419,10 +424,54 @@ static int ellsi_add_can_ids(ellsi_state_t *state) {
return ellsi_check(state->sock);
}
static int ellsi_connect(void *pvt) {
errbuf_t buf;
ellsi_state_t *state = (ellsi_state_t *)pvt;
struct timeval tmo = { 5, 0 };
/* connect socket */
if (connect(state->sock, (struct sockaddr *)&state->addr, sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr, "ellsi_connect: error in socket connect (%s)\n", ERRNO_STR(buf));
return -1;
}
/* set recv timout for during connect */
if (setsockopt(state->sock, SOL_SOCKET, SO_RCVTIMEO, &tmo, sizeof(tmo)) < 0) {
fprintf(stderr, "ellsi_connect: error setting socket option SO_RCVTIMEO (%s)\n",
ERRNO_STR(buf));
return -1;
}
/* initialize protocol */
if (ellsi_register(state)) {
return -1;
}
/* configure baudrate */
if (ellsi_set_baudrate(state, state->baudrate)) {
return -1;
}
/* enable all CAN ids */
if (ellsi_add_can_ids(state)) {
return -1;
}
/* reset timout for recv operation to zero (default, infinite) */
tmo.tv_sec = 0;
if (setsockopt(state->sock, SOL_SOCKET, SO_RCVTIMEO, &tmo, sizeof(tmo)) < 0) {
fprintf(stderr, "ellsi_connect: error resetting socket option SO_RCVTIMEO (%s)\n",
ERRNO_STR(buf));
return -1;
}
return 0;
}
static scan_methods_t methods = {
ellsi_read,
ellsi_write,
ellsi_dump,
ellsi_connect
};
int ellsiConfigPort(int can_port, const char *ip_addr, int ip_port, int baudrate) {
errbuf_t buf;
ellsi_state_t *state = new(ellsi_state_t);
scan_methods_t *methods;
if (!state) {
fprintf(stderr, "ellsiConfigPort: out of memory\n");
......@@ -442,41 +491,8 @@ int ellsiConfigPort(int can_port, const char *ip_addr, int ip_port, int baudrate
free(state);
return -1;
}
/* success, connect socket */
if (connect(state->sock, (struct sockaddr *)&state->addr, sizeof(struct sockaddr_in)) < 0) {
fprintf(stderr, "ellsiConfigPort: error in socket connect (%s)\n", ERRNO_STR(buf));
free(state);
return -1;
}
/* success, allocate methods */
methods = new(scan_methods_t);
if (!methods) {
fprintf(stderr, "ellsiConfigPort: out of memory\n");
free(state);
return -1;
}
/* success, fill methods struct */
methods->write = ellsi_write;
methods->read = ellsi_read;
methods->dump = ellsi_dump;
/* initialize protocol */
if (ellsi_register(state)) {
fprintf(stderr, "ellsiConfigPort: error in ellsi_register\n");
free(state);
return -1;
}
/* configure baudrate */
if (ellsi_set_baudrate(state, baudrate)) {
fprintf(stderr, "ellsiConfigPort: error in ellsi_set_baudrate\n");
free(state);
return -1;
}
/* enable all CAN ids */
if (ellsi_add_can_ids(state)) {
fprintf(stderr, "ellsiConfigPort: error in ellsi_add_can_ids\n");
free(state);
return -1;
}
/* success, store driver specific configuration */
state->baudrate = baudrate;
/* install driver and initialize sci port */
return scan_config_port(can_port, state, methods);
return scan_config_port(can_port, state, &methods);
}
......@@ -481,7 +481,6 @@ int scan_config_port(
tid = epicsThreadCreate(name, epicsThreadPriorityMax-1,
epicsThreadGetStackSize(epicsThreadStackSmall), sci_listen, sci_port);
if (tid) {
/* done */
res = 0;
} else {
fprintf(stderr, "scan_config_port: thread create failed (%s)\n",
......@@ -512,7 +511,13 @@ static void sci_listen(void *parg) {
errbuf_t buf;
struct sci_port *port = (struct sci_port *)parg;
port->connected = 1;
/* initial connection attempt */
if (port->methods->connect) {
/* try to reconnect */
port->connected = (port->methods->connect(port->driver_pvt) == 0);
} else {
port->connected = 1;
}
while (1) {
if (port->connected) {
......@@ -567,8 +572,15 @@ static void sci_listen(void *parg) {
}
} else {
epicsThreadSleep(5.0);
/* TODO try to reconnect */
port->connected = 1;
if (port->methods->connect) {
/* try to reconnect */
port->connected = (port->methods->connect(port->driver_pvt) == 0);
if (port->connected) {
errlogSevPrintf(errlogInfo,
"sci_listen: successfully re-connected port %d\n",
port->port_num);
}
}
}
}
}
......
......@@ -4,6 +4,9 @@
* The driver should implement a configuration/initialization call.
* This should create a socket, allocate struct scan_io_methods,
* fill in appropriate methods, and then call scan_config_port.
* This in turn will call connect method, when port has been set up.
* The connect method is also called when connection has been lost
* and we should re-connect.
* It can also remember private data in the driver_pvt parameter
* passed to scan_config_port.
*
......@@ -21,6 +24,7 @@
typedef ssize_t write_t(void *pvt, const struct can_frame *frame);
typedef ssize_t read_t(void *pvt, struct can_frame *frame);
typedef void dump_t(void *pvt);
typedef int connect_t(void *pvt);
typedef struct scan_methods scan_methods_t;
......@@ -28,12 +32,13 @@ struct scan_methods {
read_t *read; /* read a can frame */
write_t *write; /* write a can frame to a socket */
dump_t *dump; /* dump private driver info to stdout */
connect_t *connect; /* driver specific connect actions for this port */
};
/*
* Parameter 'methods' must be allocated by the caller and fully
* initialized. NULL is admissible for 'driver_pvt' but not for
* the methods.
* the methods, except 'connect', which may be NULL.
* It is an error if the port is already configured.
*/
extern int scan_config_port(
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment