SDK Script serial-tcp-broadcast.are

serial-tcp-broadcast.are
/* DESC: This script reads messages coming from the serial port and forwards them via TCP to remote hosts (and vice versa).
 * Copyright (C) 2014 NetModule AG, Switzerland
 *
 * Usage: usage: serial-tcp-broadcast.are <remote-ip-1>  <remote-ip-2> .... <remote-ip-n>
 * Version: 2.0
 *
 */
 
/* 
 * Options to configure by user
 */
 
/* Serial Baud Rate 9600, 19200, 38400, 57600, 115200 */
SER_SPEED = 115200;
/*  number of data bits (5, 6, 7, 8) */
SER_DATABIT = 8;
/*  number of stop bits (1, 2) */
SER_STOPBIT = 1;
/* parity (0=no parity, 1=odd parity, 2=even parity) */
SER_PARITY = 0;
/*  flow control (0=none, 1=xon/xoff, 2=hardware) */
SER_FLOW = 0;
/*  name of the Serial Interface */
DEV = "SERIAL1";
DEVIDX = ((int) substr(DEV, 6, 1)) - 1;
 
/* TCP Options */
/* number of retries when connecting to a slave */
RETRY = 3;	
/* the interval in seconds for each retry */
RETRY_INTERVAL = 3;	
/* recovery interval after slave got disconnected or retries were unsuccessful */
RECOVER_INTERVAL = 120;
 
 
/* 
 * Only changed things below if you know what you are doing 
 */
 
POLL_TIMEOUT = 10;  /* seconds */
REMOTE_PORT =2000;  /* port of the remote serial slaves (default 2000) */ 
 
/* slave template */
template slave 
{
    hostname = "";
    fd = -1;
    state = "down";
    since = 0; 
 
    void slave(string remotehost) 
    { 
        nb_syslog("Creating slave %s",remotehost);
        if (strlen(remotehost) > 0) {
            this.hostname = remotehost;
        } else {
            nb_syslog("ERROR: no hostname defined, slave could not be created");
        }
    }
 
    int jiffy()
    {
        sys = sysinfo();
        u = struct_get(sys, "uptime");
 
        if (is_void(u) || u < 1) {
            return 0;
        } else {
            return u;
        }
    } 
 
    int connect() {
        if (this.fd > -1) {
            close(this.fd);
            this.fd = -1;
        }
        nb_syslog("Connecting to %s", this.hostname);
 
        /* open TCP socket */
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE,1);
        setsockopt(sock, SOL_TCP, TCP_KEEPIDLE,1);
        setsockopt(sock, SOL_TCP, TCP_KEEPINTVL,5);
        setsockopt(sock, SOL_TCP, TCP_KEEPCNT,5);
 
        if (sock < 0) {
            nb_syslog("ERROR: unable to open socket");
        }
        for (i = 0; i < RETRY; i++) {
            if (connect(sock, this.hostname, REMOTE_PORT) < 0){
                nb_syslog("ERROR: could not connect to %s:%d", this.hostname, REMOTE_PORT);
                nb_syslog("Retrying to connect (trial #%i, waiting %s sec)", i, RETRY_INTERVAL);
                sleep(RETRY_INTERVAL);
            } else {
                nb_syslog("TCP connection established to host %s:%d", this.hostname, REMOTE_PORT);
                this.fd = sock;
                this.state = "up";
                return 0;
            }
        }
        nb_syslog("ERROR:  max. retries reached for %s, giving up", this.hostname);  
        this.since = this.jiffy();
        this.state = "down";
        close(this.fd);
        this.fd = -1;
 
        return -1;
    }
 
    int disconnect() {
        if (this.fd > -1) {
            close(this.fd);
            this.fd = -1;
        }
        this.state = "down";
        this.since = this.jiffy();
        nb_syslog("Disconnected host %s", this.hostname);  
        return 0;
    }
 
 
} /* endof template slave */
 
int start_serial() 
{
    /* check serial port config */
    status = nb_config_get(sprintf("serial.%d.status", DEVIDX));
    if (status != "2") {
        nb_syslog("ERROR: serial port %d is not enabled for us", DEVIDX);
        exit(1);
    }
 
    /* set attributes */
    rc = nb_serial_setattr(DEV, SER_SPEED, SER_DATABIT, SER_STOPBIT, SER_PARITY, SER_FLOW);
    if (rc != 0) {
        nb_syslog("ERROR: unable to set serial attributes (rc %d)", rc);
        exit(1);
    }
 
    /* open serial port */
    fd = nb_serial_open(DEV);
    if (fd < 0) {
        nb_syslog("ERROR: unable to open serial device (%s)", DEV);
        exit(1);
    }
 
    return fd;
}
 
