#include #include "modbus_lib/modbus-tcp.h" #include "ysp_modbus_slave.h" #include "database/database.h" # include # include #include 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_bits:IO输出状态 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_bits:IO输入状态 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; }