diff --git a/Modbus_communication/Modbus_TCP/common.cpp b/Modbus_communication/Modbus_TCP/Interactive.cpp similarity index 58% rename from Modbus_communication/Modbus_TCP/common.cpp rename to Modbus_communication/Modbus_TCP/Interactive.cpp index 9f7ab2f..909eecc 100644 --- a/Modbus_communication/Modbus_TCP/common.cpp +++ b/Modbus_communication/Modbus_TCP/Interactive.cpp @@ -1,66 +1,6 @@ -#include "common.h" +#include "Interactive.h" -/********************************************************************************************* -* 功能    : IP地址有效性检测 -* 描述   : 检测输入的IP地址是否合法 -* 输入 : IP 输入的IP地址 -* 输出 : true IP地址合法 -* false IP地址非法 -*********************************************************************************************/ -bool Check_IP(string ip) -{ - int s[4]; - if (ip.length() < 7 || ip.length() > 15) //长度判定 - return false; - if (sscanf_s(ip.c_str(), "%d.%d.%d.%d", &s[0], &s[1], &s[2], &s[3]) != 4) //IPV4格式正确 - { - return false; - } - string newip = to_string(s[0]) + "." + to_string(s[1]) + "." + to_string(s[2]) + "." + to_string(s[3]); - if (ip != newip) //前导0 - return false; - if ((s[0] & 0xffffff00) || (s[1] & 0xffffff00) || (s[2] & 0xffffff00) || (s[3] & 0xffffff00)) //判断每一段大小是否符合要求 - { - return false; - } - return true; -} - -/********************************************************************************************* -* 功能    : 获取从站IP地址和端口号 -* 描述   : 终端输入从站IP地址和端口号 -* 输入 : IP 地址 (IPV4) -* *Port_number 端口号(1-65535) -* 输出 : 无 -*********************************************************************************************/ -void Input_IP(string& ip, unsigned int *port_number) -{ - int i = 1; - do - { - if (i == 1) - { - cout << "请输入从站IP:"; - i = 0; - } - else - cout << "IP地址格式不正确,请重新输入从站IP:"; - cin >> ip; - } while (!Check_IP(ip)); - - i = 1; - cout << "IP地址输格式入正确,请输入从站端口号:"; - do - { - if (i != 1) - cout << "请重新输入从站端口号:"; - cin >> *port_number; - i = 0; - } while (*port_number == 0 || *port_number > 65535); //端口不能为0 ,端口号范围1---65535 - -} - /********************************************************************************************* * 功能    : 选择功能码 * 描述   : 在功能码 0x01 0x03 0x0F 0x10中选择一个功能码 @@ -218,33 +158,12 @@ unsigned int Count_Write_date_number(int function_code, unsigned int operations_ return write_date_number; } -/********************************************************************************************* -* 功能    : 计算请求报文计算预期响应报文字节数 -* 描述   : 通过对应的功能码和操作数量计算对应的数据字节数 -* 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量 -* 输出 : Respone_len 预期响应的数据字节数 -*********************************************************************************************/ -unsigned int Count_Respone_Len(int function_code, unsigned int operations_number) -{ - unsigned int respone_len = 0; - if (function_code == 0x0F || function_code == 0x10) - return respone_len; - if (function_code == 0x01) //读线圈 - { - respone_len = operations_number / 8; - if (operations_number % 8) - respone_len++; - } - if (function_code == 0x03) - respone_len = operations_number * 2; - return respone_len; -} /********************************************************************************************* * 功能    : 输入写入数据 * 描述   : 根据写入数量和功能码类型输入相应的数据 * 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量 -* 输出 : Write_date 写入的数据 +* 输出 : Write_date 写入的数据 *********************************************************************************************/ string Input_Write_date(int function_code, unsigned int operations_Number) { @@ -253,7 +172,7 @@ string Input_Write_date(int function_code, unsigned int operations_Number) unsigned int write_date_number = Count_Write_date_number(function_code, operations_Number); if (function_code == 0x01 || function_code == 0x03) return write_date;//读线圈/寄存器不需要输入写入数据 - + printf("请输入N组写入数据(2位16进制为一组,空格间隔)例如:00 00 01 00 20 05\n"); do { @@ -272,6 +191,67 @@ string Input_Write_date(int function_code, unsigned int operations_Number) return write_date; } + +/********************************************************************************************* +* 功能    : 打印读取从站的线圈状态 +* 描述   : 对响应报文中的数据进行解析和显示 +* 输入 : Response_Message 响应报文 Request_Message 请求报文 +* 输出 : 无 +*********************************************************************************************/ +void Printf_Coil_date(UINT8 *Response_Message, UINT8 *Request_Message) +{ + printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); + unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); + unsigned int number = ((Request_Message[10] << 8) | Request_Message[11]); + printf("线圈起始地址为%d \n", temp1); + unsigned int temp = temp1; + for (int i = 0; i < Response_Message[8]; i++) + { + unsigned int temp2 = temp + 7; + if (temp2 > temp1 + number - 1) + temp2 = temp1 + number - 1; + printf("线圈%d --- %d的状态为:%02X \n", temp2, temp, Response_Message[9 + i]); + temp = temp + 8; + } +} + +/********************************************************************************************* +* 功能    : 打印读取从站的寄存器状态 +* 描述   : 对响应报文中的数据进行解析和显示 +* 输入 : Response_Message 响应报文 Request_Message 请求报文 +* 输出 : 无 +*********************************************************************************************/ +void Printf_Register_date(UINT8 *Response_Message, UINT8 *Request_Message) +{ + printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); + unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); + printf("寄存器起始地址为%d \n", temp1); + for (int i = 0; i < Response_Message[8]; i = i + 2) + { + printf("寄存器%d的值为:%02X %02X \n", temp1++, Response_Message[9 + i], Response_Message[10 + i]); + } +} + +/********************************************************************************************* +* 功能    : 打印读取异常响应报文信息 +* 描述   : 对响应报文中的数据进行解析和显示 +* 输入 : Response_Message 响应报文 +* 输出 : 无 +*********************************************************************************************/ +void Printf_Anomaly_date(UINT8 *Response_Message) +{ + UINT8 anomaly_code = Response_Message[8]; + printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); + switch (anomaly_code) + { + case 0x01: printf("%02X : 从站设备不支持此功能码\n", anomaly_code); break; + case 0x02: printf("%02X : 指定的数据地址在从站设备中不存在\n", anomaly_code); break; + case 0x03: printf("%02X : 指定的数据超过范围或者不允许使用\n", anomaly_code); break; + case 0x04: printf("%02X : 从站设备处理响应的过程中,出现未知错误等\n", anomaly_code); break; + default: printf("Unkown Other Error!!!!!\n"); + } +} + /********************************************************************************************* * 功能    : 日志记录 * 描述   : 记录每次通信的请求和响应报文 @@ -288,14 +268,14 @@ void Log_Note(UINT8 *Message, int flage, int message_len) p = gmtime(&timep); string recv_str = to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Recv:"; - string send_str= to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) + string send_str = to_string(1900 + p->tm_year) + "-" + to_string(1 + p->tm_mon) + "-" + to_string(p->tm_mday) + " " + to_string(8 + p->tm_hour) + ":" + to_string(p->tm_min) + ":" + to_string(p->tm_sec) + " Send:"; /* 打开文件用于读写 */ if ((fp = fopen("Modbus_TCP_log.txt", "a+")) == NULL) { printf("打开文件失败"); - return ; + return; } if (flage == 1) { @@ -304,7 +284,7 @@ void Log_Note(UINT8 *Message, int flage, int message_len) { fprintf(fp, "%02X ", Message[i]); } - fprintf(fp,"\n"); + fprintf(fp, "\n"); } else { @@ -319,136 +299,46 @@ void Log_Note(UINT8 *Message, int flage, int message_len) fp = NULL; } -/********************************************************************************************* -* 功能    : socket版本 -* 描述   : 启动socket服务 -* 输入 : 无 -* 输出 : true 启动成功 -* false 启动失败 -*********************************************************************************************/ -bool InitSocket_Version(void) -{ - WORD sockVersion = MAKEWORD(2, 2);//使用winsocket2.2版本 - WSADATA wsaData; - if (WSAStartup(sockVersion, &wsaData) != 0) - { - return false; - } - return true; -} - -/********************************************************************************************* -* 功能    : 初始化客户端 -* 描述   : 根据终端输入从站IP地址和端口号连接对应的服务器,设置超时时间 -* 输入 : 无 -* 输出 : ClientSocket 连接成功的套接字 -*********************************************************************************************/ -SOCKET Init_Client(void) +void Printf_Message(UINT8 *Message, int flage, int message_len) { - if (InitSocket_Version() == 0) - { - printf("启动Socket服务失败,请检查Socket版本"); - return INVALID_SOCKET; - } - string ip_address; - unsigned int port_number; - Input_IP(ip_address, &port_number); - SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0); - sockaddr_in serversock_in; - serversock_in.sin_addr.S_un.S_addr = inet_addr(ip_address.c_str()); - serversock_in.sin_family = AF_INET; - serversock_in.sin_port = htons(port_number); - if (SOCKET_ERROR == connect(clientSocket, (SOCKADDR*)&serversock_in, sizeof(SOCKADDR))) + if (flage == 1) + printf("主站请求 :"); + else + printf("从站响应 :"); + for (int i = 0; i < message_len; i++) { - cout << "尝试连接TCP从站失败" << endl; - return INVALID_SOCKET; + printf("%02x ", Message[i]); } - cout << "连接TCP从站成功" << endl; - TIMEVAL timeout; - timeout.tv_sec = 200; //ms - timeout.tv_usec = 0; //us - setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));//设置接收超时时间 - return clientSocket; -} - -void Clear_buf(SOCKET clientSocket) -{ - unsigned long bytesToRecv; - char temp[500]; - do - { - ioctlsocket(clientSocket, FIONREAD, &bytesToRecv); - if (bytesToRecv != 0)//不等于0时进行清理操作 - { - if (bytesToRecv > 500) - { - recv(clientSocket, temp, 500, 0); - } - else - recv(clientSocket, temp, bytesToRecv, 0); - } - } while (bytesToRecv != 0); - + printf("\n"); + if (LOG_NOTE_SWITCH) + Log_Note(Message, flage, message_len); } -bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len) +void Printf_Analysis_Outcome(UINT8 *Response_Message, UINT8 *Request_Message) { - Clear_buf(clientSocket); //清理缓冲区 - int status = send(clientSocket, (char*)Request_Message, request_message_len, 0); - if (status == (-1)) + switch (Response_Message[7]) { - return false; + case 0x01:Printf_Coil_date(Response_Message, Request_Message); break; + case 0x03:Printf_Register_date(Response_Message, Request_Message); break; + case 0x0F:printf("成功写入从站线圈%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; + case 0x10:printf("成功写入从站寄存器%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; + default: printf("Other Error"); } - return true; -} - -bool Abnormal_Connection(SOCKET *clientSocket) -{ - printf("连接异常,请检查连接状态。\n"); - printf("**************************** Press Enter To Contioun ****************************\n"); - getchar(); - system("cls"); - closesocket(*clientSocket); - WSACleanup(); - printf("是否重新连接服务器: 1 重新连接 0 关闭本软件\n"); - int flage = 0; - do - { - cin >> flage; - cin.clear(); - cin.sync(); - } while (!(flage == 0 || flage == 1)); - if (flage == 0) - return false; - system("cls"); - do - { - *clientSocket = Init_Client(); - } while (INVALID_SOCKET == *clientSocket); - system("cls"); - return true; } -int Recv_date(SOCKET clientSocket, UINT8 *Response_Message) +void Printf_Interactive_Data(UINT8 *Response_Message, UINT8 *Request_Message, int flage, int request_message_len, int response_message_len) { - int Response_Message_len = recv(clientSocket, (char*)Response_Message, 600, 0); - if (Response_Message_len > 0) + Printf_Message(Request_Message, 1, request_message_len); + Printf_Message(Response_Message, 0, response_message_len); + switch (flage) { - return Response_Message_len; + case MSG_LEN_ERROR: printf("Message Length Error"); break; + case MABP_ERROR:printf("MBAP Date Error"); break; + case FUNCTION_CODE_ERROR:printf("Function Code Error"); break; + case START_ADDRESS_ERROR:printf("Start Address Error"); break; + case OPERATION_NUMBER_ERROR:printf("Operation Number Error"); break; + case NORMAL_RESPONSE: Printf_Analysis_Outcome(Response_Message, Request_Message); break; + case ABNORMAL_RESPONSE:Printf_Anomaly_date(Response_Message); break; + default:printf("Other Error"); } - return 0; -} - -bool Test_Connection_status(SOCKET clientSocket) -{ - TIMEVAL timeout; - timeout.tv_sec = 0; - timeout.tv_usec = 0; - fd_set reads; - FD_ZERO(&reads); - FD_SET(clientSocket, &reads); - if (select(0, &reads, 0, 0, &timeout)) - return false; - else - return true; } \ No newline at end of file diff --git a/Modbus_communication/Modbus_TCP/common.h b/Modbus_communication/Modbus_TCP/Interactive.h similarity index 50% rename from Modbus_communication/Modbus_TCP/common.h rename to Modbus_communication/Modbus_TCP/Interactive.h index 099a6e5..2bdc266 100644 --- a/Modbus_communication/Modbus_TCP/common.h +++ b/Modbus_communication/Modbus_TCP/Interactive.h @@ -1,20 +1,27 @@ -#ifndef __COMMON_H -#define __COMMON_H +#ifndef __INTERACTIVE_H +#define __INTERACTIVE_H + #include -#include -#include +#include #include #include -#include #include -#pragma comment(lib,"ws2_32.lib") +#include using namespace std; -#define DEVICE_ID 0x01 //豸ID -#define LOG_NOTE_SWITCH 1 //־¼λ +#define LOG_NOTE_SWITCH 1 //־¼λ +enum Response_Type{ + MSG_LEN_ERROR, + MABP_ERROR, + FUNCTION_CODE_ERROR, + START_ADDRESS_ERROR, + OPERATION_NUMBER_ERROR, + NORMAL_RESPONSE, + ABNORMAL_RESPONSE +}; int Input_Function_code(void); unsigned int Input_Starting_address(void); @@ -22,13 +29,7 @@ unsigned int Input_Operations_number(int function_code); unsigned int Count_Write_date_number(int function_code, unsigned int operations_Number); string Input_Write_date(int function_code, unsigned int operations_Number); void Log_Note(UINT8 *Message, int flage, int message_len); - -unsigned int Count_Respone_Len(int function_code, unsigned int operations_number); -SOCKET Init_Client(void); -bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len); -bool Abnormal_Connection(SOCKET *clientSocket); -int Recv_date(SOCKET clientSocket, UINT8 *Response_Message); -bool Test_Connection_status(SOCKET clientSocket); +void Printf_Interactive_Data(UINT8 *Response_Message, UINT8 *Request_Message, int flage, int request_message_len, int response_message_len); #endif \ No newline at end of file diff --git a/Modbus_communication/Modbus_TCP/Modbus_Master.cpp b/Modbus_communication/Modbus_TCP/Modbus_Master.cpp new file mode 100644 index 0000000..cf37a57 --- /dev/null +++ b/Modbus_communication/Modbus_TCP/Modbus_Master.cpp @@ -0,0 +1,235 @@ +#include "Modbus_Master.h" +UINT16 Transmission_Indicator = 0x00; //事务号 + +/********************************************************************************************* +* 功能    : 计算请求报文计算预期响应报文字节数 +* 描述   : 通过对应的功能码和操作数量计算对应的数据字节数 +* 输入 : Function_code 选择的功能码类型 Operations_Number对应功能码类型的操作数量 +* 输出 : Respone_len 预期响应的数据字节数 +*********************************************************************************************/ +unsigned int Count_Respone_Len(int function_code, unsigned int operations_number) +{ + unsigned int respone_len = 0; + if (function_code == 0x0F || function_code == 0x10) + return respone_len; + if (function_code == 0x01) //读线圈 + { + respone_len = operations_number / 8; + if (operations_number % 8) + respone_len++; + } + if (function_code == 0x03) + respone_len = operations_number * 2; + return respone_len; +} +/********************************************************************************************* +* 功能    : 检测响应报文长度 +* 描述   : 对响应报文中存放长度字节进行判断和请求报文对比 +* 输入 : Response_Message 响应报文 Request_Message 请求报文 Response_Message_len 接收到的数据长度 +* 输出 : true 长度正常 false 长度异常 +*********************************************************************************************/ +bool Check_Response_Message_len(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) +{ + if ((response_message_len - 6) != Response_Message[5]) + return false; + unsigned int operations_Number = Request_Message[10] << 8 | Request_Message[11]; + unsigned int respone_Len = Count_Respone_Len(Request_Message[7], operations_Number); + if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) + if (response_message_len != 12) + return false; + if (Request_Message[7] == 0x01 || Request_Message[7] == 0x03) + { + if (respone_Len + 9 != response_message_len) + return false; + if (respone_Len != Response_Message[8]) + return false; + } + return true; +} + +/********************************************************************************************* +* 功能    : 判断响应报文是否可以正常解析 +* 描述   : 通过长度和异常码等判定该响应报文是否可以解析 +* 输入 : Response_Message 响应报文 Request_Message请求报文 Response_Message_len 响应报文长度 +* 输出 : 响应数据类型 +*********************************************************************************************/ +int Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) +{ + if (response_message_len < 9 || response_message_len > 260) + return MSG_LEN_ERROR; + for (int i = 0; i < 7; i++) + { + if (i == 4 || i == 5) //后续字节长度 + continue; + if (Response_Message[i] != Request_Message[i]) //0 1 传输标识,2 3 协议标识,6设备ID + return MABP_ERROR; //一旦不一致,判定为异常报文数据 + } + if (Response_Message[4] != 0x00) //4 固定0x00 + return MABP_ERROR; + + if (Response_Message[7] == Request_Message[7] + 0x80)//先处理异常响应 + { + if (response_message_len == 9) + { + return ABNORMAL_RESPONSE; + } + else + return MSG_LEN_ERROR; + } + if (Response_Message[7] != Request_Message[7]) //功能码判断 + return FUNCTION_CODE_ERROR; + if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) //0F 10 地址和操作数量判定 + { + for (int j = 8; j < 10; j++) + { + if (Request_Message[j] != Response_Message[j]) + return START_ADDRESS_ERROR; + } + for (int j = 10; j < 12; j++) + { + if (Request_Message[j] != Response_Message[j]) + return OPERATION_NUMBER_ERROR; + } + } + if (!Check_Response_Message_len(Response_Message, Request_Message, response_message_len)) + return MSG_LEN_ERROR; + return NORMAL_RESPONSE; +} + +/********************************************************************************************* +* 功能    : 生成MBAP报头 +* 描述 : MBAP报文头的包括的内容: +* +-------------+---------+--------+--------+------------------------------+ +* | 域 | 长度 | 客户机 | 服务器 | 描述 | +* +-------------+---------+--------+--------+------------------------------+ +* |事务元标识符 | 2个字节 | 启动 | 复制 |请求/响应事务处理的识别码 | +* +-------------+---------+--------+--------+------------------------------+ +* |协议标识符 | 2个字节 | 启动 | 复制 |0=MODBUS 协议 | +* +-------------+---------+--------+--------+------------------------------+ +* | 长度 | 2个字节 | 启动 | 启动 |以下字节的数量 | +* +-------------+---------+--------+--------+------------------------------+ +* |单元标识符 | 1个字节 | 启动 | 复制 |连接的远程从站的识别码 | +* 输入 : 无 +* 输出 : MBAP报文头内容 +*********************************************************************************************/ +void Create_MBAP(UINT8 *Message, int function_code, unsigned int operations_number) +{ + Message[0] = Transmission_Indicator >> 8;//事务号 + Message[1] = (UINT8)Transmission_Indicator; + Transmission_Indicator++; + Message[2] = 0x00; //Modbus协议标识 + Message[3] = 0x00; + Message[4] = 0x00;//后续字节长度 + Message[5] = Count_Write_date_number(function_code, operations_number) + 0x06; + if (function_code == 0x0F || function_code == 0x10)//0f/10功能码后续字节数多1 + { + Message[5] = Message[5] + 1; + } + Message[6] = DEVICE_ID; +} + +/********************************************************************************************* +* 功能    : 字符串转UINT8类型 +* 描述   : 根据写入数量和功能码类型输入相应的数据 +* 输入 : *Message 消息帧存放的数组 Write_date 要转换的字符串 +* Message_len消息帧数组的起始位置 +* 输出 : Message_len 消息帧的长度 +*********************************************************************************************/ +int HexStringtoByte(UINT8 *Message, string write_date, int message_len, int function_code, unsigned int operations_Number) +{ + if (write_date.length() == 0) + return message_len; + Message[message_len] = Count_Write_date_number(function_code, operations_Number); + message_len++; + const char *b = write_date.c_str(); + int temp = 0; + for (unsigned int i = 0; i + 3 < write_date.length(); i = i + 3) + { + sscanf_s(b + i, "%02X", &temp); + Message[message_len] = (UINT8)temp; + message_len++; + } + sscanf_s(b + write_date.length() - 2, "%02X", &temp); + Message[message_len] = (UINT8)temp; + return ++message_len; +} + +/********************************************************************************************* +* 功能    : 生成TCP模式下的消息帧 +* 描述   : 根据要写入的数据生成消息帧 +* 输入 : *Message 消息帧存放的数组 Write_date 写入数据的字符串 +* Function_code 功能码 Operations_Number 操作数量 Starting_address起始地址 +* 输出 : 消息帧的总长度 +*********************************************************************************************/ +int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date) +{ + + Create_MBAP(Message, function_code, operations_Number); + Message[7] = function_code;//功能码 + Message[8] = starting_address >> 8;//起始地址H + Message[9] = starting_address;//起始地址L + Message[10] = operations_Number >> 8;//操作数量H + Message[11] = operations_Number;//操作数量L + return HexStringtoByte(Message, write_date, 12, function_code, operations_Number); + +} + +/********************************************************************************************* +* 功能    : 运行客户端 +* 描述   : 初始化并且生成发送请求等待响应 +* 输入 : 无 +* 输出 : 无 +*********************************************************************************************/ +int Tcp_Client() +{ + SOCKET clientSocket; + UINT8 Request_Message[600]; + memset(Request_Message, 0, 600); + UINT8 Response_Message[600]; + memset(Response_Message, 0, 600); + + do + { + clientSocket = Init_Client(); + } while (INVALID_SOCKET == clientSocket); + + while (true) + { + int function_code = Input_Function_code();//交互 + unsigned int operations_Number = Input_Operations_number(function_code); + unsigned int starting_address = Input_Starting_address(); + string write_date = Input_Write_date(function_code, operations_Number); + int request_message_len = Create_TCP_Message(Request_Message, function_code, operations_Number, starting_address, write_date);//生成 + if (!Send_date(clientSocket, Request_Message, request_message_len)) //发送 + { + if (Abnormal_Connection(&clientSocket)) + continue; + else + break; + } + int response_message_len = Recv_date(clientSocket, Response_Message);//接收 + if (response_message_len) + { + int flage = Analysis_Response_Message(Response_Message, Request_Message, response_message_len); + Printf_Interactive_Data(Response_Message, Request_Message, flage, request_message_len, response_message_len); + } + else if(!Test_Connection_status(clientSocket)) + { + if (Abnormal_Connection(&clientSocket)) + continue; + else + break; + } + else + printf("响应超时\n"); + + printf("**************************** Press Enter To Contioun ****************************\n"); + getchar(); + system("cls"); + } + //关闭套接字 + closesocket(clientSocket); + //关闭服务 + WSACleanup(); + return 0; +} diff --git a/Modbus_communication/Modbus_TCP/Modbus_Master.h b/Modbus_communication/Modbus_TCP/Modbus_Master.h new file mode 100644 index 0000000..bad494b --- /dev/null +++ b/Modbus_communication/Modbus_TCP/Modbus_Master.h @@ -0,0 +1,12 @@ +#ifndef __MODBUS_MASTER_H +#define __MODBUS_MASTER_H + +#include "TCP_client.h" +#include "Interactive.h" +#define DEVICE_ID 0x01 //豸ID + +int Tcp_Client(); +int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date); +int Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len); + +#endif \ No newline at end of file diff --git a/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj b/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj index 6d25ad6..f6ff198 100644 --- a/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj +++ b/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj @@ -66,14 +66,16 @@ - - + + + - - + + + diff --git a/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj.filters b/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj.filters index cd74da4..d7e9883 100644 --- a/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj.filters +++ b/Modbus_communication/Modbus_TCP/Modbus_TCP.vcxproj.filters @@ -18,7 +18,10 @@ 源文件 - + + 源文件 + + 源文件 @@ -26,13 +29,16 @@ - + 头文件 - + 头文件 - + + 头文件 + + 头文件 diff --git a/Modbus_communication/Modbus_TCP/TCP_client.cpp b/Modbus_communication/Modbus_TCP/TCP_client.cpp index 037c6ca..ffd9b32 100644 --- a/Modbus_communication/Modbus_TCP/TCP_client.cpp +++ b/Modbus_communication/Modbus_TCP/TCP_client.cpp @@ -1,303 +1,196 @@ #include "TCP_client.h" -UINT16 Transmission_Indicator = 0x00; //事务号 -/********************************************************************************************* -* 功能    : 打印读取从站的线圈状态 -* 描述   : 对响应报文中的数据进行解析和显示 -* 输入 : Response_Message 响应报文 Request_Message 请求报文 -* 输出 : 无 -*********************************************************************************************/ -void Printf_Coil_date(UINT8 *Response_Message, UINT8 *Request_Message) -{ - printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); - unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); - unsigned int number = ((Request_Message[10] << 8) | Request_Message[11]); - printf("线圈起始地址为%d \n",temp1); - unsigned int temp = temp1; - for (int i = 0; i < Response_Message[8]; i++) - { - unsigned int temp2 = temp + 7; - if (temp2 > temp1 + number - 1) - temp2 = temp1 + number - 1; - printf("线圈%d --- %d的状态为:%02X \n", temp2, temp, Response_Message[9+i]); - temp = temp + 8; - } -} -/********************************************************************************************* -* 功能    : 打印读取从站的寄存器状态 -* 描述   : 对响应报文中的数据进行解析和显示 -* 输入 : Response_Message 响应报文 Request_Message 请求报文 -* 输出 : 无 -*********************************************************************************************/ -void Printf_Register_date(UINT8 *Response_Message, UINT8 *Request_Message) -{ - printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); - unsigned int temp1 = ((Request_Message[8] << 8) | Request_Message[9]); - printf("寄存器起始地址为%d \n", temp1); - for (int i = 0; i < Response_Message[8]; i = i + 2) - { - printf("寄存器%d的值为:%02X %02X \n", temp1++, Response_Message[9 + i], Response_Message[10+i]); - } -} /********************************************************************************************* -* 功能    : 打印读取异常响应报文信息 -* 描述   : 对响应报文中的数据进行解析和显示 -* 输入 : Response_Message 响应报文 -* 输出 : 无 +* 功能    : IP地址有效性检测 +* 描述   : 检测输入的IP地址是否合法 +* 输入 : IP 输入的IP地址 +* 输出 : true IP地址合法 +* false IP地址非法 *********************************************************************************************/ -void Printf_Anomaly_date(UINT8 *Response_Message) +bool Check_IP(string ip) { - UINT8 anomaly_code = Response_Message[8]; - printf("从站设备ID %02X 功能码为 %02X\n", Response_Message[6], Response_Message[7]); - switch (anomaly_code) + int s[4]; + if (ip.length() < 7 || ip.length() > 15) //长度判定 + return false; + if (sscanf_s(ip.c_str(), "%d.%d.%d.%d", &s[0], &s[1], &s[2], &s[3]) != 4) //IPV4格式正确 { - case 0x01: printf("%02X : 从站设备不支持此功能码\n", anomaly_code); break; - case 0x02: printf("%02X : 指定的数据地址在从站设备中不存在\n", anomaly_code); break; - case 0x03: printf("%02X : 指定的数据超过范围或者不允许使用\n", anomaly_code); break; - case 0x04: printf("%02X : 从站设备处理响应的过程中,出现未知错误等\n", anomaly_code); break; - default: printf("Unkown Other Error!!!!!\n"); + return false; } -} - -/********************************************************************************************* -* 功能    : 检测响应报文长度 -* 描述   : 对响应报文中存放长度字节进行判断和请求报文对比 -* 输入 : Response_Message 响应报文 Request_Message 请求报文 Response_Message_len 接收到的数据长度 -* 输出 : true 长度正常 false 长度异常 -*********************************************************************************************/ -bool Check_Response_Message_len(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) -{ - if ((response_message_len - 6) != Response_Message[5]) + string newip = to_string(s[0]) + "." + to_string(s[1]) + "." + to_string(s[2]) + "." + to_string(s[3]); + if (ip != newip) //前导0 return false; - unsigned int operations_Number = Request_Message[10] << 8 | Request_Message[11]; - unsigned int respone_Len = Count_Respone_Len(Request_Message[7], operations_Number); - if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) - if (response_message_len != 12) - return false; - if (Request_Message[7] == 0x01 || Request_Message[7] == 0x03) + if ((s[0] & 0xffffff00) || (s[1] & 0xffffff00) || (s[2] & 0xffffff00) || (s[3] & 0xffffff00)) //判断每一段大小是否符合要求 { - if (respone_Len + 9 != response_message_len) - return false; - if (respone_Len != Response_Message[8]) - return false; + return false; } return true; } -bool Printf_Analysis_Outcome(UINT8 *Response_Message, UINT8 *Request_Message) -{ - switch (Response_Message[7]) - { - case 0x01:Printf_Coil_date(Response_Message, Request_Message); break; - case 0x03:Printf_Register_date(Response_Message, Request_Message); break; - case 0x0F:printf("成功写入从站线圈%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; - case 0x10:printf("成功写入从站寄存器%d个\n", ((Request_Message[10] << 8) | Request_Message[11])); break; - default: return false; - } - return true; -} /********************************************************************************************* -* 功能    : 判断响应报文是否可以正常解析 -* 描述   : 通过长度和异常码等判定该响应报文是否可以解析 -* 输入 : Response_Message 响应报文 Request_Message请求报文 Response_Message_len 响应报文长度 -* 输出 : true可以正常解析 false 无法解析 +* 功能    : 获取从站IP地址和端口号 +* 描述   : 终端输入从站IP地址和端口号 +* 输入 : IP 地址 (IPV4) +* *Port_number 端口号(1-65535) +* 输出 : 无 *********************************************************************************************/ -bool Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len) +void Input_IP(string& ip, unsigned int *port_number) { - if (response_message_len < 9 || response_message_len > 260) - return false; - for (int i = 0; i < 7; i++) - { - if (i == 4 || i == 5) //后续字节长度 - continue; - if (Response_Message[i] != Request_Message[i]) //0 1 传输标识,2 3 协议标识,6设备ID - return false; //一旦不一致,判定为异常报文数据 - } - if (Response_Message[4] != 0x00) //4 固定0x00 - return false; - - if (Response_Message[7] == Request_Message[7] + 0x80)//先处理异常响应 + int i = 1; + do { - if (response_message_len == 9) + if (i == 1) { - Printf_Anomaly_date(Response_Message); - return true; + cout << "请输入从站IP:"; + i = 0; } else - return false; - } - if (Response_Message[7] != Request_Message[7]) //功能码判断 - return false; - if (Request_Message[7] == 0x0F || Request_Message[7] == 0x10) //0F 10 地址和操作数量判定 - { - for (int j = 8; j < 12; j++) - { - if (Request_Message[j] != Response_Message[j]) - return false; - } - } - if (!Check_Response_Message_len(Response_Message, Request_Message, response_message_len)) - return false; - //打印解析结果 - return Printf_Analysis_Outcome(Response_Message, Request_Message); -} - + cout << "IP地址格式不正确,请重新输入从站IP:"; + cin >> ip; + } while (!Check_IP(ip)); -void Printf_Message(UINT8 *Message,int flage, int message_len) -{ - if (flage == 1) - printf("主站请求 :"); - else - printf("从站响应 :"); - for (int i = 0; i < message_len; i++) + i = 1; + cout << "IP地址输格式入正确,请输入从站端口号:"; + do { - printf("%02x ", Message[i]); - } - printf("\n"); - if (LOG_NOTE_SWITCH) - Log_Note(Message, flage, message_len); -} - + if (i != 1) + cout << "请重新输入从站端口号:"; + cin >> *port_number; + i = 0; + } while (*port_number == 0 || *port_number > 65535); //端口不能为0 ,端口号范围1---65535 +} /********************************************************************************************* -* 功能    : 生成MBAP报头 -* 描述 : MBAP报文头的包括的内容: -* +-------------+---------+--------+--------+------------------------------+ -* | 域 | 长度 | 客户机 | 服务器 | 描述 | -* +-------------+---------+--------+--------+------------------------------+ -* |事务元标识符 | 2个字节 | 启动 | 复制 |请求/响应事务处理的识别码 | -* +-------------+---------+--------+--------+------------------------------+ -* |协议标识符 | 2个字节 | 启动 | 复制 |0=MODBUS 协议 | -* +-------------+---------+--------+--------+------------------------------+ -* | 长度 | 2个字节 | 启动 | 启动 |以下字节的数量 | -* +-------------+---------+--------+--------+------------------------------+ -* |单元标识符 | 1个字节 | 启动 | 复制 |连接的远程从站的识别码 | +* 功能    : socket版本 +* 描述   : 启动socket服务 * 输入 : 无 -* 输出 : MBAP报文头内容 +* 输出 : true 启动成功 +* false 启动失败 *********************************************************************************************/ -void Create_MBAP(UINT8 *Message, int function_code, unsigned int operations_number) +bool InitSocket_Version(void) { - Message[0] = Transmission_Indicator >> 8;//事务号 - Message[1] = (UINT8)Transmission_Indicator; - Transmission_Indicator++; - Message[2] = 0x00; //Modbus协议标识 - Message[3] = 0x00; - Message[4] = 0x00;//后续字节长度 - Message[5] = Count_Write_date_number(function_code, operations_number) + 0x06; - if (function_code == 0x0F || function_code == 0x10)//0f/10功能码后续字节数多1 + WORD sockVersion = MAKEWORD(2, 2);//使用winsocket2.2版本 + WSADATA wsaData; + if (WSAStartup(sockVersion, &wsaData) != 0) { - Message[5] = Message[5] + 1; + return false; } - Message[6] = DEVICE_ID; + return true; } /********************************************************************************************* -* 功能    : 字符串转UINT8类型 -* 描述   : 根据写入数量和功能码类型输入相应的数据 -* 输入 : *Message 消息帧存放的数组 Write_date 要转换的字符串 -* Message_len消息帧数组的起始位置 -* 输出 : Message_len 消息帧的长度 +* 功能    : 初始化客户端 +* 描述   : 根据终端输入从站IP地址和端口号连接对应的服务器,设置超时时间 +* 输入 : 无 +* 输出 : ClientSocket 连接成功的套接字 *********************************************************************************************/ -int HexStringtoByte(UINT8 *Message, string write_date, int message_len, int function_code, unsigned int operations_Number) +SOCKET Init_Client(void) { - if (write_date.length() == 0) - return message_len; - Message[message_len] = Count_Write_date_number(function_code, operations_Number); - message_len++; - const char *b = write_date.c_str(); - int temp = 0; - for (unsigned int i = 0; i + 3 < write_date.length(); i = i + 3) + if (InitSocket_Version() == 0) + { + printf("启动Socket服务失败,请检查Socket版本"); + return INVALID_SOCKET; + } + string ip_address; + unsigned int port_number; + Input_IP(ip_address, &port_number); + SOCKET clientSocket = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in serversock_in; + serversock_in.sin_addr.S_un.S_addr = inet_addr(ip_address.c_str()); + serversock_in.sin_family = AF_INET; + serversock_in.sin_port = htons(port_number); + if (SOCKET_ERROR == connect(clientSocket, (SOCKADDR*)&serversock_in, sizeof(SOCKADDR))) { - sscanf_s(b + i, "%02X", &temp); - Message[message_len] = (UINT8)temp; - message_len++; + cout << "尝试连接TCP从站失败" << endl; + return INVALID_SOCKET; } - sscanf_s(b + write_date.length() - 2, "%02X", &temp); - Message[message_len] = (UINT8)temp; - return ++message_len; + cout << "连接TCP从站成功" << endl; + TIMEVAL timeout; + timeout.tv_sec = 200; //ms + timeout.tv_usec = 0; //us + setsockopt(clientSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));//设置接收超时时间 + return clientSocket; } -/********************************************************************************************* -* 功能    : 生成TCP模式下的消息帧 -* 描述   : 根据要写入的数据生成消息帧 -* 输入 : *Message 消息帧存放的数组 Write_date 写入数据的字符串 -* Function_code 功能码 Operations_Number 操作数量 Starting_address起始地址 -* 输出 : 消息帧的总长度 -*********************************************************************************************/ -int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date) +void Clear_buf(SOCKET clientSocket) { - - Create_MBAP(Message, function_code, operations_Number); - Message[7] = function_code;//功能码 - Message[8] = starting_address >> 8;//起始地址H - Message[9] = starting_address;//起始地址L - Message[10] = operations_Number >> 8;//操作数量H - Message[11] = operations_Number;//操作数量L - - return HexStringtoByte(Message, write_date, 12, function_code, operations_Number); + unsigned long bytesToRecv; + char temp[500]; + do + { + ioctlsocket(clientSocket, FIONREAD, &bytesToRecv); + if (bytesToRecv != 0)//不等于0时进行清理操作 + { + if (bytesToRecv > 500) + { + recv(clientSocket, temp, 500, 0); + } + else + recv(clientSocket, temp, bytesToRecv, 0); + } + } while (bytesToRecv != 0); } -/********************************************************************************************* -* 功能    : 运行客户端 -* 描述   : 初始化并且生成发送请求等待响应 -* 输入 : 无 -* 输出 : 无 -*********************************************************************************************/ -int Tcp_Client() +bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len) { - SOCKET clientSocket; - UINT8 Request_Message[600]; - memset(Request_Message, 0, 600); - UINT8 Response_Message[600]; - memset(Response_Message, 0, 600); + Clear_buf(clientSocket); //清理缓冲区 + int status = send(clientSocket, (char*)Request_Message, request_message_len, 0); + if (status == (-1)) + { + return false; + } + return true; +} +bool Abnormal_Connection(SOCKET *clientSocket) +{ + printf("连接异常,请检查连接状态。\n"); + printf("**************************** Press Enter To Contioun ****************************\n"); + getchar(); + system("cls"); + closesocket(*clientSocket); + WSACleanup(); + printf("是否重新连接服务器: 1 重新连接 0 关闭本软件\n"); + int flage = 0; do { - clientSocket = Init_Client(); - } while (INVALID_SOCKET == clientSocket); - - while (true) + cin >> flage; + cin.clear(); + cin.sync(); + } while (!(flage == 0 || flage == 1)); + if (flage == 0) + return false; + system("cls"); + do { - int function_code = Input_Function_code();//交互 - unsigned int operations_Number = Input_Operations_number(function_code); - unsigned int starting_address = Input_Starting_address(); - string write_date = Input_Write_date(function_code, operations_Number); - int request_message_len = Create_TCP_Message(Request_Message, function_code, operations_Number, starting_address, write_date);//生成 - if (!Send_date(clientSocket, Request_Message, request_message_len)) //发送 - { - if (Abnormal_Connection(&clientSocket)) - continue; - else - break; - } - int Response_Message_len = Recv_date(clientSocket, Response_Message);//接收 - Printf_Message(Request_Message, 1, request_message_len); - Printf_Message(Response_Message, 0, Response_Message_len); - if (Response_Message_len) - { - if (!Analysis_Response_Message(Response_Message, Request_Message, Response_Message_len)) - printf("响应报文数据异常\n"); - } - else if(!Test_Connection_status(clientSocket)) - { - if (Abnormal_Connection(&clientSocket)) - continue; - else - break; - } - else - printf("响应超时\n"); + *clientSocket = Init_Client(); + } while (INVALID_SOCKET == *clientSocket); + system("cls"); + return true; +} - printf("**************************** Press Enter To Contioun ****************************\n"); - getchar(); - system("cls"); +int Recv_date(SOCKET clientSocket, UINT8 *Response_Message) +{ + int Response_Message_len = recv(clientSocket, (char*)Response_Message, 600, 0); + if (Response_Message_len > 0) + { + return Response_Message_len; } - //关闭套接字 - closesocket(clientSocket); - //关闭服务 - WSACleanup(); return 0; } + +bool Test_Connection_status(SOCKET clientSocket) +{ + TIMEVAL timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + fd_set reads; + FD_ZERO(&reads); + FD_SET(clientSocket, &reads); + if (select(0, &reads, 0, 0, &timeout)) + return false; + else + return true; +} \ No newline at end of file diff --git a/Modbus_communication/Modbus_TCP/TCP_client.h b/Modbus_communication/Modbus_TCP/TCP_client.h index 91bfb24..03e4cf6 100644 --- a/Modbus_communication/Modbus_TCP/TCP_client.h +++ b/Modbus_communication/Modbus_TCP/TCP_client.h @@ -1,11 +1,21 @@ #ifndef __TCP_CLIENT_H #define __TCP_CLIENT_H -#include "common.h" +#include +#include +#include +#include +#include +#include +#pragma comment(lib,"ws2_32.lib") +using namespace std; +unsigned int Count_Respone_Len(int function_code, unsigned int operations_number); +SOCKET Init_Client(void); +bool Send_date(SOCKET clientSocket, UINT8 *Request_Message, int request_message_len); +bool Abnormal_Connection(SOCKET *clientSocket); +int Recv_date(SOCKET clientSocket, UINT8 *Response_Message); +bool Test_Connection_status(SOCKET clientSocket); -int Tcp_Client(); -int Create_TCP_Message(UINT8 *Message, int function_code, unsigned int operations_Number, unsigned int starting_address, string write_date); -bool Analysis_Response_Message(UINT8 *Response_Message, UINT8 *Request_Message, int response_message_len); #endif \ No newline at end of file diff --git a/Modbus_communication/Modbus_TCP/main.h b/Modbus_communication/Modbus_TCP/main.h index 4bac59f..0274a3f 100644 --- a/Modbus_communication/Modbus_TCP/main.h +++ b/Modbus_communication/Modbus_TCP/main.h @@ -1,6 +1,6 @@ #ifndef __MAIN_H #define __MAIN_H -#include "TCP_client.h" +#include "Modbus_Master.h" #endif \ No newline at end of file