diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index cfc834c73..33b3bc740 100644 --- a/APP_Framework/Applications/app_test/Kconfig +++ b/APP_Framework/Applications/app_test/Kconfig @@ -270,6 +270,10 @@ menu "test app" bool "Config test red black tree" default n + menuconfig USER_TEST_MODBUS_TCP + bool "Config test modbus_tcp" + default n + menuconfig USER_TEST_WEBSERVER bool "Config test webserver" default n diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 35752e630..6b0c1ea96 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -121,6 +121,10 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) SRC_FILES += test_socket.c endif + ifeq ($(CONFIG_USER_TEST_MODBUS_TCP),y) + SRC_DIR += test_modbus_tcp + endif + ifeq ($(CONFIG_USER_TEST_WEBSERVER),y) SRC_FILES += endif diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/Makefile b/APP_Framework/Applications/app_test/test_modbus_tcp/Makefile new file mode 100644 index 000000000..34093f67d --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/Makefile @@ -0,0 +1,11 @@ +ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) +SRC_FILES := modbus_tcp.c test_modbus_tcp.c +include $(KERNEL_ROOT)/compiler.mk +endif + +include $(KERNEL_ROOT)/.config +ifeq ($(CONFIG_ADD_NUTTX_FEATURES),y) + include $(APPDIR)/Make.defs + CSRCS += modbus_tcp.c test_modbus_tcp.c + include $(APPDIR)/Application.mk +endif diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/README.md b/APP_Framework/Applications/app_test/test_modbus_tcp/README.md new file mode 100644 index 000000000..153cd6d86 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/README.md @@ -0,0 +1,340 @@ +# ##modbus-tcp## + +## 1. 简介 + +在xiuos平台实现modbusTCP协议,包括协议报文组装、解析和数据传输,支持主从通信。 + +## 2. 数据结构设计说明 + +### 2.1 数据结构定义 + +首先,需要定义设备存储区的结构体,包括两种存储类型,16位的寄存器和线圈。 + +```c +//定义存储区结构体 +typedef struct MbMemory +{ + coils8_t*rcoil_mem; + reg_t*rreg_mem; + coils8_t*rwcoil_mem; + reg_t*rwreg_mem; +}MBmemoryType; +``` + +​ 然后便是关于ModbusTCP协议相关的结构体定义,包括MBAP和PDU,后续数据区视情况而定,长短不固定。 + +```c +//协议的固定部分为12个字节,当功能码为写多个数据时,后续还有不定长的数据部分 +typedef struct mbap +{ + //MbapType + u16_t tid; + u16_t pid; + u16_t len; + u8_t uid; + + /* data */ +}MbapType; + +typedef struct pdu +{ + u8_t func; + + u16_t addr; + + u8_t operand1; + u8_t operand2; + /* data */ +}PduType; +``` + +### 2.1 从设备请求解析和响应部分 + +​ 主要定义请求的解析器结构,以及每种功能码对应的解析函数,和发送响应的函数。 + +```c +//定义解析器结构体 +typedef struct mbparser +{ + int (*func_set[20])(MBmemoryType*,int,MbapType*,PduType*,u8_t**resp); +}MbParserType; + +//功能码解析函数 +int FuncReadRwCoilX01(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRCoilX02(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRwRegX03(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRRegX04(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwCoilX05(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwRegX06(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMcoilsX0f(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMregsX10(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReportSlaveIDX11(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); + + +/** + * @description: 制作响应报文 + * @param {MbapType*mbap,PduType*pdu,u8_t**resp,u16_t buf_len} + * @return {} + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +void MakeResponse(MbapType*,PduType*,u8_t**,u16_t); + +/** + * @Description: 发送响应报文 + * @param {int} fd 套接字对应文件描述符 + * @param {u16_t} n 报文大小 + * @return {int} + * @Date: 2023-07-25 17:24:55 + * @Author: pgh_dd 1041315949@qq.com + */ +int SendResponse(int fd,u8_t**buf,u16_t n); + + +``` + +### 2.2 主设备的请求包装和发送部分 + +```c +/** + * @Description: 读取键盘输入,并生成请求报文 + * @param {u8_t} flag + * @return {int} + * @Date: 2023-07-25 17:25:26 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int GenerateModbusRequest(MbapType*,PduType*,u8_t flag,u8_t**request); + +/** + * @Description: 发送请求报文 + * @param {int fd,u8_t**request,int n} + * @return {} + * @Date: 2023-07-25 17:26:10 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +void SendModbus(int fd,u8_t**request,int n); + +/** + * @Description: 读取请求报文 + * @param {int fd,MbapType*mbap,PduType*pdu} + * @return {void} + * @Date: 2023-07-25 17:26:49 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +void GetRequest(int fd,MbapType*,PduType*); +``` + + + +## 3. 测试程序说明 + +​ modbusTCP协议基于TCP协议,因此其主从通信实际上是基于TCP的S/C通信,因此分为服务端和客户端。 + +​ 服务端部分(从设备)程序实际上是一个被动接受请求报文的TCP服务器程序,除了一些基础参数的定义外,主要包括一个无限循环的服务程序,包括报文的接收,以及对存储区的操作,和生成发送响应。 + +```c +static void *ModbusTcpServer(void *arg) +{ + //设置IP和子网掩码网关 + u8_t uid=1;//定义从设备id和存储区 + + MBmemory mbm;//定义存储区 + if(mb_memory_init(&mbm)==-1)//初始化存储区,包括对四个存储区进行内存分配 + { + return 0; + }; + + MBparser mb_parser;//初始化功能码解析器 + MBparser_init(&mb_parser,MBTCP);//初始化解析器,将功能码对应函数注册 + + int fd=create_socket(PORT);//创建监听套接字 + if(fd==-1)return 0; + + if (listen(fd, 10) != 0 ) { + lw_error("Unable to listen\n"); + close(fd); + return 0; + } + + while(1) + { + //建立连接,因为每次接受的连接可能不是同一个设备发来的,因此需要把建立连接部分放在循环体内。 + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + printf("wait accept\n"); + int clientfd = accept(fd, (struct sockaddr *)&tcp_addr, (socklen_t*)&addr_len); + + if(clientfd==-1) + { + lw_error("Unable to listen\n"); + return 0; + } + + while(1) + { + MBAP mbap; + PDU pdu; + read_mbtcp_MBAP(clientfd,&mbap);//读取数据前7字节为mbap初始化 + + if(mbap.uid!=uid){//检验是否为此从机 + close(clientfd); + break; + } + + + read_mbtcp_PDU(clientfd,&pdu);//读取pdu和一些定长部分 + + printf("OP:%x\n",pdu.func); + printf("ADDR:%x\n",pdu.addr); + + u8_t** response_buf;//定义操作返回的指针 + u8_t buf_len=mb_parser.func_set[pdu.func](&mbm,clientfd,&mbap,&pdu,response_buf);//请求的解析和对存储区的操作 + + send_response(clientfd,response_buf,buf_len);//发送响应 + // return NULL; + //执行操作 + + } + close(clientfd); + } + close(fd); + mb_memory_free(&mbm);//释放存储区 +} + +``` + +​ 客户端部分(主设备)是一个主动发送请求的TCP客户端程序,主要包括一个接受键盘输入的循环体,可以接受用户输入的指令,然后包装成Modbus请求报文,并发送给ModbusTCP服务器,然后接受响应报文。 + +```c +static void *ModbusTcpClient(void *arg) +{ + u16_t counter=0; + int fd = -1; + int ret; + + // lw_print("2023-05-27 Peng Guanhua\n"); + lw_print("%s start\n", __func__); + + fd = socket(AF_INET, SOCK_STREAM, 0);//定义服务器套接字 + if (fd < 0) { + lw_print("Socket error\n"); + return NULL; + } + + char tcp_ip_str[128]="192.168.31.148";//服务器ip和端口号 + u16_t tcp_socket_port=6000; + + /*建立套接字连接*/ + printf("%s\n",tcp_ip_str); + struct sockaddr_in tcp_sock; + tcp_sock.sin_family = AF_INET; + tcp_sock.sin_port = htons(tcp_socket_port); + tcp_sock.sin_addr.s_addr = inet_addr(tcp_ip_str); + printf("%s\n",tcp_ip_str); + + memset(&(tcp_sock.sin_zero), 0, sizeof(tcp_sock.sin_zero)); + + ret = connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr)); + + if (ret < 0) { + lw_print("Unable to connect %s:%d = %d\n", tcp_ip_str, tcp_socket_port, ret); + close(fd); + return NULL; + } + + lw_print("TCP connect %s:%d success, start.\n", tcp_ip_str, tcp_socket_port); + + + + while (1) { + + MBAP mbap={counter,0,0,0}; + PDU pdu; + u8_t*request; + + int mesg_len=generate_modbus_request(&mbap,&pdu,MBTCP,&request);//此函数中接收键盘输入,并生成请求报文。 + send_modbus(fd,&request,mesg_len);//发送请求报文。 + get_response(fd,&mbap,&pdu);//接收响应报文,并显示 + counter++; + } + + close(fd); + return NULL; +} +``` + + + +## 4. 运行结果 + +### 4.1 从设备通信测试 + +从设备测试,在终端上将TCP服务端程序打开,如图1,等待主设备的连接。 + +![alt 图1从设备等到主设备连接](./img/modbusTCP_S1.png) + +​ 主设备采用Modbus Poll应用程序,建立TCP连接,如图2所示。 + +![alt 图2主设备与从设备建立TCP连接](./img/modbusTCP_S2.png) + +​ 此时modbus poll程序便会不断的向从设备发送请求,如图3。 + +![alt 图3从设备接收从设备请求报文并响应](./img/modbusTCP_S3.png) + +​ 可以看到解析出的功能码、地址以及对应的响应报文,然后我们在modbus poll上修改一下存储区数据。如图4所示。 + +![alt 图4](./img/modbusTCP_S4.png) + +因为修改的是寄存器存储区的值,因此对应0x10功能码,然后看看从设备的反映。如图5所示。 + +![alt 图5](./img/modbusTCP_S6.png) + +可见成功收到功能码,并返回了响应的报文。 + +![alt 图6](./img/modbusTCP_S5.png) + +modbus poll显示响应成功,存储区已成功修改期望的值。 + +![alt 图7](./img/modbusTCP_S7.png) + +可见存储区已成功修改。 + +线圈部分的查询修改同理,不再赘述。 + +### 4.2主设备通信测试 + +​ 首先打开modbus slave应用程序,用以作为从设备,然后将存储区数据修改,用以测试,并打开TCP端口,等待主设备的连接,如图8所示。 + +![alt 图8](./img/modbusTCP_C2.png) + +​ 同样在终端打开从设备程序,从设备的ip,port在源码中已定义好,所以打开时已经连接上,如图9所示。 + + + +![alt 图9](./img/modbusTCP_C1.png) + +​ 开始输入从设备id,功能码,以及其他信息用以生成请求报文。 + +![alt 图10](./img/modbusTCP_C3.png) + +​ 如图10所示,输入功能码3,对应读取寄存器功能,地址从0开始,数量4,然后便会生成请求报文,然后发送,结果如图11所示。 + +![alt](./img/modbusTCP_C4.png) + +​ 可见,已成功查询到寄存器的值。 + +​ 然后测试写入功能,输入功能码15(0xf),对应写入多个线圈功能,如图12,modbus slave对应的响应结果如图13所示。 + +​ ![alt 图12](./img/modbusTCP_C5.png) + +​ 写入线圈的值为5个,分别为1 0 1 0 1。发送成功后,modbus salve中显示如图13所示。 + +![alt 图13](./img/modbusTCP_C6.png) + +​ 可见,已成功修改。 + +​ 其他功能码测试过程类似,不再赘述。 \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C1.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C1.png new file mode 100644 index 000000000..5561b12c4 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C1.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C2.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C2.png new file mode 100644 index 000000000..86ae5e38a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C2.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C3.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C3.png new file mode 100644 index 000000000..89a1b8d93 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C3.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C4.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C4.png new file mode 100644 index 000000000..7f9af533d Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C4.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C5.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C5.png new file mode 100644 index 000000000..da5319285 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C5.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C6.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C6.png new file mode 100644 index 000000000..9f9c230ec Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_C6.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S1.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S1.png new file mode 100644 index 000000000..174c7d1f8 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S1.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S2.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S2.png new file mode 100644 index 000000000..247be62ed Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S2.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S3.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S3.png new file mode 100644 index 000000000..7a15f8800 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S3.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S4.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S4.png new file mode 100644 index 000000000..4d8bf1a0e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S4.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S5.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S5.png new file mode 100644 index 000000000..31ad122b8 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S5.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S6.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S6.png new file mode 100644 index 000000000..f06a4bf29 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S6.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S7.png b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S7.png new file mode 100644 index 000000000..fdbec3295 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_modbus_tcp/img/modbusTCP_S7.png differ diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.c b/APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.c new file mode 100644 index 000000000..7e7f57ee4 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/modbus_tcp.c @@ -0,0 +1,729 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/* + * @Description:包含modbusTCP请求报文的包装解析,以及报文收发,和每种功能码对应操作的实现。 + * @Version: V1.0.0 + * @Author: pgh_dd 1041315949@qq.com + * @Date: 2023-05-24 04:00:02 + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 16:36:53 + */ + + + +#include"modbus_tcp.h" + +u16_t Func0x_response_length[20]= +{ + 0,9,9,9,9,12,12,0,0,0,0,0,0,0,0,12,12 +}; + + +/** + * @description: 初始化功能码解析器 + * @param {MbParserType*}mbp {u8_t} type + * @return {void} + * @Date: 2023-07-25 16:59:23 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +void MbparserInit(MbParserType*mbp,u8_t type) +{ + mbp->func_set[R_RW_COIL]=FuncReadRwCoilX01; + mbp->func_set[R_R_COIL]=FuncReadRCoilX02; + mbp->func_set[R_RW_REG]=FuncReadRwRegX03; + mbp->func_set[R_R_REG]=FuncReadRRegX04; + mbp->func_set[W_RW_COIL]=FuncWriteRwCoilX05; + mbp->func_set[W_RW_REG]=FuncWriteRwRegX06; + mbp->func_set[W_MRW_REG]=FuncWriteRwMregsX10; + mbp->func_set[W_MRW_COIL]=FuncWriteRwMcoilsX0f; + mbp->func_set[REPORT_SlAVE_ID]=FuncReportSlaveIDX11; +}; + + +/** + * @description:初始化存储区 + * @return {int} + * @Date: 2023-07-25 17:01:47 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int MbMemoryInit(MBmemoryType*mbm) +{ + mbm->rcoil_mem=(coils8_t*)malloc(sizeof(coils8_t)*RCOILMEM); + mbm->rreg_mem=(reg_t*)malloc(sizeof(reg_t)*RREGMEM); + mbm->rwcoil_mem=(coils8_t*)malloc(sizeof(coils8_t)*RWCOILMEM); + mbm->rwreg_mem=(reg_t*)malloc(sizeof(reg_t)*RWREGMEM); + + memset(mbm->rwreg_mem,0,RWREGMEM); + memset(mbm->rreg_mem,0,RREGMEM); + memset(mbm->rcoil_mem,0,RCOILMEM); + memset(mbm->rwcoil_mem,0,RWCOILMEM); + + // mbm->rwreg_mem[0]=3; + // mbm->rwreg_mem[1]=0x30ff; + mbm->rwcoil_mem[0]=1;mbm->rwcoil_mem[2]=1;mbm->rwcoil_mem[4]=1; + + if(mbm->rcoil_mem==NULL||mbm->rreg_mem==NULL||mbm->rwcoil_mem==NULL||mbm->rwreg_mem==NULL) + { + lw_error("memory is not full\n"); + return -1; + } + return 0; +} + +/** + * @description: 释放存储区 + * @return {void} + * @Date: 2023-07-25 17:02:22 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +void MbMemoryFree(MBmemoryType*mbm) +{ + free(mbm->rcoil_mem); + free(mbm->rreg_mem); + free(mbm->rwcoil_mem); + free(mbm->rwreg_mem); +} + +/** + * @description: 创建tcp通信套接字 + * @param {int} port + * @return {int} + * @Date: 2023-07-25 17:02:39 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int CreateSocket(int port) +{ + int fd = -1, clientfd; + fd = socket(AF_INET, SOCK_STREAM, 0); + if(fd==-1)return -1; + int recv_len; + char *recv_buf; + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + tcp_addr.sin_family = AF_INET; + tcp_addr.sin_addr.s_addr = INADDR_ANY; + tcp_addr.sin_port = htons(port); + memset(&(tcp_addr.sin_zero), 0, sizeof(tcp_addr.sin_zero)); + + if (bind(fd, (struct sockaddr *)&tcp_addr, sizeof(struct sockaddr)) == -1) { + lw_error("Unable to bind\n"); + close(fd); + return -1; + } + + lw_print("tcp bind success, start to receive.\n"); + lw_notice("\nLocal Port:%d\n", port); + + // setup socket fd as listening mode + if (listen(fd,128) != 0 ) { + lw_error("Unable to listen\n"); + close(fd); + return -1; + } + lw_print("Tcp start to listen.\n"); + return fd; +} + +/** + * @description: 读取请求报文的MBAP头部分 + * @param {int} fd {MbapType*}mbap + * @return {} + * @Date: 2023-07-25 17:03:04 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int ReadMbtcpMBAP(int fd,MbapType*mbap) +{ + char buf[MODBUS_MBAP]; + read(fd,buf,MODBUS_MBAP); + mbap->tid=(((u16_t)buf[0])<<8)+(u16_t)buf[1];//高位左移8位再加低位 + mbap->pid=(((u16_t)buf[2])<<8)+(u16_t)buf[3]; + mbap->len=(((u16_t)buf[4])<<8)+(u16_t)buf[5]; + mbap->uid=((u8_t)buf[6]); + +}; + +/** + * @description: 读取请求报文的PDU部分 + * @param {int} fd {PduType*}pdu + * @return {} + * @Date: 2023-07-25 17:03:04 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int ReadMbtcpPDU(int fd,PduType*pdu) +{ + char buf[MODBUS_PDUHEAD]; + + int n=read(fd,buf,MODBUS_PDUHEAD); + + + pdu->func=(u8_t)buf[0];//高位左移8位再加低位 + + if(n>3) + { + pdu->addr=(((u16_t)buf[1])<<8)+(u16_t)buf[2]; + pdu->operand1=(u8_t)buf[3]; + pdu->operand2=(u8_t)buf[4]; + } +}; + +/** + * @description: 制作响应报文 + * @param {MbapType*mbap,PduType*pdu,u8_t**resp,u16_t buf_len} + * @return {} + * @Date: 2023-07-25 17:11:23 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +void MakeResponse(MbapType*mbap,PduType*pdu,u8_t**resp,u16_t buf_len) +{ + buf_len-=6; + //这个长度实际是从数据长度位置到结束的长度,因此要将响应报文的总长减去事务头、协议、长度三个两字节数共6个 + (*resp)[0]=(u8_t)(mbap->tid>>8);(*resp)[1]=(u8_t)(0xff&mbap->tid); + (*resp)[2]=(u8_t)(mbap->pid>>8);(*resp)[3]=(u8_t)(0xff&mbap->pid); + (*resp)[4]=(u8_t)(buf_len>>8);(*resp)[5]=(u8_t)(0xff&buf_len); + (*resp)[6]=mbap->uid; + (*resp)[7]=pdu->func; +} + + +/** + * @description: 功能码0x1,负责读取从设备线圈的状态 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:12:00 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + * @LastEditTime: 2023-07-25 17:10:31 + */ +int FuncReadRwCoilX01(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x01\n"); + printf("coils num:%d\n"); + u16_t coils_num=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2;//线圈个数 + u16_t bytes_num=(coils_num/8+((coils_num%8)!=0));//输出的字节长度 + u16_t buf_len=Func0x_response_length[R_RW_COIL]+bytes_num;//response报文所占长度 + + *resp=(u8_t*)malloc(buf_len); memset(*resp,0,buf_len); + MakeResponse(mbap,pdu,resp,buf_len); + (*resp)[8]=(u8_t)bytes_num; + u8_t*sub_mem=mem->rwcoil_mem; + for(int i=0;iaddr]==1?((u8_t)1<<(i%8)):0); + } + + return buf_len; + // u8_t +}; + +/** + * @Description: 0x2功能码,读离散输入状态 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:20:13 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReadRCoilX02(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x02\n"); + + u16_t coils_num=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2;//线圈个数 + u16_t bytes_num=(coils_num/8+((coils_num%8)!=0));//输出的字节长度 + u16_t buf_len=Func0x_response_length[R_RW_COIL]+bytes_num;//response报文所占长度 + + *resp=(u8_t*)malloc(buf_len); memset(*resp,0,buf_len); + MakeResponse(mbap,pdu,resp,buf_len); + (*resp)[8]=(u8_t)bytes_num; + u8_t*sub_mem=mem->rcoil_mem; + for(int i=0;iaddr]==1?((u8_t)1<<(i%8)):0); + } + + return buf_len; +}; + + +/** + * @Description: 读保持寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:21:01 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReadRwRegX03(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x03\n"); + u16_t data_len=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2; + + u16_t buf_len=Func0x_response_length[R_RW_REG]+data_len*2; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)data_len*2; + + u16_t*sub_mem=mem->rwreg_mem; + + u16_t addr_mem=pdu->addr,addr_resp=9; + int c=0; + while(c>8); + (*resp)[addr_resp+1]=(u8_t)(sub_mem[addr_mem]&0xff); + addr_resp+=2;addr_mem++; + c++; + } + + return buf_len; +}; + +/** + * @Description: 0x4功能码,读输入寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {} + * @Date: 2023-07-25 17:21:22 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReadRRegX04(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x04\n"); + u16_t data_len=((u16_t)pdu->operand1<<8)+(u16_t)pdu->operand2; + + u16_t buf_len=Func0x_response_length[R_R_REG]+data_len*2; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)data_len*2; + + u16_t*sub_mem=mem->rreg_mem; + + u16_t addr_mem=pdu->addr,addr_resp=9; + + int c=0; + while(c>8); + (*resp)[addr_resp+1]=(u8_t)(sub_mem[addr_mem]&0xff); + addr_resp+=2;addr_mem++; + c++; + } + + return buf_len; +}; + +/** + * @Description: 0x5功能码,写单个线圈 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:21:51 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwCoilX05(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x05\n"); + u16_t addr_coil=pdu->addr; + u16_t operand=(((u16_t)pdu->operand1)<<8)+(u16_t)pdu->operand2; + if(operand==0xff00||operand==0x0000) + { + mem->rwcoil_mem[addr_coil]=(operand==0xff00?1:0); + } + + u16_t buf_len=Func0x_response_length[W_RW_COIL]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + printf("write data:%x\n",operand); + + return buf_len; + +}; + +/** + * @Description: 0x6功能码,写单个保持寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:22:38 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwRegX06(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x06\n"); + u16_t addr_reg=pdu->addr; + u16_t reg_data=(u16_t)((pdu->operand1)<<8)+(u16_t)(pdu->operand2); + mem->rwreg_mem[addr_reg]=reg_data; + u16_t buf_len=Func0x_response_length[W_RW_REG]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + printf("write data:%x\n",reg_data); + return buf_len; +}; + +/** + * @Description: 0xf功能码,写多个线圈 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:23:16 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwMcoilsX0f(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x0f\n"); + u16_t coils_num=(((u16_t)(pdu->operand1))<<8)+(u16_t)(pdu->operand2); + + u16_t res_num=coils_num/8+(coils_num%8!=0)+1; + + u8_t*recv_buf=(u8_t*)malloc(sizeof(u8_t)*res_num); + + int n=read(fd,(char*)recv_buf,res_num); + + + u8_t*sub_mem=mem->rwcoil_mem; + + for(int i=0;iaddr+i]=((recv_buf[1+i/8]&(1<<(i%8)))==0?0:1); + } + + + u16_t buf_len=Func0x_response_length[W_MRW_COIL]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + free(recv_buf); + + + + return buf_len; +}; + +/** + * @Description: 0x10功能码,写多个保持寄存器 + * @param {MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp} + * @return {int} + * @Date: 2023-07-25 17:23:56 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncWriteRwMregsX10(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x10\n"); + u16_t data_num=(((u16_t)(pdu->operand1))<<8)+(u16_t)(pdu->operand2); + u16_t res_num=data_num*2+1; + u8_t*recv_buf=(u8_t*)malloc(sizeof(u8_t)*res_num); + + + int n=read(fd,(char*)recv_buf,res_num); + + // printf("%x %x %x\n",recv_buf[0],recv_buf[1],recv_buf[2]); + + u16_t*sub_mem=mem->rwreg_mem; + + for(int i=0;iaddr+i]=(((u16_t)(recv_buf[1+i*2]))<<8)+(u16_t)(recv_buf[1+i*2+1]); + } + + + u16_t buf_len=Func0x_response_length[W_MRW_REG]; + *resp=(u8_t*)malloc(buf_len); + + MakeResponse(mbap,pdu,resp,buf_len); + + (*resp)[8]=(u8_t)((pdu->addr)>>8); + (*resp)[9]=(u8_t)(pdu->addr&0xff); + (*resp)[10]=(u8_t)(pdu->operand1); + (*resp)[11]=(u8_t)(pdu->operand2); + + + free(recv_buf); + + return buf_len; +}; + +/** + * @Description: 报告从设备id + * @param {int} fd + * @return {} + * @Date: 2023-07-25 17:24:43 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int FuncReportSlaveIDX11(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp) +{ + printf("enter x11\n"); + +}; + +/** + * @Description: 发送响应报文 + * @param {int} fd + * @param {u16_t} n + * @return {} + * @Date: 2023-07-25 17:24:55 + * @Author: pgh_dd 1041315949@qq.com + * @LastEditors: pgh_dd 1041315949@qq.com + */ +int SendResponse(int fd,u8_t**buf,u16_t n) +{ + printf("Response:"); + for(int i=0;iuid);printf("%d",mbap->uid);printf("\n"); + + printf("Please input function code:"); + scanf("%x",&pdu->func);printf("%x",pdu->func);printf("\n"); + + printf("The address:"); + scanf("%d",&pdu->addr);printf("%d",pdu->addr);printf("\n"); + + switch (pdu->func) + { + case R_RW_COIL: + case R_R_COIL:printf("The number of coils you want read:");break; + case R_RW_REG: + case R_R_REG:printf("The number of registers you want read:");break; + + case W_RW_COIL:printf("The value of coil you want write(1 or 0):");break; + case W_RW_REG:printf("The value of register you want write:");break; + + case W_MRW_COIL:printf("The number of coils you want write:");break; + case W_MRW_REG:printf("The number of registers you want write:");break; + + default: + break; + } + u16_t num; + scanf("%d",&num);printf("%d\n",num); + if(pdu->func==W_RW_COIL) + { + pdu->operand2=0; + pdu->operand1=(num==1?0xff:0); + } + else { + pdu->operand1=(u8_t)(num>>8); + pdu->operand2=(u8_t)(num&0x00ff); + } + + + if(pdu->func==W_MRW_REG) + { + send_buf_length=num*2+1+12; + *request=(u8_t*)malloc(send_buf_length); + memset(*request,0,send_buf_length); + printf("input register data(decimal integer,in total %d):",num); + (*request)[12]=(u8_t)(num*2); + for(int i=0;i>8); + (*request)[i*2+13+1]=(u8_t)(tem&0x00ff); + } + printf("\n"); + } + else if(pdu->func==W_MRW_COIL) + { + send_buf_length=num/8+(num%8!=0)+1+12; + *request=(u8_t*)malloc(send_buf_length); + memset(*request,0,send_buf_length); + printf("input coil data(0 or 1,in total %d):",num); + (*request)[12]=(u8_t)(num/8+(num%8!=0)); + for(int i=0;iaddr]==1?((u8_t)1<<(i%8)):0); + } + } + printf("\n"); + } + else + { + send_buf_length=12; + *request=(u8_t*)malloc(send_buf_length); + if((*request)==NULL)printf("erro\n"); + + } + + (*request)[0]=(u8_t)(mbap->tid>>8);(*request)[1]=(u8_t)(0xff&mbap->tid); + (*request)[2]=(u8_t)(mbap->pid>>8);(*request)[3]=(u8_t)(0xff&mbap->pid); + (*request)[4]=(u8_t)((send_buf_length-6)>>8);(*request)[5]=(u8_t)(0xff&(send_buf_length-6)); + (*request)[6]=mbap->uid; + (*request)[7]=pdu->func; + (*request)[8]=(u8_t)(pdu->addr>>8);(*request)[9]=(u8_t)(0xff&pdu->addr); + (*request)[10]=pdu->operand1;(*request)[11]=pdu->operand2; + + printf("messege is:"); + for(int i=0;ifunc,1);//读取功能码 + + u8_t byte_num=0; + u8_t *recv_buf; + switch (pdu->func) + { + case R_R_COIL: + case R_R_REG: + case R_RW_COIL: + case R_RW_REG: + data_num=(((u16_t)(pdu->operand1))<<8)+(u16_t)(pdu->operand2); + read(fd,&byte_num,1); + recv_buf=(u8_t*)malloc(byte_num); + read(fd,recv_buf,byte_num); + /* code */ + break; + case W_RW_COIL: + case W_RW_REG: + case W_MRW_REG: + case W_MRW_COIL: + recv_buf=(u8_t*)malloc(4); + read(fd, recv_buf,4); + break; + default: + break; + } + + printf("Response: TID:%x func code:%x byte num:%x\n",mbap->tid,pdu->func,byte_num); + + if(pdu->func==R_R_COIL||pdu->func==R_RW_COIL) + { + printf("coils:"); + for(int i=0;ifunc==R_R_REG||pdu->func==R_RW_REG) + { + printf("registers:"); + // for(int i=0;i +#include +#include +#include +#include +#include "lwip/sys.h" + +#define PORT 8888 +#define RCOILMEM 1000 +#define RREGMEM 1000 +#define RWCOILMEM 1000 +#define RWREGMEM 1000 + +#define MBTCP 1 +#define MBRTU 2 +//功能码 +#define R_RW_COIL 0x01 +#define R_R_COIL 0x02 +#define R_RW_REG 0x03 +#define R_R_REG 0x04 +#define W_RW_COIL 0x05 +#define W_RW_REG 0x06 +#define DIAGNOSTIC 0x08 +#define GET_COUNTER 0x0B +#define W_MRW_COIL 0x0F +#define W_MRW_REG 0x10 +#define REPORT_SlAVE_ID 0x11 + + +typedef u8_t coils8_t; +typedef u16_t reg_t; + +#define MODBUS_MBAP 7 +#define MODBUS_PDUHEAD 5 + + +//定义存储区结构体 +typedef struct MbMemory +{ + coils8_t*rcoil_mem; + reg_t*rreg_mem; + coils8_t*rwcoil_mem; + reg_t*rwreg_mem; +}MBmemoryType; +//初始化存储区的函数 +int MbMemoryInit(MBmemoryType*mb); +//释放存储区 +void MbMemoryFree(MBmemoryType*mb); + + +//协议的固定部分为12个字节,当功能码为写多个数据时,后续还有不定长的数据部分 +typedef struct mbap +{ + //MbapType + u16_t tid; + u16_t pid; + u16_t len; + u8_t uid; + + /* data */ +}MbapType; + +typedef struct pdu +{ + u8_t func; + + u16_t addr; + + u8_t operand1; + u8_t operand2; + /* data */ +}PduType; + +int CreateSocket(int port); + +int ReadMbtcpMBAP(int fd,MbapType*mb_s); +int ReadMbtcpPDU(int fd,PduType*mb_s); + + +//操作函数 + +int FuncReadRwCoilX01(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRCoilX02(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRwRegX03(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncReadRRegX04(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwCoilX05(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwRegX06(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMcoilsX0f(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +int FuncWriteRwMregsX10(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); + +int FuncReportSlaveIDX11(MBmemoryType*mem,int fd,MbapType*mbap,PduType*pdu,u8_t**resp); +//位地址转换字节地址 + +//定义解析器结构体 +typedef struct mbparser +{ + int (*func_set[20])(MBmemoryType*,int,MbapType*,PduType*,u8_t**resp); +}MbParserType; +//解析器初始化,实际上就是每个功能码对应的操作函数的注册 +void MbparserInit(MbParserType*mbp,u8_t flag); + +void MakeResponse(MbapType*,PduType*,u8_t**,u16_t); + +int SendResponse(int fd,u8_t**buf,u16_t n); + + +// void func(PDU*pdu); + +// void delete_modbus_request(); +//主机程序 +int GenerateModbusRequest(MbapType*,PduType*,u8_t flag,u8_t**request); + +void SendModbus(int fd,u8_t**request,int n); + +void GetRequest(int fd,MbapType*,PduType*); +#endif diff --git a/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c b/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c new file mode 100644 index 000000000..b0565511d --- /dev/null +++ b/APP_Framework/Applications/app_test/test_modbus_tcp/test_modbus_tcp.c @@ -0,0 +1,248 @@ +/* +* Copyright (c) 2022 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file lwip_tcp_socket_demo.c +* @brief TCP socket demo based on LwIP +* @version 1.0 +* @author AIIT XUOS Lab +* @date 2022-03-21 +*/ + +#include +#include"modbus_tcp.h" + +#ifdef ADD_XIZI_FEATURES +#include +#include +#include "lwip/sys.h" +#endif + +#ifdef ADD_NUTTX_FEATURES +#include +#include +#include +#include "stdio.h" +#endif + +#define TCP_DEMO_BUF_SIZE 65535 +#define TCP_DEMO_SEND_TIMES 20 +#define LWIP_TCP_DEMO_TASK_STACK_SIZE 4096 +#define LWIP_TCP_DEMO_TASK_PRIO 20 + +static pthread_t tcp_client_task; +static pthread_t tcp_server_task; + +static char tcp_demo_ipaddr[] = {192, 168, 31, 77}; +static char tcp_demo_netmask[] = {255, 255, 255, 0}; +static char tcp_demo_gwaddr[] = {192, 168, 31, 1}; + +#ifdef ADD_NUTTX_FEATURES +#define lw_print printf +#define lw_notice printf +#define lw_error printf + +#define LWIP_DEMO_TIMES 3 +#define LWIP_TARGET_PORT 4840 +#endif + +static uint16_t tcp_socket_port = 8888; +static char tcp_ip_str[128] = {0}; + +/******************************************************************************/ +static void TcpSocketConfigParam(char *ip_str) +{ + int ip1, ip2, ip3, ip4, port = 0; + + if(ip_str == NULL) + return; + + if(sscanf(ip_str, "%d.%d.%d.%d:%d", &ip1, &ip2, &ip3, &ip4, &port)) { + printf("config ip %s port %d\n", ip_str, port); + strcpy(tcp_ip_str, ip_str); + if(port) + tcp_socket_port = port; + return; + } + + if(sscanf(ip_str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4)) { + printf("config ip %s\n", ip_str); + strcpy(tcp_ip_str, ip_str); + } +} + +static void *ModbusTcpServer(void *arg) +{ + + u8_t uid=1;//定义从设备id和存储区 + + MBmemoryType mbm;//定义存储区 + if(MbMemoryInit(&mbm)==-1)//初始化存储区,包括对四个存储区进行内存分配 + { + return 0; + }; + + MbParserType mb_parser;//初始化功能码解析器 + MbparserInit(&mb_parser,MBTCP);//初始化解析器,将功能码对应函数注册 + + int fd=CreateSocket(PORT);//创建监听套接字 + if(fd==-1)return 0; + + int recv_len; + char *recv_buf; + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + while(1) + { + struct sockaddr_in tcp_addr; + socklen_t addr_len; + + printf("wait accept\n"); + int clientfd = accept(fd, (struct sockaddr *)&tcp_addr, (socklen_t*)&addr_len); + + if(clientfd==-1) + { + lw_error("Unable to listen\n"); + return 0; + } + + while(1) + { + MbapType mbap; + PduType pdu; + ReadMbtcpMBAP(clientfd,&mbap);//读取数据前7字节为mbap初始化 + + if(mbap.uid!=uid){//检验是否为此从机 + close(clientfd); + break; + } + + + ReadMbtcpPDU(clientfd,&pdu);//读取pdu和一些定长部分 + + printf("OP:%x\n",pdu.func); + printf("ADDR:%x\n",pdu.addr); + + u8_t* response_buf;//定义操作返回的指针 + u8_t buf_len=mb_parser.func_set[pdu.func](&mbm,clientfd,&mbap,&pdu,&response_buf); + + SendResponse(clientfd,&response_buf,buf_len); + // return NULL; + //执行操作 + + } + close(clientfd); + } + close(fd); + MbMemoryFree(&mbm);//释放存储区 +} + +void TestModbusTcpServer(int argc, char *argv[]) +{ + if(argc >= 2) { + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + TcpSocketConfigParam(argv[1]); + } + +#ifdef ADD_XIZI_FEATURES + lwip_config_tcp(0, tcp_demo_ipaddr, tcp_demo_netmask, tcp_demo_gwaddr); + +#endif + +#ifdef ADD_NUTTX_FEATURES + pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; + attr.priority = LWIP_TCP_DEMO_TASK_PRIO; + attr.stacksize = LWIP_TCP_DEMO_TASK_STACK_SIZE; +#endif + + ModbusTcpServer(NULL); +} + +PRIV_SHELL_CMD_FUNCTION(TestModbusTcpServer, a modbusS test sample, PRIV_SHELL_CMD_MAIN_ATTR); + +static void *ModbusTcpClient(void *arg) +{ + u16_t counter=0; + int fd = -1; + int ret; + + // lw_print("2023-05-27 Peng Guanhua\n"); + lw_print("%s start\n", __func__); + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + lw_print("Socket error\n"); + return NULL; + } + + char tcp_ip_str[128]="192.168.31.148"; + u16_t tcp_socket_port=6000; + + + printf("%s\n",tcp_ip_str); + struct sockaddr_in tcp_sock; + tcp_sock.sin_family = AF_INET; + tcp_sock.sin_port = htons(tcp_socket_port); + tcp_sock.sin_addr.s_addr = inet_addr(tcp_ip_str); + printf("%s\n",tcp_ip_str); + + memset(&(tcp_sock.sin_zero), 0, sizeof(tcp_sock.sin_zero)); + + ret = connect(fd, (struct sockaddr *)&tcp_sock, sizeof(struct sockaddr)); + + if (ret < 0) { + lw_print("Unable to connect %s:%d = %d\n", tcp_ip_str, tcp_socket_port, ret); + close(fd); + return NULL; + } + + lw_print("TCP connect %s:%d success, start.\n", tcp_ip_str, tcp_socket_port); + + + while (1) { + + MbapType mbap={counter,0,0,0}; + PduType pdu; + u8_t*request; + + int mesg_len=GenerateModbusRequest(&mbap,&pdu,MBTCP,&request); + SendModbus(fd,&request,mesg_len); + GetRequest(fd,&mbap,&pdu); + counter++; + } + + close(fd); + return NULL; +} + +void TestModbusTcpClient(int argc, char *argv[]) +{ + if(argc >= 2) { + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + TcpSocketConfigParam(argv[1]); + } + +#ifdef ADD_XIZI_FEATURES + lwip_config_tcp(0, tcp_demo_ipaddr, tcp_demo_netmask, tcp_demo_gwaddr); + +#endif +#ifdef ADD_NUTTX_FEATURES + pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; + attr.priority = LWIP_TCP_DEMO_TASK_PRIO; + attr.stacksize = LWIP_TCP_DEMO_TASK_STACK_SIZE; +#endif + + ModbusTcpClient(NULL); +} +PRIV_SHELL_CMD_FUNCTION(TestModbusTcpClient, a modbustcpC test sample, PRIV_SHELL_CMD_MAIN_ATTR); +