/*********************************************************************************
|
* 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 <guowenxue@gmail.com>
|
* ChangeLog: 1, Release initial version on "2018年05月29日 21时03分54秒"
|
*
|
********************************************************************************/
|
|
#include <libgen.h>
|
#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;
|
}
|