/********************************************************************************* * Copyright: (C) 2018 LingYun IoT System Studio * All rights reserved. * * Filename: sp2sck.c * Description: This file is the serial port to socket converter * * Version: 1.0.0(2018年05月29日) * Author: Guo Wenxue * ChangeLog: 1, Release initial version on "2018年05月29日 21时03分54秒" * ********************************************************************************/ #include #include "cp_logger.h" #include "cp_comport.h" #include "cp_socket.h" #include "cp_proc.h" #include "sp2sck.h" void *comport_worker(void *); void *socket_worker(void *); /* Show program version information, can used by 'sp2sck --version or sp2sck -v'*/ static inline void prog_version(const char *progname) { printf("%s Version 1.0.0 Build(%s)\n", progname, __DATE__); return ; } /* Show program help information, can used by 'sp2sck --help or sp2sck -h'*/ static void prog_usage(const char *progname) { prog_version(progname); printf("Usage: %s [OPTION]...\n", progname); printf("Receive date from a serial port and transfer the data to remote server by socket.\n"); printf("\nMandatory arguments to long options are mandatory for short options too:\n"); printf(" -d[debug ] Running in debug mode\n"); printf(" -l[level ] Set the log level as [0..%d]\n", LOG_LEVEL_MAX-1); printf(" -c[comport ] Serial port device, default as /dev/ttyS1.\n"); printf(" -b[baudrate] Baudrate, default as 115200.\n"); printf(" -s[server ] Socket connect server host and port, format as: Hostname:Port, default as 127.0.0.1:8900\n"); printf(" -v[version ] Display program version\n"); printf(" -h[help ] Display this help information\n"); return ; } int main (int argc, char **argv) { const char *progname=NULL; int opt; int debug = 0; /* program running information log to stdard output */ char pid_file[64] = { 0 }; /* The file used to record the PID */ int log_level = LOG_LEVEL_TRACE; /* program running information log to file level */ cp_logger *logger; char *log_file="sp2sck.log"; /* program running information log file name */ char *devname = "/dev/ttyS1"; /* default serial port, can use '-c' option to change */ unsigned long baudrate = 115200; /* default serial port baudrate, can use '-b' option to change */ char *server="127.0.0.1:8900"; /* default connect server address, can use '-s' option to change */ sp2sck_ctx_t ctx; int rv; pthread_t tid; memset(&ctx, 0, sizeof(ctx)); /* progranm command options */ struct option long_options[] = { {"debug", no_argument, NULL, 'd'}, /* sp2sck -d or sp2sck --debug, log to standard output or not */ {"level", required_argument, NULL, 'l'}, /* sp2sck -l 7, specify log to file level */ {"comport", required_argument, NULL, 'c'}, /* sp2sck -c /dev/ttyUSB0, specify serial port device name */ {"baudrate", required_argument, NULL, 'b'}, /* sp2sck -b 115200, specify serial port baudrate */ {"server", required_argument, NULL, 's'}, /* sp2sck -s 192.168.0.5:9999, specify server address and port */ {"version", no_argument, NULL, 'v'}, /* sp2sck -v or sp2sck --version to check program version information */ {"help", no_argument, NULL, 'h'}, /* sp2sck -h or sp2sck --help to get program help information */ {NULL, 0, NULL, 0} /* array end flag */ }; progname = basename(argv[0]); /* Parser the command line options */ while ((opt = getopt_long(argc, argv, "c:b:s:dl:vh", long_options, NULL)) != -1) { switch (opt) { case 'c': /* sp2sck -c /dev/ttyUSB0 */ devname = optarg; break; case 'b': /* sp2sck -b 115200 */ baudrate = atol(optarg); break; case 's': /* sp2sck -s 192.168.0.5:9999 */ server=optarg; break; case 'd': /* sp2sck -d or sp2sck --debug */ debug = 1; log_file = DBG_LOG_FILE; break; case 'l': /* sp2sck -l 7 */ rv = atoi(optarg); log_level = rv>LOG_LEVEL_MAX ? LOG_LEVEL_MAX-1 : rv; break; case 'v': /* sp2sck -v */ prog_version(progname); return EXIT_SUCCESS; case 'h': /* sp2sck -h */ prog_usage(progname); return 0; default: break; } /* end of "switch(opt)" */ } /* parser hostname and port by server*/ { char *ptr; ptr=strchr(server, ':'); if( !ptr ) { printf("Invalid server host[%s], which format should be [HostName:Port] such as \"127.0.0.1:8900\"", server); return -1; } strncpy(ctx.hostname, server, ptr-server); ctx.port = atoi(&ptr[1]); } printf("Serial[%s] and server[%s:%d] converter start\n", devname, ctx.hostname, ctx.port); /* check program already running on background or not */ if( !debug ) { snprintf(pid_file, sizeof(pid_file), "/var/run/%s.pid", progname); if( check_daemon_running(pid_file) ) { printf("Programe already running, exit now.\n"); return -1; } } /* initialise logger system */ if( !(logger=cp_log_init(NULL, log_file, log_level, 0)) || cp_log_open()<0 ) { printf("Init logger system failed, program exit now...\n"); return -1; } log_nrml("Serial[%s] and server[%s] converter start\n", devname, server); if( NULL == (ctx.comport=comport_init(devname, baudrate, "8N1N")) ) { log_err("initial serial port failure\n"); return -1; } /* set program running in background */ if( !debug ) { if( set_daemon_running(pid_file) ) { log_fatal("Set program \"%s\" running as daemon failure.\n", progname); return -2; } } /* install signal process handler */ cp_install_proc_signal(); /* start serial port data process thread */ thread_start(&tid, comport_worker, &ctx); /* start socket data process thread */ thread_start(&tid, socket_worker, &ctx); while( !g_cp_signal.stop ) { /* control/main thread do nothing here */ sleep(1); } return 0; } void *comport_worker(void *arg) { sp2sck_ctx_t *ctx = (sp2sck_ctx_t *)arg; cp_comport_t *comport; cp_socket_t *socket; char buf[1024]; int rv = 0; if( !ctx ) { log_err("Invalid arguments\n"); return NULL; } comport = ctx->comport; socket = &ctx->socket; log_nrml("Thread worker for comport start\n"); while( !g_cp_signal.stop ) { if( ctx->comport->connected ) { rv=comport_open(ctx->comport); if( 0 == rv ) log_nrml("open serial port \"%s\" successfully\n", ctx->comport->dev_name); else log_err("open serial port \"%s\" failure, rv=%d\n", ctx->comport->dev_name, rv); } if( !comport->connected || SOCK_STAT_CONNECTED != socket->status ) { continue; } rv=comport_recv(comport, buf, sizeof(buf), 1000); if( rv > 0 ) { log_nrml("Recive %d bytes data from comport[%s] and will write to socket[%s:%d]\n", rv, comport->dev_name, socket->servaddr, socket->servport); cp_log_dump(LOG_LEVEL_DEBUG, buf, rv); write(socket->fd, buf, rv); } } return NULL; } void *socket_worker(void *arg) { sp2sck_ctx_t *ctx = (sp2sck_ctx_t *)arg; cp_comport_t *comport; cp_socket_t *socket; char buf[1024]; int rv = 0; if( !ctx ) { log_err("Invalid arguments\n"); return NULL; } comport = ctx->comport; socket = &ctx->socket; log_nrml("Thread worker for socket start\n"); while( !g_cp_signal.stop ) { if( SOCK_STAT_CONNECTED != ctx->socket.status ) { printf("start block connect\n"); rv=cp_sock_block_connect(&ctx->socket, ctx->hostname, ctx->port); if( rv<0 ) log_err("connect to server [%s:%d] return rv=%d\n", ctx->socket.servaddr, ctx->socket.servport, rv); else log_nrml("connect to server [%s:%d] successfully\n", ctx->socket.servaddr, ctx->socket.servport); } if( !comport->connected || SOCK_STAT_CONNECTED != socket->status ) { continue; } rv=read(socket->fd, buf, sizeof(buf)); if( rv > 0 ) { log_nrml("Recive %d bytes data from socket[%s:%d] and will write to comport[%s]\n", rv, socket->servaddr, socket->servport, comport->dev_name); cp_log_dump(LOG_LEVEL_DEBUG, buf, rv); comport_send(comport, buf, rv); } else if( 0==rv ) { log_warn("Remove socket server disconnect\n"); socket->status = SOCK_STAT_DISCONNECT; } } return NULL; }