You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

501 lines
14 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <iostream>
#include "modbus_lib/modbus-tcp.h"
#include "ysp_modbus_slave.h"
#include "database/database.h"
# include <winsock2.h>
# include <ws2tcpip.h>
#include <time.h>
using namespace std;
#define REG_ADDR (header_length+1)
#define REG_NUM (header_length+3)
#define REG_VAL (header_length+3)
#define IP_LOCAL_ADDR "192.168.1.21"
#define TCP_PORT 502
#define MODBUS_UART "COM3" //爱尔兰项目用不到 uart modbus
#define SERVER_ID 0x00000001 //默认从机地址
#define SLAVE_REG_ADDR 0x008E //从机modbus 地址的寄存器地址
char slave_ip_addr[20] = { 0 }; //modbus slave tcp ip地址
unsigned short slave_tcp_port = TCP_PORT; //modbus slave tcp port端口号
extern int pro_delay_min;
ysp_modbus_regs_t ysp_modbus_data;
slave_add_t salve_addr = {0};
enum {
TCP,
TCP_PI,
RTU
};
int xSlave_info_init()
{
salve_addr.slave_addr = SERVER_ID;
memset(&ysp_modbus_data, 0, sizeof(ysp_modbus_data));
//memcpy(slave_ip_addr, IP_LOCAL_ADDR, strlen(IP_LOCAL_ADDR));
return 0;
}
//modbus 从机tcp ipaddr
void xSet_slave_ip_addr(const char* ip_addr)
{
if (ip_addr) {
memcpy(slave_ip_addr, ip_addr, strlen(ip_addr));
printf("slave_ip_addr=%s\n", slave_ip_addr);
}
else {
memcpy(slave_ip_addr, IP_LOCAL_ADDR, strlen(IP_LOCAL_ADDR));
printf("SLAVE_IP_ADDR is null,use default\n");
}
}
//modbus从机tcp port
void xSet_slave_port(const char* tcp_port)
{
if (tcp_port) {
slave_tcp_port = atoi(tcp_port);
printf("tcp_port=%s\n", tcp_port);
printf("slave_tcp_port=%d\n", slave_tcp_port);
}
else {
slave_tcp_port = TCP_PORT;
printf("SLAVE_PORT is null,use default\n");
}
}
//创建 TCP Server socket
BYTE CreateTcpServerSock(int *LiSock, DWORD NetPort)
{
int i, tmp, ret, retval;
int len;
struct sockaddr_in addr;
u_long largp = 1L; //非阻塞模式
char tmp_buf[256];
if (-1 < *LiSock)
return *LiSock;
if (NetPort == 0)
return -1;
*LiSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (*LiSock < 0)
{
*LiSock = -1;
return -1;
}
// 将socket设置为非阻塞模式
retval = ioctlsocket(*LiSock, FIONBIO, (u_long FAR*)&largp);
if (SOCKET_ERROR == retval)
{
// retval = WSAGetLastError();
// sprintf(tmp_buf, "ioctlsocket设置非阻塞模式错误, SocketError=%d, SocketId = %d", retval, *LiSock);
// DebugPrint(tmp_buf);
closesocket(*LiSock);
*LiSock = -1;
return -1;
}
///设置socket的输入输出缓冲区大小
tmp = MODBUS_TCP_MAX_ADU_LENGTH;
setsockopt(*LiSock, SOL_SOCKET, SO_RCVBUF, (char*)&tmp, sizeof(tmp));
tmp = MODBUS_TCP_MAX_ADU_LENGTH;
setsockopt(*LiSock, SOL_SOCKET, SO_SNDBUF, (char*)&tmp, sizeof(tmp));
len = 4;
tmp_buf[0] = 1;
tmp = setsockopt(*LiSock, SOL_SOCKET, SO_KEEPALIVE, tmp_buf, len);
tmp = getsockopt(*LiSock, SOL_SOCKET, SO_KEEPALIVE, tmp_buf, &len);
tmp = 1;
// setsockopt(*LiSock, SOL_SOCKET, SO_KEEPALIVE, (char*)&tmp, sizeof(tmp));
setsockopt(*LiSock, SOL_SOCKET, SO_REUSEADDR, (char*)&tmp, sizeof(tmp));
//set_keepalive(*LiSock, keep_alive, keep_idle, keep_interval, keep_count);
//让TCP接收所有连线
memset((char*)&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(NetPort);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
//printf("TIP_(%04d): Port = %d, NetCommIpAddr(%08x), addr.sin_addr.s_addr(%08x)\n", getpid(), commid+1, *NetCommIpAddr, addr.sin_addr.s_addr);
if (bind(*LiSock, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
//printf("errno = %d\n", errno);
//close(*LiSock);
closesocket(*LiSock);
*LiSock = -1;
return -1;
}
//printf("succeed!\n");
ret = listen(*LiSock, 4);
if (ret < 0)
{
//close(*LiSock);
closesocket(*LiSock);
*LiSock = -1;
return -1;
}
return *LiSock;
}
DWORD WINAPI xModbus_msg_ThreadProc(LPVOID lp) {
char* com = (char*)lp;
CHAR rcv_buf[1024] = { 0 };
int s = -1,sockt=-1,extrasock=-1;
int i = 0;
int rc = 0;
modbus_t* ctx = NULL;
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];//接收缓冲区
int header_length;
uint16_t* ptr,*ptr1;
int use_backend=TCP;
uint16_t reg_addr, reg_val, reg_num;
uint16_t tmp = 0;
modbus_mapping_t* mb_mapping;
fd_set fdset_ro, fdset_wr, fdset_ex;
struct timeval timeout;
struct sockaddr_in addr;
socklen_t addrlen;
//struct tcp_info info;
time_t ictime, recvtime;
/*
一次只接受一个主机master连接
即 accept 只接受一个客户端连接该连接错误退出后重新走到这里来accept
*/
if (use_backend == TCP) {
ctx = modbus_new_tcp(slave_ip_addr, slave_tcp_port);
//modbus_set_slave(ctx, SERVER_ID);
}
else if (use_backend == TCP_PI) {
ctx = modbus_new_tcp_pi(slave_ip_addr, "502");
}
else {
ctx = modbus_new_rtu(MODBUS_UART, 115200, 'N', 8, 1);
modbus_set_slave(ctx, SERVER_ID);
}
//modbus_set_indication_timeout(ctx, 4, 0);
header_length = modbus_get_header_length(ctx);
modbus_set_debug(ctx, TRUE);
printf("modbus set debug true\n");
mb_mapping = modbus_mapping_new(0, 0, sizeof(ysp_modbus_regs_t) / 2 + sizeof(slave_add_t) / 2, 0);
printf("total regs nums = %d\n", sizeof(ysp_modbus_regs_t) / 2 + sizeof(slave_add_t) / 2);
if (mb_mapping == NULL) {
fprintf(stderr, "Failed to allocate the mapping: %s\n", modbus_strerror(errno));
modbus_free(ctx);
return -1;
}
for(;;){
if (use_backend == TCP) {
if (s < 0) {
std::cout << "modbus start listen\n" << std::endl;
s = modbus_tcp_listen(ctx, 1);
if (-1 == s) {
std::cout << "modbus tcp listen fial,check ip or port,exit thread\n" << std::endl;
return -1;
}
}
}
else if (use_backend == TCP_PI) {
if (s < 0) {
s = modbus_tcp_pi_listen(ctx, 1);
}
}
for (;;) {
if (use_backend == TCP) {
if (s < 0)
break;
FD_ZERO(&fdset_ro);
FD_ZERO(&fdset_wr);
FD_ZERO(&fdset_ex);
FD_SET(s, &fdset_ro);
FD_SET(s, &fdset_wr);
FD_SET(s, &fdset_ex);
memset((char*)&timeout, 0, sizeof(struct timeval));
if (select(s + 1, &fdset_ro, &fdset_wr, &fdset_ex, &timeout) < 1)
{
Sleep(1);
continue;
}
if (FD_ISSET(s, &fdset_ex))
{
//printf("关闭服务器端口\n");
closesocket(s);
s = -1;
break;
}
else if (FD_ISSET(s, &fdset_ro))
{
std::cout << std::endl << "modbus accept wait connect (hold)\n" << std::endl;
sockt = modbus_tcp_accept(ctx, &s);
std::cout << "modbus accept new connect\n" << std::endl;
}
}
else if (use_backend == TCP_PI) {
modbus_tcp_pi_accept(ctx, &s);
}
ictime = time(NULL);
recvtime = ictime;
std::cout << "modbus receive loop\n" << std::endl;
for (;;) {//循环接受消息
#if 0
if (s < 0)
break;
FD_ZERO(&fdset_ro);
FD_ZERO(&fdset_wr);
FD_ZERO(&fdset_ex);
FD_SET(s, &fdset_ro);
FD_SET(s, &fdset_wr);
FD_SET(s, &fdset_ex);
memset((char*)&timeout, 0, sizeof(struct timeval));
if (select(s + 1, &fdset_ro, &fdset_wr, &fdset_ex, &timeout) < 1)
{
//Sleep(1);
//continue; /* 此时不能continue了*/
}else if (FD_ISSET(s, &fdset_ex))
{
//printf("关闭服务器端口\n");
closesocket(s);
s = -1;
break;
}
else if (FD_ISSET(s, &fdset_ro))// accept(*s, (struct sockaddr *) &addr, &addrlen);
{
//std::cout << std::endl << "second accept wait connect (hold)\n" << std::endl;
extrasock = accept(s, (struct sockaddr *) &addr, &addrlen);
//std::cout << "second accept new connect\n" << std::endl;
if (extrasock < 0)
;
else {
if (sockt < 0) {
sockt = extrasock;
extrasock = -1;
}
else {
closesocket(extrasock);
extrasock = -1;
}
}
}
#endif
if (NULL == ctx)
return 0;
if (0 > sockt)
break;
FD_ZERO(&fdset_ro);
FD_ZERO(&fdset_wr);
FD_ZERO(&fdset_ex);
FD_SET(sockt, &fdset_ro);
FD_SET(sockt, &fdset_wr);
FD_SET(sockt, &fdset_ex);
ictime = time(NULL);
if (ictime - recvtime > pro_delay_min*10)
{
closesocket(sockt);
sockt = -1;
printf("TCP connection timeout of% d seconds, no valid data received", ictime - recvtime);
recvtime = ictime;
break;
}
memset((char*)&timeout, 0, sizeof(struct timeval));
if (select(sockt + 1, &fdset_ro, &fdset_wr, &fdset_ex, &timeout) < 1)
{
Sleep(1);
continue;
}
if (FD_ISSET(sockt, &fdset_ex))
{
//printf("关闭套接字\n");
closesocket(sockt);
sockt = -1;
break;
}
#if 1
if (FD_ISSET(sockt, &fdset_wr))
{
int ierrlen, ierr;
ierrlen = sizeof(ierr);
rc = getsockopt(sockt, SOL_SOCKET, SO_ERROR, (char *)&ierr, &ierrlen);
if (SOCKET_ERROR == rc)
{
closesocket(sockt);
sockt = -1;
break;
}
}
#endif
if (FD_ISSET(sockt, &fdset_ro)) // 可读
{
do {
rc = modbus_receive(ctx, query);
/* Filtered queries return 0 */
} while (rc == 0);//地址错误会返回0,CRC错误会返回-1和errno=EMBBADCRC,正常会返回接收到的字节数
/* The connection is not closed on errors which require on reply such as
bad CRC in RTU. */
if (rc == -1 && errno != EMBBADCRC) {
/* Quit */
closesocket(sockt);
sockt = -1;
std::cout << "error once connect over 1" << std::endl;
break;
}
if (rc == -1)
{
if (errno == EMBBADCRC)
{
std::cout << "bad crc in query" << std::endl;
continue;
}
else
{
closesocket(sockt);
sockt = -1;
std::cout << "parse modbus frame error: %s\n" << std::endl;
std::cout << "error once connect over 2" << std::endl;
break;
}
}
}
else
{
//int len = sizeof(info);
//getsockopt(sockt, IPPROTO_TCP, TCP_INFO, &info, (socklen t *)&len);if (info.tcpi state == TCP CLOSE WAIT && info.tcpi state != TCP ESTABLISHED)
#if 0
rc = recv(sockt, rcv_buf, 1,0);
if (rc <= 0)
{
if (errno == ETIMEDOUT)
{
closesocket(sockt);
sockt = -1;
printf("Client TCP may unexpectedly interrupt!\n");
}
}
#endif
Sleep(5);
continue;
}
recvtime = time(NULL);
std::cout << "modbus new msg\n" << std::endl;
switch (query[header_length])
{
case MODBUS_FC_READ_COILS://0x01 Read Coils in mb_mapping->tab_bitsIO输出状态
printf("unsurported function 2\n");
//否定应答,暂不做数据映射
modbus_reply_exception(ctx, query, MODBUS_EXCEPTION_ILLEGAL_FUNCTION);
break;
case MODBUS_FC_READ_DISCRETE_INPUTS://0x02 Read Discrete Inputs in mb_mapping->tab_input_bitsIO输入状态
printf("unsurported function 2\n");
//否定应答,暂不做数据映射
modbus_reply_exception(ctx, query, MODBUS_EXCEPTION_ILLEGAL_FUNCTION);
continue;
break;
case MODBUS_FC_READ_HOLDING_REGISTERS://0x03 Read Holding Registers in mb_mapping->tab_registers:REG输出
//读寄存器
std::cout << "modbus function 0x03\n" << std::endl;
if (MODBUS_GET_INT16_FROM_INT8(query, REG_ADDR) < (sizeof(ysp_modbus_regs_t) / 2 + sizeof(slave_add_t) / 2))
{
printf("reg vaild\n");
//读取实时数据
if ((MODBUS_GET_INT16_FROM_INT8(query, REG_ADDR) + MODBUS_GET_INT16_FROM_INT8(query, REG_NUM)) > (sizeof(ysp_modbus_regs_t) / 2) + (sizeof(slave_add_t) / 2))
{
printf("reply to this special register address by an exception\n");
//否定应答
modbus_reply_exception(ctx, query, MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS);
continue;
}
printf("update data\n");
//气体数据
if (fill_ysp_data(&ysp_modbus_data)) {
printf("get data from database fail, erase regs\n");
//memset(&ysp_modbus_data, 0, sizeof(&ysp_modbus_data)); 不擦除,回复上次结果
}
printf("time h32:%d\n", ysp_modbus_data.time_H32);
printf("time l32:%d\n", ysp_modbus_data.time_L32);
ptr = (uint16_t*)&ysp_modbus_data;
for (i = 0; i < (sizeof(ysp_modbus_regs_t) / 2); i++)
{
mb_mapping->tab_registers[i] = ptr[i];
}
ptr1 = (uint16_t*)&salve_addr;
mb_mapping->tab_registers[i] = ptr1[1];//i越小越先传输
mb_mapping->tab_registers[i + 1] = ptr1[0];//ptr1[0] 里存的是低地址数据
//mb_mapping->tab_registers[i] =0x3344;
//mb_mapping->tab_registers[i+1] = 0x5566;
}
break;
case MODBUS_FC_READ_INPUT_REGISTERS://0x04 Read Input Registers in mb_mapping->tab_input_registers:REG输入
//可用于读取测量值等不可上位机修改的量,暂不做数据映射
break;
case MODBUS_FC_WRITE_SINGLE_COIL://0x05 Write Single Coil:slave_addr+cmd+reg_addr+[FF 00 or 00 00]+crc corresponding to mb_mapping->tab_bits
//写单个继电器
break;
case MODBUS_FC_WRITE_SINGLE_REGISTER://0x06 Write Single Register:slave_addr+cmd+reg_addr+reg_val+crc corresponding to mb_mapping->tab_registers
//写单个寄存器,暂不做数据映射
printf("function code 0x06\n");
reg_addr = MODBUS_GET_INT16_FROM_INT8(query, REG_ADDR);
reg_val = MODBUS_GET_INT16_FROM_INT8(query, REG_VAL);
printf("reg_addr=%d reg_val=0x%04x\n", reg_addr, reg_val);
if (reg_addr == SLAVE_REG_ADDR)
{
printf("modify slave address succ\n");
salve_addr.slave_addr = reg_val;
}
break;
case MODBUS_FC_WRITE_MULTIPLE_COILS://0x0F Write Multiple Coils:slave_addr+cmd+reg_addr+coil_num+bit_arr+crc corresponding to mb_mapping->tab_bits
//写多个继电器,暂不做数据映射
break;
case MODBUS_FC_WRITE_MULTIPLE_REGISTERS://0x10 Write Multiple Registers:slave_addr+cmd+reg_addr+reg_num+reg_val_arr+crc corresponding to mb_mapping->tab_registers
//写多个寄存器
break;
default://
break;
}
//正常应答
rc = modbus_reply(ctx, query, rc, mb_mapping);
if (rc == -1) {
printf("reply modbus frame error:%s\n", modbus_strerror(errno));
break;
}
}
}
}
return 0;
}
int xModbus_task_init()
{
// 创建线程,返回句柄
int a = 0;
xSlave_info_init();
Sleep(10);
HANDLE h = CreateThread(NULL, 0, xModbus_msg_ThreadProc, NULL, 0, 0);
printf("start Modbus thread\n");
//WaitForSingleObject(h, INFINITE);
//CloseHandle(h);
return 0;
}