You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
9.2 KiB
357 lines
9.2 KiB
/*
|
|
* modbus_port_linux.c
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2024-04-02 qiyongzhong first version
|
|
*/
|
|
|
|
#include "modbus_port.h"
|
|
|
|
#ifdef MB_USING_PORT_LINUX
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <netdb.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
|
|
#define QLOG_TAG "modbus.port.linux"
|
|
#define QLOG_LVL QLOG_LVL_LOG //QLOG_LVL_INFO //
|
|
#include "qlog.h"
|
|
|
|
MB_WEAK long long mb_port_get_ms(void)//获取毫秒时间
|
|
{
|
|
long long ms;
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
ms = tv.tv_sec;
|
|
ms = ms * 1000;
|
|
ms = ms + (tv.tv_usec / 1000);
|
|
return(ms);
|
|
}
|
|
|
|
MB_WEAK void mb_port_delay_ms(int tmo_ms)
|
|
{
|
|
usleep((long long)tmo_ms * 1000);
|
|
}
|
|
|
|
#ifdef MB_USING_RTU_BACKEND
|
|
static int mb_port_uart_config(int fd, int baudrate, int databit, int parity, int stopbit)//配置串口, 成功返回0, 失败返回-1
|
|
{
|
|
struct termios options;
|
|
|
|
//获取串口相关参数,该函数还可以测试配置是否正确,该串口是否可用等。
|
|
//若调用成功,函数返回值为0,若调用失败,函数返回值为1.
|
|
if( tcgetattr(fd, &options) != 0)
|
|
{
|
|
//zlog_error(g_zc, "uart(fd=%d) get attr fail.", fd);
|
|
return(-1);
|
|
}
|
|
|
|
//设置串口输入波特率和输出波特率
|
|
int speed = B115200;
|
|
switch(baudrate)
|
|
{
|
|
case 1200:
|
|
speed = B1200;
|
|
break;
|
|
case 2400:
|
|
speed = B2400;
|
|
break;
|
|
case 4800:
|
|
speed = B4800;
|
|
break;
|
|
case 9600:
|
|
speed = B9600;
|
|
break;
|
|
case 19200:
|
|
speed = B19200;
|
|
break;
|
|
case 38400:
|
|
speed = B38400;
|
|
break;
|
|
case 57600:
|
|
speed = B57600;
|
|
break;
|
|
case 115200:
|
|
speed = B115200;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
cfsetispeed(&options, speed);
|
|
cfsetospeed(&options, speed);
|
|
|
|
options.c_cflag |= CLOCAL;//修改控制模式,保证程序不会占用串口
|
|
options.c_cflag |= CREAD;//修改控制模式,使得能够从串口中读取输入数据
|
|
//options.c_cflag &= ~CRTSCTS;//不使用流控制
|
|
|
|
options.c_iflag = 0;
|
|
//options.c_iflag &= ~(IGNCR | ICRNL | INLCR);//不使用回车换行转换
|
|
//options.c_iflag &= ~(IXON | IXOFF | IXANY);//不使用软件流控制
|
|
//options.c_iflag |= IXON | IXOFF | IXANY;//使用软件流控制
|
|
|
|
options.c_cflag &= ~CSIZE;//屏蔽其他标志位
|
|
switch (databit)//设置数据位
|
|
{
|
|
case 5:
|
|
options.c_cflag |= CS5;
|
|
break;
|
|
case 6:
|
|
options.c_cflag |= CS6;
|
|
break;
|
|
case 7:
|
|
options.c_cflag |= CS7;
|
|
break;
|
|
case 8:
|
|
options.c_cflag |= CS8;
|
|
break;
|
|
default:
|
|
options.c_cflag |= CS8;
|
|
break;
|
|
}
|
|
|
|
switch (parity)//设置校验位
|
|
{
|
|
case 0: //无奇偶校验位。
|
|
options.c_cflag &= ~PARENB;
|
|
options.c_iflag &= ~INPCK;
|
|
break;
|
|
case 1://设置为奇校验
|
|
options.c_cflag |= (PARODD | PARENB);
|
|
options.c_iflag |= INPCK;
|
|
break;
|
|
case 2://设置为偶校验
|
|
options.c_cflag |= PARENB;
|
|
options.c_cflag &= ~PARODD;
|
|
options.c_iflag |= INPCK;
|
|
break;
|
|
default://无奇偶校验位。
|
|
options.c_cflag &= ~PARENB;
|
|
options.c_iflag &= ~INPCK;
|
|
break;
|
|
}
|
|
|
|
switch (stopbit)// 设置停止位
|
|
{
|
|
case 1:
|
|
options.c_cflag &= ~CSTOPB;
|
|
break;
|
|
case 2:
|
|
options.c_cflag |= CSTOPB;
|
|
break;
|
|
default:
|
|
options.c_cflag &= ~CSTOPB;
|
|
break;
|
|
}
|
|
|
|
options.c_oflag &= ~OPOST;//关闭输出处理
|
|
options.c_lflag &= ~(ISIG | TCSADRAIN | ICANON | ECHO | ECHOE);//关闭特殊字符信号发送, 关闭规范输入, 关闭回显
|
|
//options.c_lflag &= ~(ISIG | ICANON);//关闭特殊字符信号发送, 关闭规范输入
|
|
|
|
//设置等待时间和最小接收字符
|
|
options.c_cc[VTIME] = 0; /* 读取一个字符等待1*(1/10)s */
|
|
options.c_cc[VMIN] = 0; /* 读取字符的最少个数为1 */
|
|
|
|
//如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
|
|
//tcflush(fd, TCIFLUSH);
|
|
|
|
//激活配置 (将修改后的termios数据设置到串口中)
|
|
//opt : TCSANOW--立即生效, TCSADRAIN--发送完成后生效, TCSAFLUSH--发送完成后生效并清除输入缓冲
|
|
if (tcsetattr(fd, TCSADRAIN, &options) != 0)//发送完成后生效
|
|
{
|
|
LOG_E("uart set attr error!");
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
MB_WEAK void * mb_port_rtu_open(const mb_backend_param_t *param)//打开, 成功返回实例指针或文件标识, 错误返回NULL
|
|
{
|
|
MB_ASSERT(param != NULL);
|
|
MB_ASSERT(param->rtu.dev != NULL);
|
|
|
|
char *path = param->rtu.dev;
|
|
int fd = open(path, O_RDWR|O_NOCTTY|O_NONBLOCK);
|
|
if (fd == -1)
|
|
{
|
|
LOG_E("device (%s) open fail.", path);
|
|
return(NULL);
|
|
}
|
|
|
|
if(fcntl(fd, F_SETFL, 0) < 0)//恢复串口为阻塞状态
|
|
{
|
|
close(fd);
|
|
LOG_E("device (%s) fcntl fail.", path);
|
|
return(NULL);
|
|
}
|
|
if (mb_port_uart_config(fd, param->rtu.baudrate, 8, param->rtu.parity, 1) < 0)
|
|
{
|
|
close(fd);
|
|
LOG_E("device (%s) config fail.", path);
|
|
return(NULL);
|
|
}
|
|
|
|
LOG_D("device (%s, fd=%d) open suceess.", path, fd);
|
|
|
|
return((void *)fd);
|
|
}
|
|
|
|
MB_WEAK int mb_port_rtu_close(void *hinst)//关闭, 成功返回0, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
|
|
int fd = (int)hinst;
|
|
return(close(fd));
|
|
}
|
|
|
|
MB_WEAK int mb_port_rtu_read(void *hinst, u8 *buf, int bufsize)//接收数据, 返回接收到的数据长度, 0表示超时, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
MB_ASSERT(buf != NULL);
|
|
|
|
int fd = (int)hinst;
|
|
int len = read(fd, buf, bufsize);
|
|
if (len < 0)
|
|
{
|
|
LOG_E("device (fd = %d) read error!", fd);
|
|
return(-1);
|
|
}
|
|
return(len);
|
|
}
|
|
|
|
MB_WEAK int mb_port_rtu_write(void *hinst, u8 *buf, int size)//发送数据, , 返回成功发送的数据长度, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
MB_ASSERT(buf != NULL);
|
|
|
|
int fd = (int)hinst;
|
|
int len = write(fd, buf, size);
|
|
if (len <= 0)
|
|
{
|
|
LOG_E("device (fd = %d) write error!", fd);
|
|
return(-1);
|
|
}
|
|
return(len);
|
|
}
|
|
|
|
MB_WEAK int mb_port_rtu_flush(void *hinst)//清空接收缓存, 成功返回0, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
|
|
int fd = (int)hinst;
|
|
return(tcflush(fd, TCIFLUSH));
|
|
}
|
|
#endif
|
|
|
|
#if (defined(MB_USING_TCP_BACKEND) || defined(MB_USING_SOCK_BACKEND))
|
|
MB_WEAK void * mb_port_tcp_open(const mb_backend_param_t *param)//打开, 成功返回实例指针或文件标识, 错误返回NULL
|
|
{
|
|
MB_ASSERT(param != NULL);
|
|
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock < 0)
|
|
{
|
|
LOG_E("socket create fail.");
|
|
return(NULL);
|
|
}
|
|
LOG_D("socket create success, fd = %d.", sock);
|
|
|
|
struct hostent *host = gethostbyname(param->tcp.host);
|
|
if (host == NULL)
|
|
{
|
|
close(sock);
|
|
LOG_E("host get error.");
|
|
return(NULL);
|
|
}
|
|
|
|
struct sockaddr_in srv_addr;
|
|
memset(&srv_addr, 0, sizeof(srv_addr));
|
|
srv_addr.sin_family = AF_INET;
|
|
srv_addr.sin_port = htons(param->tcp.port);
|
|
srv_addr.sin_addr = *((struct in_addr *)host->h_addr);
|
|
if (connect(sock, (struct sockaddr *)&srv_addr, sizeof(struct sockaddr)) < 0)
|
|
{
|
|
close(sock);
|
|
LOG_E("socket connect fail.");
|
|
return(NULL);
|
|
}
|
|
|
|
return((void *)sock);
|
|
}
|
|
|
|
MB_WEAK int mb_port_tcp_close(void *hinst)//关闭, 成功返回0, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
|
|
int sock = (int)hinst;
|
|
return((close(sock) == 0) ? 0 : -1);
|
|
}
|
|
|
|
MB_WEAK int mb_port_tcp_read(void *hinst, u8 *buf, int bufsize)//接收数据, 返回接收到的数据长度, 0表示超时, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
MB_ASSERT(buf != NULL);
|
|
|
|
int sock = (int)hinst;
|
|
int len = recv(sock, buf, bufsize, MSG_DONTWAIT);
|
|
if (len == 0)//socket已关闭
|
|
{
|
|
LOG_E("tcp read error, fd = %d", sock);
|
|
return(-1);
|
|
}
|
|
if (len < 0)//超时或其它错误
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
return(len);
|
|
}
|
|
|
|
MB_WEAK int mb_port_tcp_write(void *hinst, u8 *buf, int size)//发送数据, , 返回成功发送的数据长度, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
MB_ASSERT(buf != NULL);
|
|
|
|
int sock = (int)hinst;
|
|
int len = send(sock, buf, size, 0);
|
|
if (len <= 0)//socket已关闭
|
|
{
|
|
LOG_E("tcp write error, fd = %d", sock);
|
|
return(-1);
|
|
}
|
|
|
|
return(len);
|
|
}
|
|
|
|
MB_WEAK int mb_port_tcp_flush(void *hinst)//清空接收缓存, 成功返回0, 错误返回-1
|
|
{
|
|
MB_ASSERT(hinst != NULL);
|
|
|
|
u8 c;
|
|
int sock = (int)hinst;
|
|
while(1)
|
|
{
|
|
int len = recv(sock, &c, 1, MSG_DONTWAIT);
|
|
if (len == 0)//socket已关闭
|
|
{
|
|
return(-1);
|
|
}
|
|
if (len < 0)//超时或其它错误
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|