int stop_serial() 
{
    if (fd > -1) {
        close(fd);
        fd = -1;
    }
    return 0; 
}
 
void usage()
{
    nb_syslog("usage: serial-tcp-broadcast.are <remote-ip-1>  <remote-ip-2> .... <remote-ip-n>");
    exit(1);
}
 
 
/* ------------------------ main ------------------------------- */
 
 
if (strlen(argv[1]) == 0) {
    nb_syslog("ERROR: no remote host specified");
    usage();
}
 
/* create slaves */
nb_syslog("creating slaves");
for (i = 1; i < argc; i++) {
    slaves[i-1] = new slave(argv[i]);
}
 
/* connect slave */
for (i = 0; i < length(slaves); i++) {
    nb_syslog("connecting slave %d", i);
    rc = slaves[i].connect();
    if (rc < 0) {
        nb_syslog("ERROR: could not connect %s", slaves[i].hostname);   
    }
}
 
nb_syslog("starting serial");
serial_fd = start_serial();
 
/* create file descriptor array */
fds = mkarray();
tcp_fds = mkarray();
j = 0;
for (i = 0; i < length(slaves); i++) {
     if (slaves[i].state == "up") {
        tcp_fds[j] = slaves[i].fd;
        j++;   
    }
}
fds = array_merge(serial_fd, tcp_fds);
 
 
/* loop infinitely */
while (1) {
    buffer = "";
    rc = select(fds, POLL_TIMEOUT);
    if (rc == -1) {
        nb_syslog("ERROR: select failed");
    } else if (rc == serial_fd) {
        /* received something from serial device */
        data = read(serial_fd, 4096);
        if (data) {
            serialbuffer = strcat(serialbuffer, data);
            nb_syslog("data on serial port: %s",serialbuffer);
        }
    } else if (rc != 0 && array_search(tcp_fds, rc) != NULL) {
        /* received something from TCP */
        data = recv(rc);
        len=strlen(data);
        if (len > 0) {
            /* write to serial device */
            write(serial_fd, data, len);
        } else if(len == 0) {
            //nb_syslog("tcp connection lost, reconnecting");
            for (i = 0; i < length(slaves); i++) {
                if (rc == slaves[i].fd) {
                    /* disconnect */
                    slaves[i].disconnect();   
                }
            }
        }
    }
 
 
    /* check serial buffer and send message */
    len = strlen(serialbuffer);
    msg = serialbuffer;
    msglen = len;
    serialbuffer = "";
 
    /* send TCP packet to every slave */
    if (len > 0) {
        for (i = 0; i < length(slaves); i++) {
            if (slaves[i].state == "up" ) {
                sent = sendbuf(slaves[i].fd, msg, msglen);
                if (sent == -1) {
                    nb_syslog("ERROR: failed to send packet to %s", slaves[i].hostname);
                    /* disconnect slave */
                    slaves[i].disconnect();   
                }
            }
        }
    }
    /* check if we have an error on the socket */
    for (i = 0; i < length(slaves); i++) {
        if (slaves[i].state == "up" ) {
            opt = getsockopt(slaves[i].fd, SOL_SOCKET, 4);
            if (opt != 0) {
               nb_syslog("ERROR: getsockopt(%d) on slave %s failed, reconnecting", opt, slaves[i].hostname);
               slaves[i].disconnect();
            }
        }
    }
 
    /* reconnect to disconnected slaves */
    for ( i=0; i < length(slaves); i++) {
        if (slaves[i].state == "down" ) {
            if ( (slaves[i].since + RECOVER_INTERVAL) < slaves[i].jiffy() ) {
              slaves[i].connect();
            }
        }
    }
 
    /* re-create file descriptor array */
    fds = mkarray();
    tcp_fds = mkarray();
    j = 0;
    for (i = 0; i < length(slaves); i++) {
         if (slaves[i].state == "up") {
             tcp_fds[j] = slaves[i].fd;
             j++;
        }
    }
    fds = array_merge(serial_fd, tcp_fds);
}
 
exit(0);