Serial Point to Multipoint

This script reads messages coming from the local serial port and forwards them via TCP to a all remote host (and vice versa). The local port can be the physical RS-232 (SERIAL1) or a virtual one using a USB adapter (SERIAL2).

serial-point-to-multipoint.are
// DESC: This script reads messages coming from the serial port and forwards them via TCP to a all remote host (and vice versa).
// Copyright (C) 2014 NetModule AG
//  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 (Only changed if your device has more then one serial Interface (NB3710) */
DEV = "SERIAL1";
 
/* TCP Options */
/* If the script try to connect to a tcp slave, how many retries should it make */
RETRY = 3;	
/* The pause in seconds between the retries*/
RETRY_INTERVAL = 3;	
/* How many seconds should the script wait to run a new set of retries after the client got lost, or the retries were not sucessfull */
RECOVER_INTERVAL = 120;
 
 
//################################################################
//################################################################
//################################################################
/* Only changed things below this line if you know what you are doing */
//################################################################
//################################################################
//################################################################
POLL_TIMEOUT = 10;           	/* seconds */
REMOTE_PORT =2000;  /* port of the remote serial slaves 2000 is default on NB Products */ 
 
 
 
template slave 
{
    hostname = "";
    fd = -1;
    state = "down";
    downSince = 0; 
 
 
    void slave(string remotehost) 
    { 
        nb_syslog("creating slave %s",remotehost);
        if ( strlen(remotehost) > 0 ) {
            this.hostname=remotehost;
        } else {
            nb_syslog("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() {
        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("Could not connect to %s (port %d)", this.hostname, REMOTE_PORT);
                nb_syslog("Retry connect, trial #%i, waiting %s sec", i, RETRY_INTERVAL);
                sleep(RETRY_INTERVAL);
            } else {
                nb_syslog("TCP connection is opened - remote server %s:%d", this.hostname, REMOTE_PORT);
                this.fd=sock;
                this.state="up";
                return 0;
            }
        }
        nb_syslog("Max retries reached, giving up TCP connection to %s",this.hostname);  
        this.downSince=this.jiffy();
        this.state="down";
        close(this.fd);
        return -1;
    }
 
    int disconnect() {
        close(this.fd);
        this.fd=-1;
        this.state="down";
        this.downSince=this.jiffy();
        nb_syslog("Host %s disconnected", this.hostname);  
        return 0;
    }
 
 
} // of template slave
 
void usage()
{
    nb_syslog("usage: serial-tcp-broadcast.are <remote-ip-1>  <remote-ip-2> .... <remote-ip-n>");
    exit(1);
}
 
 
 
int start_serial() {
 
    /* check serial port config */
    status = nb_config_get("serial.0.status");
    if (status != "2") {
        nb_syslog("Serial port is not enabled for us");
        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("Unable to open serial device (%s)", DEV);
        exit(1);
    }
 
    return fd;
}
 
int stop_serial() {
 
close(fd);
return 0; 
}
 
 
 
 
// ###################################################
// ###################################################
// ########Begining of Program########################
// ###################################################
// ###################################################
// ###################################################
 
/* check arguments */
if (strlen(argv[1]) == 0) {
    nb_syslog("ERROR: no remote host specified");
    usage();
}
 
/* create slave host list and set  */
 
 
nb_syslog("creating slaves");
for (i=1; i < argc; i++) {
    slaves[i-1] = new slave(argv[i]);
}
// connect to slave
//for ( i=0; i < length(slaves); i++) {
for ( i=0; i < length(slaves); i++) {
     nb_syslog("connect to slave %i",i);
    rc = slaves[i].connect();
    if (rc < 0) {
    nb_syslog("could not create socket %s", slaves[i].hostname);   
    }
}
 
nb_syslog("starting serial");
serial_fd = start_serial();
 
 
 
// Creating tcp fd array for select()
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);
 
 
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 is gone lets set the slave correctly");
            for ( i=0; i < length(slaves); i++) {
                if (rc == slaves[i].fd) {
                /* lets put the socket on disable */
                    slaves[i].disconnect();   
                }
            }
 
        }
     }
 
 
    /* check serial buffer and send message */
    len = strlen(serialbuffer);
    // we have data for the slaves lets sent it
    msg = serialbuffer;
    msglen = len;
    serialbuffer = "";
 
    /* send TCP packet to every socket */
    if (len > 0) {
        for ( i=0; i < length(slaves); i++) {
            if (slaves[i].state == "up" ) {
                sent = send(slaves[i].fd, msg);
                if (sent == -1) {
                    nb_syslog("failed sending to host: %s fd: %i",slaves[i].hostname,slaves[i].fd);
                    /* lets put the socket on disable */
                    slaves[i].disconnect();   
                }
            }
        }
    }
    /* check if we have a error on the soocket */
    for ( i=0; i < length(slaves); i++) {
        if (slaves[i].state == "up" ) {
            sockop=getsockopt(slaves[i].fd, SOL_SOCKET, 4);
            if (sockop != 0) {
               nb_syslog("Error No %i on slave %s, Set Slave offline and try reconnect again", sockop, slaves[i].hostname);
               slaves[i].disconnect();
            }
        }
    }
 
 
    // reconnect to lost slaves    
    for ( i=0; i < length(slaves); i++) {
        if (slaves[i].state == "down" ) {
            if ( (slaves[i].downSince + RECOVER_INTERVAL) < slaves[i].jiffy() ) {
              slaves[i].connect();
            }
        }
    }
 
    // recreate online fds
    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);