diff --git a/APP_Framework/Applications/app_test/Kconfig b/APP_Framework/Applications/app_test/Kconfig index cfc834c73..74b6d0973 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 @@ -282,6 +286,10 @@ menu "test app" bool "Config test ftp client" default n + menuconfig USER_TEST_FTPCLIENT_RISCV + bool "Config test ftp client on riscv" + default n + menuconfig USER_TEST_LORA_P2P bool "Config test lora p2p" default n diff --git a/APP_Framework/Applications/app_test/Makefile b/APP_Framework/Applications/app_test/Makefile index 7ae722f47..c15e802a4 100644 --- a/APP_Framework/Applications/app_test/Makefile +++ b/APP_Framework/Applications/app_test/Makefile @@ -24,7 +24,7 @@ endif ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) SRC_FILES := test_shell.c - + ifeq ($(CONFIG_USER_TEST_ADC),y) SRC_FILES += test_adc.c endif @@ -125,22 +125,32 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) ifeq ($(CONFIG_USER_TEST_RBTREE),y) SRC_FILES += test_rbtree/test_rbtree.c - endif + endif ifeq ($(CONFIG_USER_TEST_SOCKET),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 += + SRC_FILES += test_webserver/test_webserver.c endif ifeq ($(CONFIG_USER_TEST_MQTTCLIENT),y) + SRC_DIR:= test_mqttclient SRC_FILES += endif ifeq ($(CONFIG_USER_TEST_FTPCLIENT),y) - SRC_FILES += + SRC_FILES += test_ftpclient/test_ftpclient.c test_ftpclient/ftp_client/ftp_client.c\ + test_ftpclient/ftp_client/my_socket.c + endif + + ifeq ($(CONFIG_USER_TEST_FTPCLIENT_RISCV),y) + SRC_FILES += test_ftpclient_riscv/test_ftpclient_riscv.c endif ifeq ($(CONFIG_USER_TEST_LORA_P2P),y) @@ -157,7 +167,11 @@ ifeq ($(CONFIG_ADD_XIZI_FEATURES),y) ifeq ($(CONFIG_USER_TEST_USB_CAMERA),y) SRC_FILES += - endif + endif + + ifeq ($(CONFIG_USER_TEST_FTPCLIENT_FINAL),y) + SRC_FILES += test_ftpclient_final/test_ftpclient_final.c test_ftpclient_final/ftp_client/ftp_client.c test_ftpclient_final/ftp_client/my_socket.c + endif include $(KERNEL_ROOT)/compiler.mk endif diff --git a/APP_Framework/Applications/app_test/test_ftpclient/README.md b/APP_Framework/Applications/app_test/test_ftpclient/README.md new file mode 100644 index 000000000..3905e33aa --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/README.md @@ -0,0 +1,60 @@ +# 初赛一级赛题3:基于矽璓已实现的Lwip,在ARM上实现FTP协议的Client功能 + +## 1. 简介 +本项目是基于矽璓已实现的Lwip,在ARM上实现FTP协议的Client功能 +test_ftpclient.h声明了下载10个文件的测试函数 +test_ftpclient.c实现了下载10个文件的测试函数 +ftp_client文件夹定义了ftp_client的相关类库其中my_socket.h,my_socket.c定义了socket抽象层,并基于 +Lwip实现了该抽象层,ftp_client.h,ftp_client.c实现了ftp登录,获取文件大小,下载文件等功能 + +## 2. 数据结构设计说明 +- ftp_client.c 的设计 +分别定义了发送命令和接收数据的socket和相应的缓冲区并且实现了登录、发送命令、接收响应数据、查找文件大小、进入被动模式、下载文件、关闭ftp客户端等操作 +```c +static int m_socket_cmd; // 发送命令的socket文件描述符 +static int m_socket_data; // 接收ftp服务器文件的socket文件描述符 +static char m_send_buffer[1024]; // 发送缓冲区 +static char m_recv_buffer[1024]; // 接收缓冲区 +``` +## 3. 测试程序说明 +- test_ftpclient.c用于测试下载10个文件 +连接电脑上的ftp服务器下载10个4KB文件,通过终端日志打印确定文件是否下载成功,以及追踪通信流程和下载进度 +```c +void TestFtpClient(int argc, char* argv[]) +{ + FtpInitCmd(); + int ret = FtpLogin("192.168.0.248", 21, "anonymous", "anonymous"); + int size; + char *buf; + for(int i = 1;i <= 10;i++){ + char fileName[20] = "/file"; + char temp[5] = ""; + sprintf(temp,"%d",i); + strcat(fileName,temp); + size = FtpFileSize(fileName); + buf = malloc(size); + FtpInitData(); // data socket 每次下载都要重新创建,下载完都要关闭 + ret = FtpDownload(fileName, buf, size); + free(buf); + } + FtpQuit(); + return; +} +``` + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) +1. 配置开启BSP_USING_LWIP、USER_TEST_FTPCLIENT +![](./img/image.png) +![](./img/image-1.png) +2. 编译 +![](./img/image-2.png) +3. 烧写 +![](./img/image-3.png) +4. xshell连接串口终端 +![](./img/image-4.png) +6. 配置ip +![](./img/image-5.png) +7. 运行TestFtpClient,开始下载文件 +![](./img/image-6.png) +![](./img/image-7.png) +![](./img/image-8.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.c b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.c new file mode 100644 index 000000000..e1ac107ec --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/ftp_client.c @@ -0,0 +1,256 @@ +/* +* 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. +*/ + +/** +* @file: ftp_client.c +* @brief: ftp client tool +* @version: 1.0 +* @author: bdislab_final +* @date: 2023/7/25 +* @reference https://kerndev.blog.csdn.net/article/details/89383888 +*/ + +#include +#include +#include +#include +#include "my_socket.h" +#include "ftp_client.h" + +static int m_socket_cmd; +static int m_socket_data; +static char m_send_buffer[1024]; +static char m_recv_buffer[1024]; + +static int FtpSendCommand(char *cmd) +{ + int ret; + printf("send command: %s\r\n", cmd); + ret = SocketSend(m_socket_cmd, cmd, (int)strlen(cmd)); + if(ret < 0) + { + printf("failed to send command: %s\r\n",cmd); + return 0; + } + return 1; +} + +static int FtpRecvRespond(char *resp, int len) +{ + int ret; + int off; + len -= 1; + for(off=0; off +#include +#include + +int SocketCreate(void){ + return socket(AF_INET,SOCK_STREAM,0); +} + +int SocketConnect(int sock, const char *addr, int port){ + // unsigned int iRemoteAddr = 0; + struct sockaddr_in stRemoteAddr = {0}; //对端,即目标地址信息 + + stRemoteAddr.sin_family = AF_INET; + stRemoteAddr.sin_port = htons(port); + stRemoteAddr.sin_addr.s_addr = inet_addr(addr); + // inet_pton(AF_INET, addr, &iRemoteAddr); + // stRemoteAddr.sin_addr.s_addr=iRemoteAddr; + int res = connect(sock,(struct sockaddr *)&stRemoteAddr,sizeof(stRemoteAddr)); + if(res == -1){ + printf("error:%d\n",errno); + } + return res == 0 ? 1 : 0; +} + +int SocketSend(int sock, void *data, int len){ + return send(sock,data,len,0); +} + +int SocketRecv(int sock, void *data, int len){ + return recv(sock,data,len,0); +} + +void SocketClose(int sock){ + close(sock); +} diff --git a/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/my_socket.h b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/my_socket.h new file mode 100644 index 000000000..2b3aee813 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/ftp_client/my_socket.h @@ -0,0 +1,46 @@ +/* +* 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. +*/ + +/** + * @file my_socket.h + * @brief a abstract socket api + * @version 1.0 + * @author bdislab_final + * @date 2023/7/25 + */ + +#ifndef MYSOCKET_H +#define MYSOCKET_H + +#ifdef __cplusplus +extern "C" { +#endif +/* create a socket */ +int SocketCreate(void); + +/* connect a socket */ +int SocketConnect(int sock, const char *addr, int port); + +/* send data through socket*/ +int SocketSend(int sock, void *data, int len); + +/* receive data from socket*/ +int SocketRecv(int sock, void *data, int len); + +/* close socket*/ +void SocketClose(int sock); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-1.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-1.png new file mode 100644 index 000000000..0d050f2ca Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-1.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-2.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-2.png new file mode 100644 index 000000000..39b7fdc20 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-2.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-3.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-3.png new file mode 100644 index 000000000..a22bb75d2 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-3.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-4.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-4.png new file mode 100644 index 000000000..30de94357 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-4.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-5.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-5.png new file mode 100644 index 000000000..729f01a77 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-5.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-6.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-6.png new file mode 100644 index 000000000..dc3672a47 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-6.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-7.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-7.png new file mode 100644 index 000000000..97bb36a30 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-7.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image-8.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image-8.png new file mode 100644 index 000000000..25a5fc307 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image-8.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/img/image.png b/APP_Framework/Applications/app_test/test_ftpclient/img/image.png new file mode 100644 index 000000000..9775c39a5 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient/img/image.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.c b/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.c new file mode 100644 index 000000000..a97858799 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.c @@ -0,0 +1,50 @@ +/* +* 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. +*/ + +/** +* @file: test_ftpclient.c +* @brief: a ftpClient test sample +* @version: 1.0 +* @author: bdislab_final +* @date: 2023/7/25 +*/ + +#include "test_ftpclient.h" +#include "ftp_client/ftp_client.h" +#include +#include +#include +#include + + +/* test for ftp client */ +void TestFtpClient(int argc, char* argv[]) +{ + FtpInitCmd(); + int ret = FtpLogin("192.168.1.248", 21, "anonymous", "anonymous"); + int size; + char *buf; + for(int i = 1;i <= 10;i++){ + char fileName[20] = "/file"; + char temp[5] = ""; + sprintf(temp,"%d",i-1); + strcat(fileName,temp); + size = FtpFileSize(fileName); + buf = malloc(size); + FtpInitData(); // data socket 每次下载都要重新创建,下载完都要关闭 + ret = FtpDownload(fileName, buf, size); + free(buf); + } + FtpQuit(); + return; +} +PRIV_SHELL_CMD_FUNCTION(TestFtpClient, a ftpClient test sample, PRIV_SHELL_CMD_MAIN_ATTR); diff --git a/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.h b/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.h new file mode 100644 index 000000000..d35e65cab --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient/test_ftpclient.h @@ -0,0 +1,35 @@ +/* +* 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. +*/ + +/** + * @file test_ftpclient.h + * @brief a ftpClient test sample + * @version 1.0 + * @author bdislab_final + * @date 2023/7/25 + */ + +#ifndef TEST_FTPCLIENT_H +#define TEST_FTPCLIENT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* test for ftp client */ +void TestFtpClient(int argc, char* argv[]); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/README.md b/APP_Framework/Applications/app_test/test_ftpclient_final/README.md new file mode 100644 index 000000000..2e691ad00 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/README.md @@ -0,0 +1,135 @@ +# 决赛一级赛题3:基于初赛一级赛题3,在云服务器上实现FTP Server功能 + +## 1. 简介 +本项目是基于赛事提供的云服务器,实现FTP协议的Server功能,其功能支持至少10个Client端并发向Server端传输4KB大小的文件, +支持Server端并发地向至少10个Client端传输4KB大小的文件。 +test_ftpclient_final.h声明了多个客户端并发下载文件、并发上传文件的测试函数 +test_ftpclient_final.c实现了多个客户端并发下载文件、并发上传文件的测试函数 +ftp_client文件夹定义了ftp_client的相关类库其中my_socket.h,my_socket.c定义了socket抽象层,并基于 +Lwip实现了该抽象层,ftp_client.h,ftp_client.c实现了ftp登录,获取文件大小,下载文件等功能 +注意:在赛事提供的云服务器/root/yanglongFTP上有对应服务器代码 ./server运行服务器,为了方便查看代码我们讲服务器代码备份存放在ftp_server目录下 +## 2. 数据结构设计说明 +- ftp_client.c 的设计 +分别定义了发送命令和接收数据的socket和相应的缓冲区并且实现了登录、发送命令、接收响应数据、查找文件大小、进入被动模式、下载文件、关闭ftp客户端等操作 +```c +static int m_socket_cmd[THREAD_NUM]; // 发送命令的socket文件描述符,THREAD_NUM表示线程数目,用来模拟多个客户端并发访问 +static int m_socket_data[THREAD_NUM]; // 接收ftp服务器文件的socket文件描述符 +static char m_send_buffer[THREAD_NUM][1024]; // 发送缓冲区 +static char m_recv_buffer[THREAD_NUM][1024]; // 接收缓冲区 +``` +- server.c 的设计(具体代码在赛事提供的云服务器的/root/yanglongFTP下) +```c +#define THREAD_NUM 10000 // thread num +static int isBinary = 0; // transmit binary data +static int port = 9992; // service port +static int dataPort = 9993; // the port for file download +static char order[4]; // receive order +static char param[20]; // receive order param +static char *respMessage; // respose message +static int serverFd; // the server fd for deal with order requests +static int dataServerFd; // the server fd for file download +sem_t mutex; // mutex lock +struct Data{ // 线程间通信传输的数据 + char fileName[20]; // file name + int type; // 0:download 1:upload + sem_t isDone; // complete file downlaod + sem_t isReady; // 文件准备好了 +} data; +``` +## 3. 测试程序说明 +- test_ftpclient_final.c用于测试多个客户端并发下载文件 +通过多线程模拟多个客户端并发访问服务器,通过 TestFtpClient options threads进行测试 +其中options=1表示下载options=2表示上传,threads表示线程数/模拟的并发客户端数目 +```c +/* test for 10 ftp client */ +void TestFtpClient(int argc, char* argv[]) +{ + int options = atoi(argv[1]); + int n = atoi(argv[2]); + pthread_t threads[THREAD_NUM]; + for(int i = 0;i < n;++i){ + threadIDs[i] = i; + if(options == 1){ // 全部都是下载 + pthread_create(&threads[i],NULL,&DownLoad,&threadIDs[i]); + }else if(options == 2){ // 全部都是上传 + pthread_create(&threads[i],NULL,&UpLoad,&threadIDs[i]); + }else if(options == 3){ // 随机下载/上传 + int r = rand()%2; + if(r == 0){ + printf("===============download===============\n"); + pthread_create(&threads[i],NULL,&DownLoad,&threadIDs[i]); + }else{ + printf("===============upload===============\n"); + pthread_create(&threads[i],NULL,&UpLoad,&threadIDs[i]); + } + } + } + return; +} +PRIV_SHELL_CMD_FUNCTION(TestFtpClient, a ftpClient test sample, PRIV_SHELL_CMD_MAIN_ATTR); +``` + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) +1. 配置开启BSP_USING_LWIP、USER_TEST_FTPCLIENT +![](./img/image.png) +![](./img/image-1.png) +![](./img/image-2.png) +2. 编译 +![](./img/image-3.png) +3. 烧写 +![](./img/image-4.png) +4. xshell连接串口终端并配置ip +![](./img/image-5.png) +5. 通过./server在云服务器运行FTP服务器 +![](./img/image-6.png) +6. 运行TestFtpClient 1 10,模拟10个客户端并发下载文件 +- 客户端日志 +![](./img/image-7.png) +![](./img/image-8.png) +![](./img/image-9.png) +![](./img/image-10.png) +![](./img/image-11.png) +![](./img/image-12.png) +![](./img/image-13.png) +![](./img/image-14.png) +![](./img/image-15.png) +![](./img/image-16.png) +- 服务器日志 +![](./img/image-17.png) +![](./img/image-18.png) +![](./img/image-19.png) +![](./img/image-20.png) +![](./img/image-21.png) +7. 运行TestFtpClient 2 10,模拟10个客户端并发上传文件 +- 客户端日志 +![](./img/image-22.png) +![](./img/image-23.png) +![](./img/image-24.png) +![](./img/image-25.png) +![](./img/image-26.png) +![](./img/image-27.png) +![](./img/image-28.png) +- 服务器日志 +![](./img/image-29.png) +![](./img/image-30.png) +![](./img/image-31.png) +![](./img/image-32.png) +- 上传结果 +![](./img/image-33.png) +8. 运行TestFtpClient 3 10,模拟10个客户端混合并发下载和上传文件 +- 客户端日志 +![](./img/image-34.png) +![](./img/image-35.png) +![](./img/image-36.png) +![](./img/image-37.png) +![](./img/image-38.png) +![](./img/image-39.png) + +- 服务器日志 +![](./img/image-40.png) +![](./img/image-41.png) +![](./img/image-42.png) + +- 下载和上传结果 +![](./img/image-43.png) +![](./img/image-44.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_client/ftp_client.c b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_client/ftp_client.c new file mode 100644 index 000000000..4e1cb554f --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_client/ftp_client.c @@ -0,0 +1,299 @@ +/* +* 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. +*/ + +/** +* @file: ftp_client.c +* @brief: ftp client tool +* @version: 1.0 +* @author: bdislab_final +* @date: 2023/9/16 +*/ + +#include +#include +#include +#include +#include "my_socket.h" +#include "ftp_client.h" + + +static int m_socket_cmd[THREAD_NUM]; +static int m_socket_data[THREAD_NUM]; +static char m_send_buffer[THREAD_NUM][1024]; +static char m_recv_buffer[THREAD_NUM][1024]; + +static int FtpSendCommand(int threadID,char *cmd) +{ + int ret; + printf("send command: %s,threadID=%d\r\n", cmd,threadID); + ret = SocketSend(m_socket_cmd[threadID], cmd, (int)strlen(cmd)); + if(ret < 0) + { + printf("failed to send command: %s\r\n",cmd); + return 0; + } + return 1; +} + +static int FtpRecvRespond(int threadID,char *resp, int len) +{ + int ret; + int off; + len -= 1; + for(off=0; off +#include +#include +#include + +int SocketCreate(void){ + return socket(AF_INET,SOCK_STREAM,0); +} + +int SocketConnect(int sock, const char *addr, int port){ + // unsigned int iRemoteAddr = 0; + struct sockaddr_in stRemoteAddr = {0}; //对端,即目标地址信息 + + stRemoteAddr.sin_family = AF_INET; + stRemoteAddr.sin_port = htons(port); + stRemoteAddr.sin_addr.s_addr = inet_addr(addr); + int res = connect(sock,(struct sockaddr *)&stRemoteAddr,sizeof(stRemoteAddr)); + if(res == -1){ + printf("error:%d,str error:%s\n",errno,strerror(errno)); + } + return res == 0 ? 1 : 0; +} + +int SocketSend(int sock, void *data, int len){ + return send(sock,data,len,0); +} + +int SocketRecv(int sock, void *data, int len){ + return recv(sock,data,len,0); +} + +void SocketClose(int sock){ + close(sock); +} diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_client/my_socket.h b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_client/my_socket.h new file mode 100644 index 000000000..19854f55e --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_client/my_socket.h @@ -0,0 +1,46 @@ +/* +* 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. +*/ + +/** + * @file my_socket.h + * @brief a abstract socket api + * @version 1.0 + * @author bdislab_final + * @date 2023/9/16 + */ + +#ifndef MYSOCKET_H +#define MYSOCKET_H + +#ifdef __cplusplus +extern "C" { +#endif +/* create a socket */ +int SocketCreate(void); + +/* connect a socket */ +int SocketConnect(int sock, const char *addr, int port); + +/* send data through socket*/ +int SocketSend(int sock, void *data, int len); + +/* receive data from socket*/ +int SocketRecv(int sock, void *data, int len); + +/* close socket*/ +void SocketClose(int sock); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/a.out b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/a.out new file mode 100755 index 000000000..7e9454464 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/a.out differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file1 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file1 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file1 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file10 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file10 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file10 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file2 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file2 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file2 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file3 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file3 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file3 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file4 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file4 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file4 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file5 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file5 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file5 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file6 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file6 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file6 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file7 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file7 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file7 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file8 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file8 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file8 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file9 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file9 new file mode 100644 index 000000000..08e7df176 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/file9 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/gen.sh b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/gen.sh new file mode 100644 index 000000000..7382342b3 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/data/gen.sh @@ -0,0 +1,5 @@ +#!/bin/bash +for i in {1..10} +do + dd if=/dev/zero of="file$i" bs=4K count=1 +done diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file0 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file0 new file mode 100644 index 000000000..8486a6b13 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file0 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file1 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file1 new file mode 100644 index 000000000..b70b444a1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file1 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file2 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file2 new file mode 100644 index 000000000..a18027c49 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file2 @@ -0,0 +1 @@ +0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file3 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file3 new file mode 100644 index 000000000..b70b444a1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file3 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file4 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file4 new file mode 100644 index 000000000..b70b444a1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file4 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file5 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file5 new file mode 100644 index 000000000..b70b444a1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file5 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file6 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file6 new file mode 100644 index 000000000..b70b444a1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file6 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file7 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file7 new file mode 100644 index 000000000..b70b444a1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file7 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file8 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file8 new file mode 100644 index 000000000..b70b444a1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file8 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file9 b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file9 new file mode 100644 index 000000000..6fc16fe2c Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/out/file9 differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/server b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/server new file mode 100755 index 000000000..5a76806a4 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/server differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/server.c b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/server.c new file mode 100644 index 000000000..14000941a --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/ftp_server/server.c @@ -0,0 +1,251 @@ +/* +* 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. +*/ + +/** +* @file: server.c +* @brief: a ftpserver +* @version: 1.0 +* @author: bdislab_final +* @date: 2023/10/11 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define THREAD_NUM 10000 // thread num +static int isBinary = 0; // transmit binary data +static int port = 9992; // service port +static int dataPort = 9993; // the port for file download +static char order[4]; // receive order +static char param[20]; // receive order param +static char *respMessage; // respose message +static int serverFd; // the server fd for deal with order requests +static int dataServerFd; // the server fd for file download +sem_t mutex; // mutex lock +struct Data{ // 线程间通信传输的数据 + char fileName[20]; // file name + int type; // 0:download 1:upload + sem_t isDone; // complete file downlaod + sem_t isReady; // 文件准备好了 +} data; + +void RecvData(int clientFd,char param[20]){ + recv(clientFd,param,20,0); + for(int i = 0;i < 20;++i){ + if(param[i] == '\r'){ + param[i] = '\0'; + return; + } + } + return; +} + +void SendMessage(int clientFd, char * respMessage){ + int len = 0; + while(respMessage[len] != '\n'){ + len++; + } + send(clientFd,respMessage,len+1,0); +} + +int CreateServer(int port,int type) { + int serverFd = 0; + struct sockaddr_in serverAddr = {0}; + socklen_t socklen = 0; + serverFd = socket(AF_INET, SOCK_STREAM, 0); + if(0 > serverFd) + { + printf("创建socket失败\n"); + return 0; + } + + if(type == 0){ + printf("in ftp server:serverFd=%d\n",serverFd); + }else{ + printf("in ftp data server:serverFd=%d\n",serverFd); + } + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(port); + serverAddr.sin_addr.s_addr=htonl(INADDR_ANY); + if(0 > bind(serverFd, (void *)&serverAddr, sizeof(serverAddr))) + { + printf("绑定失败!\n"); + return 0; + } + if(0 > listen(serverFd, 1024)) + { + printf("监听失败!\n"); + return 0; + } + return serverFd; +} + +// receive file's data +void* UpLoadServer(void * args){ + int clientFd = *(int *) args; + char buf[4096]; + int ret = recv(clientFd,buf,4096,0); + printf("ret=%d,errno=%d\n",ret,errno); + FILE * file = fopen(data.fileName,"wb"); + fwrite(buf,4096,1,file); + fclose(file); + printf("upload complete for client: clientFd=%d,tid=%ld\n",clientFd,pthread_self()); + // notify for file download complete + sem_post(&data.isDone); + close(clientFd); +} + +// send file's data +void* DownLoadServer(void * args){ + int clientFd = *(int *) args; + char buf[4096]; + FILE * file = fopen(data.fileName,"rb"); + fread(buf,4096,1,file); + send(clientFd,buf,sizeof(buf),0); + fclose(file); + printf("download complete for client: clientFd=%d,tid=%ld\n",clientFd,pthread_self()); + // notify for file download complete + sem_post(&data.isDone); + close(clientFd); +} + +// accept a client and new a thread for downlaod/upload +void* DataServer(void * args){ + dataServerFd = CreateServer(dataPort,1); + while(1){ + // wait for a filename + sem_wait(&data.isReady); + struct sockaddr_in clientAdrr = {0}; + int socketLen = sizeof(clientAdrr); + int clientFd = accept(dataServerFd,(struct sockaddr *)&clientAdrr,&socketLen); + pthread_t thread; + if(data.type == 0){ // download + printf("accept a client for file download: clientFd=%d,tid=%ld\n",clientFd,pthread_self()); + pthread_create(&thread,NULL,&DownLoadServer,&clientFd); + }else if(data.type == 1){ // upload + printf("accept a client for file upload: clientFd=%d,tid=%ld\n",clientFd,pthread_self()); + pthread_create(&thread,NULL,&UpLoadServer,&clientFd); + } + // release lock + sem_post(&mutex); + } +} + +// deal with order requests +void* OrderServer(void * args){ + int clientFd = *(int *)args; + printf("in OrderServer thread: tid=%ld\n",pthread_self()); + // order format: USER anonymous\r\n + // response code format: 213 4096\r\n + while(1){ + recv(clientFd,order,4,0); + printf("order=%s,tid=%ld\n",order,pthread_self()); + if(strcmp(order,"USER") == 0){ + RecvData(clientFd,param); + if(strcmp(param," anonymous") == 0){ + respMessage = "331 Please specify the password.\r\n"; + SendMessage(clientFd,respMessage); + } + }else if(strcmp(order,"PASS") == 0){ + RecvData(clientFd,param); + if(strcmp(param," anonymous") == 0){ + respMessage = "230 Login successful.\r\n"; + SendMessage(clientFd,respMessage); + printf("login successful in clientFd %d,tid=%ld\n",clientFd,pthread_self()); + } + }else if(strcmp(order,"TYPE") == 0){ + RecvData(clientFd,param); + if(strcmp(param," I") == 0){ + isBinary = 1; + respMessage = "200 Switching to Binary mode.\r\n"; + SendMessage(clientFd,respMessage); + } + }else if(strcmp(order,"SIZE") == 0){ + RecvData(clientFd,param); + respMessage = "213 4096\r\n"; + SendMessage(clientFd,respMessage); + }else if(strcmp(order,"PASV") == 0){ + sem_wait(&mutex); // 锁竞争 + sem_init(&data.isDone,0,0); + RecvData(clientFd,param); + char buf[50]; + sprintf(buf,"227 Entering passive mode (8,140,53,225,%d,%d).\r\n",dataPort/256,dataPort%256); + respMessage = buf; + SendMessage(clientFd,respMessage); + }else if(strcmp(order,"RETR") == 0){ + RecvData(clientFd,param); + sprintf(data.fileName,"./data/%s",param+2); + data.type = 0; + // notify the thread of waiting fileName + sem_post(&data.isReady); + char buf[70]; + sprintf(buf,"150 Opening Binary mode data connection for %s (4096 bytes).\r\n",param+1); + respMessage = buf; + SendMessage(clientFd,respMessage); + // wait for download complete + printf("wait for download complete,tid=%ld\n",pthread_self()); + sem_wait(&data.isDone); + respMessage = "226 Transfer complete.\r\n"; + SendMessage(clientFd,respMessage); + }else if(strcmp(order,"STOR") == 0){ + RecvData(clientFd,param); + sprintf(data.fileName,"./out/%s",param+1); + data.type = 1; + // notify the thread of waiting fileName + sem_post(&data.isReady); + char buf[70]; + sprintf(buf,"150 Opening Binary mode data connection for %s (4096 bytes).\r\n",param+1); + respMessage = buf; + SendMessage(clientFd,respMessage); + // wait for upload complete + printf("wait for upload complete,tid=%ld\n",pthread_self()); + sem_wait(&data.isDone); + respMessage = "226 Transfer complete.\r\n"; + SendMessage(clientFd,respMessage); + } + else if(strcmp(order,"QUIT") == 0){ + printf("close clientFd=%d,tid=%ld\n",clientFd,pthread_self()); + close(clientFd); + break; + } + } + return NULL; +} + +int main(){ + serverFd = CreateServer(port,0); + sem_init(&data.isReady,0,0); + sem_init(&mutex,0,1); + pthread_t thread; + pthread_create(&thread,NULL,&DataServer,NULL); + int clientFds[THREAD_NUM]; + int i = 0; + while(1){ + struct sockaddr_in clientAdrr = {0}; + int socketLen = sizeof(clientAdrr); + clientFds[i] = accept(serverFd,(struct sockaddr *)&clientAdrr,&socketLen); + printf("accept a client:fd=%d\n",clientFds[i]); + respMessage = "220 (myFtpServer 1.0)\r\n"; + SendMessage(clientFds[i],respMessage); + printf("send welcome message successfully.\n"); + pthread_t thread; + pthread_create(&thread,NULL,&OrderServer,&clientFds[i]); + i++; + } + return 0; +} diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-1.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-1.png new file mode 100644 index 000000000..61d2c21d7 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-1.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-10.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-10.png new file mode 100644 index 000000000..a1f0f4966 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-10.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-11.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-11.png new file mode 100644 index 000000000..ea40d9457 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-11.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-12.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-12.png new file mode 100644 index 000000000..dca42550d Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-12.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-13.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-13.png new file mode 100644 index 000000000..41e2f35a7 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-13.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-14.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-14.png new file mode 100644 index 000000000..9b70c95c9 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-14.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-15.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-15.png new file mode 100644 index 000000000..dc979f115 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-15.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-16.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-16.png new file mode 100644 index 000000000..dd4f08a58 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-16.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-17.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-17.png new file mode 100644 index 000000000..1a95ff84c Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-17.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-18.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-18.png new file mode 100644 index 000000000..c6cc1296d Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-18.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-19.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-19.png new file mode 100644 index 000000000..d3aa14dfb Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-19.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-2.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-2.png new file mode 100644 index 000000000..35ea476c9 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-2.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-20.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-20.png new file mode 100644 index 000000000..3a881cfc6 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-20.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-21.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-21.png new file mode 100644 index 000000000..f5eb00ea4 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-21.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-22.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-22.png new file mode 100644 index 000000000..e1bae3078 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-22.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-23.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-23.png new file mode 100644 index 000000000..b5e2a992b Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-23.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-24.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-24.png new file mode 100644 index 000000000..897720f70 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-24.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-25.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-25.png new file mode 100644 index 000000000..ec6f3df43 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-25.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-26.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-26.png new file mode 100644 index 000000000..80d82d3d1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-26.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-27.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-27.png new file mode 100644 index 000000000..c60114bda Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-27.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-28.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-28.png new file mode 100644 index 000000000..98f1d2160 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-28.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-29.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-29.png new file mode 100644 index 000000000..fdc6a1d9a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-29.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-3.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-3.png new file mode 100644 index 000000000..1245e6843 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-3.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-30.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-30.png new file mode 100644 index 000000000..248c97262 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-30.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-31.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-31.png new file mode 100644 index 000000000..121765f9d Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-31.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-32.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-32.png new file mode 100644 index 000000000..2e00b936a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-32.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-33.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-33.png new file mode 100644 index 000000000..432974fef Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-33.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-34.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-34.png new file mode 100644 index 000000000..f75b9add0 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-34.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-35.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-35.png new file mode 100644 index 000000000..57b8fc78b Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-35.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-36.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-36.png new file mode 100644 index 000000000..f92bc2ea9 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-36.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-37.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-37.png new file mode 100644 index 000000000..0ca516df6 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-37.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-38.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-38.png new file mode 100644 index 000000000..c58057dc6 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-38.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-39.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-39.png new file mode 100644 index 000000000..b45cd4f88 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-39.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-4.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-4.png new file mode 100644 index 000000000..a8e31410a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-4.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-40.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-40.png new file mode 100644 index 000000000..0c787de91 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-40.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-41.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-41.png new file mode 100644 index 000000000..c9cde1089 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-41.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-42.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-42.png new file mode 100644 index 000000000..cb6147feb Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-42.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-43.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-43.png new file mode 100644 index 000000000..885ba0192 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-43.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-44.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-44.png new file mode 100644 index 000000000..d41535660 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-44.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-5.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-5.png new file mode 100644 index 000000000..3b1130a3e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-5.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-6.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-6.png new file mode 100644 index 000000000..531ccc29b Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-6.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-7.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-7.png new file mode 100644 index 000000000..bf0d48630 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-7.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-8.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-8.png new file mode 100644 index 000000000..c88f44e85 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-8.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-9.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-9.png new file mode 100644 index 000000000..db34576c5 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image-9.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/img/image.png b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image.png new file mode 100644 index 000000000..516c58f46 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_final/img/image.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/test_ftpclient_final.c b/APP_Framework/Applications/app_test/test_ftpclient_final/test_ftpclient_final.c new file mode 100644 index 000000000..5a2024f79 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/test_ftpclient_final.c @@ -0,0 +1,93 @@ +/* +* 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. +*/ + +/** +* @file: test_ftpclient.c +* @brief: a ftpClient test sample +* @version: 1.0 +* @author: bdislab_final +* @date: 2023/9/16 +*/ + +#include "test_ftpclient_final.h" +#include "ftp_client/ftp_client.h" +#include +#include +#include +#include +#include + +int threadIDs[THREAD_NUM]; + +/*one client upLoad a file of 4K*/ +void* UpLoad(void* arg){ + int *pThreadID = (int*)arg; + printf("in thread:tid=%ld\n", pthread_self()); + FtpInitCmd(*pThreadID); + int ret = FtpLogin(*pThreadID,"8.140.53.225", 9992, "anonymous", "anonymous"); + int size = 4096; + char buf[size]; + char fileName[20]; + sprintf(fileName,"file%d",*pThreadID); + for(int i = 0;i < size;++i){ + buf[i] = '0'; + } + FtpInitData(*pThreadID); // data socket 每次下载都要重新创建,下载完都要关闭 + ret = FtpUpload(*pThreadID,fileName,buf,size); + FtpQuit(*pThreadID); + return NULL; +} + +/*one client downLoad a file of 4K*/ +void* DownLoad(void* arg){ + int *pThreadID = (int*)arg; + printf("in thread:tid=%ld\n", pthread_self()); + FtpInitCmd(*pThreadID); + int ret = FtpLogin(*pThreadID,"8.140.53.225", 9992, "anonymous", "anonymous"); + int size; + char *buf; + char fileName[20] = "/file1"; + size = FtpFileSize(*pThreadID,fileName); + buf = malloc(size); + FtpInitData(*pThreadID); // data socket 每次下载都要重新创建,下载完都要关闭 + ret = FtpDownload(*pThreadID,fileName, buf, size); + free(buf); + FtpQuit(*pThreadID); + return NULL; +} + +/* test for 10 ftp client */ +void TestFtpClient(int argc, char* argv[]) +{ + int options = atoi(argv[1]); + int n = atoi(argv[2]); + pthread_t threads[THREAD_NUM]; + for(int i = 0;i < n;++i){ + threadIDs[i] = i; + if(options == 1){ // 全部都是下载 + pthread_create(&threads[i],NULL,&DownLoad,&threadIDs[i]); + }else if(options == 2){ // 全部都是上传 + pthread_create(&threads[i],NULL,&UpLoad,&threadIDs[i]); + }else if(options == 3){ // 随机下载/上传 + int r = rand()%2; + if(r == 0){ + printf("===============download===============\n"); + pthread_create(&threads[i],NULL,&DownLoad,&threadIDs[i]); + }else{ + printf("===============upload===============\n"); + pthread_create(&threads[i],NULL,&UpLoad,&threadIDs[i]); + } + } + } + return; +} +PRIV_SHELL_CMD_FUNCTION(TestFtpClient, a ftpClient test sample, PRIV_SHELL_CMD_MAIN_ATTR); diff --git a/APP_Framework/Applications/app_test/test_ftpclient_final/test_ftpclient_final.h b/APP_Framework/Applications/app_test/test_ftpclient_final/test_ftpclient_final.h new file mode 100644 index 000000000..a57b016f7 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_final/test_ftpclient_final.h @@ -0,0 +1,37 @@ +/* +* 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. +*/ + +/** + * @file test_ftpclient.h + * @brief a ftpClient test sample + * @version 1.0 + * @author bdislab_final + * @date 2023/9/16 + */ + +#ifndef TEST_FTPCLIENT_H +#define TEST_FTPCLIENT_H + +#ifdef __cplusplus +extern "C" { +#endif +/*one client upLoad a file of 4K*/ +void* UpLoad(void* arg); +/*one client downLoad 10 files of 4K*/ +void* DownLoad(void* arg); +/* test for ftp client */ +void TestFtpClient(int argc, char* argv[]); +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/README.md b/APP_Framework/Applications/app_test/test_ftpclient_riscv/README.md new file mode 100644 index 000000000..fa485085c --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_riscv/README.md @@ -0,0 +1,43 @@ +# 基于矽璓已实现的W5500网卡驱动,在Riscv上实现FTP协议的Client功能 # + +## 1. 简介 +利用w5500网络编程的api实现了ftp客户端的功能,具体功能包括向ftp服务器发送指定命令,根据用户名登陆ftp服务器,获取文件大小,下载指定文件并保存至sd卡内,并提供了测试函数模拟从ftp服务器上下载10个4KB文件的过程 + +## 2. 数据结构设计说明 +全局变量: +char sendBuffer[]:输出缓冲区 +char recvBuffer[]:输入缓冲区 + +包括以下函数功能,分别为: +`SendCommand`:发送指定的指令 +`EnterPasv`:进入被动模式 +`Login`:根据用户名登陆ftp服务器 +`GetFileSize`:获取指定文件的大小 +`Download`:下载指定文件 + + +## 3. 测试程序说明 +测试了创建套接字,与服务器建立连接并根据传入的用户名和密码发送登陆指令。登陆成功后下载服务器上指定的10个4KB大小的txt文件,然后调用开发板的相关api在系统根目录中创建对应的文件,写入从服务器接收到的数据。 + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) +![image](img/01.png) +![image](img/02.png) +打开menuconfig之后,将test_ftp开启(y),将Using TFcard device 和 Using W5500 as network device 开启(y)保存后退出 + +![image](img/03.png) +编译XiZi-edu-riscv64.elf成功 + +![image](img/04.png) +启动kflash烧录bin文件,按reset键重置成功进入系统并显示SD卡已挂载 + +![image](img/05.png) +执行TestTfp命令,可以看到成功登陆ftp服务器,并开始下载10个大小为4KB的文件。 + +![image](img/06.png) +服务器日志信息 + +![image](img/07.png) +读取sd卡中的文件可以看到10个大小为4KB的文件被成功下载 +![image](img/08.png) +![image](img/09.png) +![image](img/10.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/01.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/01.png new file mode 100644 index 000000000..ecf8ae8e6 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/01.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/02.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/02.png new file mode 100644 index 000000000..1c99e096a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/02.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/03.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/03.png new file mode 100644 index 000000000..28c7339df Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/03.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/04.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/04.png new file mode 100644 index 000000000..f45b3588f Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/04.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/05.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/05.png new file mode 100644 index 000000000..9c3f3e064 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/05.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/06.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/06.png new file mode 100644 index 000000000..0b04f45c1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/06.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/07.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/07.png new file mode 100644 index 000000000..26d9ef32c Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/07.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/08.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/08.png new file mode 100644 index 000000000..49bd8a3e3 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/08.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/09.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/09.png new file mode 100644 index 000000000..60f289d79 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/09.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/10.png b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/10.png new file mode 100644 index 000000000..e56ec0712 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_ftpclient_riscv/img/10.png differ diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/test_ftpclient_riscv.c b/APP_Framework/Applications/app_test/test_ftpclient_riscv/test_ftpclient_riscv.c new file mode 100644 index 000000000..876788358 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_riscv/test_ftpclient_riscv.c @@ -0,0 +1,241 @@ +/* +* Copyright (c) 2020 AIIT Ubiquitous Team +* 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. +*/ + +#include +#include +#include +#include +#include "test_ftpclient_riscv.h" +#define SOCKET_DATA 2 +#define SOCKET_CMD 3 +char sendBuffer[1024]; +char recvBuffer[1024]; + +//quoted from https://kerndev.blog.csdn.net/article/details/89383888 +int SendCommand(char *cmd) +{ + int ret; + ret = wiz_sock_send(SOCKET_CMD,cmd,(int)strlen(cmd)); + if(ret < 0 ){ + return 0; + } + return 1; +} +int RecvRespond(char * respond,int len) +{ + int ret; + int off; + len -= -1; + for(off = 0;off < len;off += ret){ + ret = wiz_sock_recv(SOCKET_CMD,&respond[off],1); + if(ret < 0 ){ + printf("recv respond error\r\n"); + return 0; + } + if(respond[off] == '\n'){ + break; + } + } + respond[off + 1]=0; + return atoi(respond); +} +int EnterPasv(uint8_t *ipaddr,int *port) +{ + int ret; + char *find; + int ip1,ip2,ip3,ip4,tmp1,tmp2; + ret = SendCommand("PASV\r\n"); + if(ret != 1){ + return 0; + } + ret=RecvRespond(recvBuffer,1024); + if(ret != 227){ + return 0; + } + find=strrchr(recvBuffer,'('); + sscanf(find,"(%d,%d,%d,%d,%d,%d)",&ip1,&ip2,&ip3,&ip4,&tmp1,&tmp2); + ipaddr[0] = ip1; + ipaddr[1] = ip2; + ipaddr[2] = ip3; + ipaddr[3] = ip4; + *port = tmp1 * 256 + tmp2; + return 1; +} +int Login(uint8_t addr[4],int port,char *username,char *password) +{ + int ret; + printf("connect....\n"); + ret = wiz_sock_connect(SOCKET_CMD,addr,port); + if(ret != 1){ + printf("connect server failed\r\n"); + return 0; + } + printf("connect ok.\r\n"); + + ret = RecvRespond(recvBuffer,1024); + if(ret != 220){ + printf("bad server ret=%d\r\n",ret); + return 0; + } + printf("login....\r\n"); + sprintf(sendBuffer,"USER %s\r\n",username); + ret = SendCommand(sendBuffer); + + if(ret != 1){ + return 0; + } + ret = RecvRespond(recvBuffer,1024); + if(ret == 220)ret = RecvRespond(recvBuffer,1024); + if(ret != 331){ + return 0; + } + sprintf(sendBuffer,"PASS %s\r\n",password); + ret = SendCommand(sendBuffer); + if(ret != 1){ + return 0; + } + ret = RecvRespond(recvBuffer,1024); + if(ret != 230){ + return 0; + } + printf("login sucess \r\n"); + + + ret = SendCommand("TYPE I\r\n"); + if(ret != 1){ + return 0; + } + ret = RecvRespond(recvBuffer,1024); + if(ret != 200){ + return 0; + } + return 1; +} +int GetFileSize(char * name) +{ + int ret; + int size; + sprintf(sendBuffer,"SIZE %s\r\n",name); + ret = SendCommand(sendBuffer); + if(ret != 1){ + return 0; + } + ret = RecvRespond(recvBuffer,1024); + if(ret != 213){ + printf("get file size failed\r\n"); + return 0; + } + size = atoi(recvBuffer + 4); + return size; +} +int Download(char *name) +{ + wiz_socket(SOCKET_DATA, Sn_MR_TCP,12345,0); + int len = GetFileSize(name); + char *buf = malloc(len+1); + printf("downloading file %s\r\n",name); + int it; + int ret; + uint8_t addr[4]; + int port; + ret = EnterPasv(addr,&port); + + if(ret != 1){ + wiz_sock_disconnect(SOCKET_DATA); + return 0; + } + ret = wiz_sock_connect(SOCKET_DATA,addr,port); + if(ret != 1){ + wiz_sock_disconnect(SOCKET_DATA); + printf("fail to connect data port\r\n"); + return 0; + } + sprintf(sendBuffer,"RETR %s\r\n",name); + ret = SendCommand(sendBuffer); + if(ret != 1){ + wiz_sock_disconnect(SOCKET_DATA); + return 0; + } + ret = RecvRespond(recvBuffer,1024); + if(ret!=150){ + wiz_sock_disconnect(SOCKET_DATA); + return 0; + } + for(it = 0;it < len;it += ret){ + ret = wiz_sock_recv(SOCKET_DATA,((char *)buf + it),len); + if(ret<0){ + printf("download was interupted\r\n"); + break; + } + } + buf[len] = 0; + printf("download %d/%d bytes complete. \r\n",it,len); + ret = RecvRespond(recvBuffer,1024); + if(ret != 226){ + wiz_sock_disconnect(SOCKET_DATA); + return 0; + } + wiz_sock_disconnect(SOCKET_DATA); + + printf("creating file %s ....\r\n",name); + int fd=open(name,O_RDWR | O_CREAT); + if(fd > 0){ + ret = write(fd,buf,strlen(buf)); + if(ret < 0 ){ + printf("write failed\r\n"); + } + else printf("success!\r\n"); + } + else { + printf("create file %s failed\r\n",name); + } + if(buf != NULL){ + free(buf); + } + return 1; +} +void Init() +{ + + wiz_socket(SOCKET_CMD, Sn_MR_TCP,54321,0); + printf("socket created\n"); +} +void Quit() +{ + wiz_sock_disconnect(SOCKET_CMD); +} +int TestFtp() +{ + + Init(); + + uint8_t ip[4] = {192,168,130,70}; + Login(ip,21,"frank","114514"); + + Download("File0.txt"); + Download("File1.txt"); + Download("File2.txt"); + Download("File3.txt"); + Download("File4.txt"); + Download("File5.txt"); + Download("File6.txt"); + Download("File7.txt"); + Download("File8.txt"); + Download("File9.txt"); + + Quit(); + + return 0; +} +PRIV_SHELL_CMD_FUNCTION(TestFtp, Implement ftp, PRIV_SHELL_CMD_MAIN_ATTR); diff --git a/APP_Framework/Applications/app_test/test_ftpclient_riscv/test_ftpclient_riscv.h b/APP_Framework/Applications/app_test/test_ftpclient_riscv/test_ftpclient_riscv.h new file mode 100644 index 000000000..a92585c45 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_ftpclient_riscv/test_ftpclient_riscv.h @@ -0,0 +1,14 @@ +#include +#include +#include +#include + +int SendCommand(char *cmd); +int RecvRespond(char * respond,int len); +int EnterPasv(uint8_t *ipaddr,int *port); +int Login(uint8_t addr[4],int port,char *username,char *password); +int GetFileSize(char * name); +int Download(char *name); +void Init(); +void Quit(); +int TestFtp(); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/README.md b/APP_Framework/Applications/app_test/test_lora_net_final/README.md new file mode 100755 index 000000000..f74ac38f5 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/README.md @@ -0,0 +1,187 @@ +# 基于初赛二级赛题1,实现多个LoRa节点和LoRa网关通信私有协议 +## 1. 简介 +基于 E220-400T22S Lora模块设计实现通信私有协议,主要包含以下几个方面:
+- 协议实现完全遵循LoRa Adapter代码规范,可以直接使用原有的Adapter相关函数基于本协议构建Lora网络。 +- 重新设计实现E220底层函数,增加了IOCTL函数实现,支持在运行时对地址、信道、通信模式、空中速率等参数的动态配置。 +- E220模块只支持单通道通信,本协议基于定向传输模式实现了多下行信道通信。 +- 支持数据帧完整性校验、支持数据帧丢失或者跳帧检测。 +- 支持节点断网自动重联,在数据发送失败时可以进行重联或者搜索其他网关。 +- 支持节点对网关的搜索,可以主动寻找在线网关入网。 +- 支持信道冲突检测,检测上行通道是否占用。 +## 2. 数据结构说明 +### E220模块相关数据结构 + +```c +enum LoraMode // E220工作模式,支持四种,配置模式与休眠模式一致 +{ + MODE_TRANSFER_MODE = 0, // M0 : M1 = 0 : 0 可发可收 + MODE_WOR_SEND, // M0 : M1 = 1 : 0 可发可收 + MODE_WOR_RECV, // M0 : M1 = 0 : 1 可收不发 + MODE_CONFIG_SLEEP // M0 : M1 = 1 : 1 不收不发 +}; + +enum LoraState // 硬件状态 +{ + STATE_CLOSED, // 硬件关闭 + STATE_OPENED, // 已正常开启 + STATE_BROKEN // 硬件损坏 +}; + +enum LoraTransMode // 模块传输模式 +{ + TRANS_MODE_HYALINE = 0X0, // 透明传输 + TRANS_MODE_ORIENTED = 0X40, // 定点传输 +}; + +enum LoraAirRate // 空中速率,通信双方必须设置一致的空中速率方可进行通信 +{ + AIR_RATE_2D4K = 0X0, // 2.4K + AIR_RATE_4D8K = 0X3, // 4.8K + AIR_RATE_9D6K = 0X4, // 9.6K + AIR_RATE_19D2K = 0X5, // 19.2K + AIR_RATE_38D4K = 0X6, // 38.4K + AIR_RATE_62D5K = 0X7 // 62.5K +}; + +enum LoraConfig // 支持IOCTL函数进行配置的配置项枚举,某些项只支持9600波特率配置 +{ + CONFIG_SERIAL_TIME_OUT = 0X0, // 串口超时时间 + CONFIG_LORA_MODE = 0X1, // 配置Lora模式 + CONFIG_TRANS_MODE = 0X2, // 配置Lora传输模式(9600) + CONFIG_AIR_RATE = 0X3, // 配置空中速率(9600) + CONFIG_BAUD_RATE = 0X4, // 配置波特率 + CONFIG_ADDRESS = 0X5, // 配置设备地址(9600) + CONFIG_CHANNEL = 0X6, // 配置设备信道(9600) + CONFIG_SHOW = 0X7 // 打印配置信息 +}; +``` + +### 数据帧相关数据结构 + +```c +enum FrameType // 数据帧类型枚举 +{ + E_G_JOIN = 0, // 客户端入网请求,需回复 + E_G_QUIT, // 客户端退网请求,不需回复 + E_G_DATA, // 客户端上传数据,需回复 + G_E_ANS, // 网关响应客户端请求 +}; + +struct LoraFrame // 数据帧消息类型 +{ + uint16 begin_mark; // 开始标志 0x3C3C + uint16 session_id; // 会话ID,客户端每次数据上传会随机设置,若是回复ID不一致则表示数据丢失 + uint16 dev_addr; // 设备地址,特指终端地址,网关地址固定为 0XFFFF + uint8 down_channel; // 下行通道,即终端所在的信道 + uint8 frame_type; // 数据帧的类型 + uint8 frame_confirm; // 该数据帧是否需要确认回复 + uint8 frame_attach; // 用于携带简单的数据,例如用户数据长度 + uint8 *user_data; // 需要额外携带的大量数据 + uint8 crc_hi; // 校验位高字节 + uint8 crc_lo; // 校验位低字节 + uint16 end_mark; // 结束标志 0X5A5A +}; +``` + +### Lora节点相关数据结构 + +```c +enum EndNodeState //节点状态 +{ + NODE_STATE_DISCONNECT = 0, // 硬件开启,但是没有联网,后台程序可能开启 + NODE_STATE_CONNECT, // 硬件开启,已经联网,后台程序可能开启 + NODE_STATE_BROKEN, // 硬件损坏,无后台程序 + NODE_STATE_CLOSED // 硬件关闭,无后台程序 +}; + +struct EndNodeParam // 节点参数结构 +{ + uint16 dev_addr; // 设备地址信息 + uint8 down_channel; // 下行通道,终端可以接收信息的信道 + uint8 upload_channel; // 上行通道,终端可以给网关发送信息的信道 + uint16 session_id; // 会话ID,用于判定数据帧是否存在丢失 + + enum EndNodeState node_state; // 客户端状态 + enum LoraAirRate air_rate; // 终端的空中速率 + uint8 adr_enable; // 终端是否开启自动寻找上行网关 + uint8 reconnect_enable; // 终端断网自动重联 + uint8 recv_time; // 接受下行消息的时间窗口长度,单位为秒 +}; +``` + +### Lora网关相关数据结构 + +```c +struct EndNodeInfo // 网关存储的客户端信息 +{ + uint16 node_addr; // 客户端地址 + uint8 node_down_channel; // 客户端下行通道 + uint16 session_id; // 上一个命令的会话ID +}; + +enum GatewayState // 网关的状态枚举 +{ + GATEWAY_CLOSED, // Lora还没有打开 + GATEWAY_OPENED, // 已打开,但是未开始工作 + GATEWAY_WORKING, // 正常打开且已经开始正常工作 + GATEWAY_BROKEN // 模块损坏 +}; + +struct GatewayParam // 网关参数 +{ + uint16 dev_addr; // 设备ID + uint8 channel; // 网络编号 + + int recv_time; // 串口超时时间 + enum LoraAirRate air_rate; // 网关空中速率 + uint8 frame_retry; // 重传次数 + enum GatewayState gateway_state; // 网关状态 + struct EndNodeInfo *node_infos[LORA_GATEWAY_MAX_NODE]; // 客户端信息 + uint16 node_count; // 已链接客户端数量 +}; +``` + +## 3. 测试程序说明 +- 编译烧录两个不同地址、信道的客户端和一个网关。 +- 分别打开客户端和网关并联网。 +- 两个客户端分别向网关发送数据。 +## 4. 运行结果 +### (1)编译模块: +- 配置MENUCONFIG +![image](./images/0-1.png) +> 开启连接框架支持,同时开启Lora Adapter支持 + +![image](./images/0-2.png) +> 选择E220模块 + +![image](./images/0-3.png) +> 开启测试函数 + +![image](./images/0-4.png) +> 测试单通道网关 + +- 修改MakeFile:xiuos/APP_Framework/Applications/app_test/Makefile第141行修改为 +```c + SRC_FILES += test_lora_net_final/lora_driver/e220.c + SRC_FILES += test_lora_net_final/lora_mac/lora_mac.c + SRC_FILES += test_lora_net_final/test_lora_net_final.c +``` +- 修改框架初始函数: +> 1. 文件:xiuos/APP_Framework/Framework/framework_init.c 第27行修改为
extern int UsrAdapterLoraInit(void); +> 2. 文件:xiuos/APP_Framework/Framework/framework_init.c 第170行的AdapterLoraInit修改为UsrAdapterLoraInit +### (2)打开网关 +![image](./images/1.png) +> 网关打开,地址OXFFFF,信道OXA,空中速率2.4K + +### (3)客户端连接网关 +![image](./images/2.png) +> 两边客户端同时发送入网请求,可以看见信道检测效果 + +### (4)客户端发送数据 +![image](./images/3.png) +> 客户端向网关发送数据 + +![image](./images/4.png) +> 两边客户端同时发送数据 + +使用自带的天线,2.4K空中速率,载荷64字节,在无大型遮盖物的情况下传输距离近1500米 \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-1.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-1.png new file mode 100755 index 000000000..e3e3a8e74 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-1.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-2.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-2.png new file mode 100755 index 000000000..a24c14716 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-2.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-3.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-3.png new file mode 100755 index 000000000..544c91b43 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-3.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-4.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-4.png new file mode 100755 index 000000000..954d436c8 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-4.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/1.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/1.png new file mode 100755 index 000000000..77b3a5e7f Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/1.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/2.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/2.png new file mode 100755 index 000000000..4675aef37 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/2.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/3.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/3.png new file mode 100755 index 000000000..337d3de50 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/3.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/4.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/4.png new file mode 100755 index 000000000..a28da7bad Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/4.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.c b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.c new file mode 100755 index 000000000..db094a145 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.c @@ -0,0 +1,591 @@ +/* +* 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. +*/ + +/** +* @file: e220.c +* @brief: E220 module underlying related configuration driver functions implement +* @version: 0.1 +* @author: YUNFEI CHU +* @date: 2023/10/01 +* +*/ + +#include "e220.h" + +struct RegisterCfg // 寄存器结构 +{ + uint8 ADDH; + uint8 ADDL; // 地址 + uint8 REG0; // 波特率/串口校验/空中速率 + uint8 REG1; // 分包设定/RSSI环境噪声/发射功率 + uint8 REG2; // 信道 + uint8 REG3; // RSSI字节/传输方式/LBT使能/WOR周期 + uint8 KEYH; + uint8 KEYL; // 密钥 +}; + +/** + * 默认的寄存器配置信息,在每次配置更新后会同步更新该数据,保持其与硬件寄存器数据的一致性 +*/ +static struct RegisterCfg register_cfg = +{ + .ADDH = (E220_ADDRESS >> 8) & 0XFF, + .ADDL = E220_ADDRESS & 0XFF, + .REG0 = E220_DEFAULT_CONFIG_BAUD_RATE_BYTE | E220_DEFAULT_AIR_RATE, + .REG1 = 0X0, + .REG2 = E220_DEFAULT_CHANNEL, + .REG3 = E220_DEFAULT_TRANS_MODE, + .KEYH = 0X0, + .KEYL = 0X0, +}; + +/** + * 默认的串口配置信息,初始化时波特率为9600,保持与硬件一致,在波特率更改过程中也需要保证一致 +*/ +static struct SerialDataCfg serial_cfg = +{ + .serial_baud_rate = E220_DEFAULT_CONFIG_BAUD_RATE,// 串口波特率 + .serial_data_bits = DATA_BITS_8, + .serial_stop_bits = STOP_BITS_1, + .serial_parity_mode = PARITY_NONE, + .serial_bit_order = BIT_ORDER_LSB, + .serial_invert_mode = NRZ_NORMAL, + .serial_buffer_size = SERIAL_RB_BUFSZ, + .serial_timeout = E220_DAFAULT_SERIAL_TIMEOUT, // 串口超时配置 + .is_ext_uart = 0, +}; + +enum LoraMode current_mode = -1; // 当前模块处于什么模式 +enum LoraState lora_state = STATE_CLOSED; // 当前模块的状态 +uint8 lora_init = E220_NO; // 当前模块是否已经被初始化过了 + +/** + * @brief: 将硬件切换至指定的模式 + * @param mode: 支持休眠/配置模式 和 传输模式 +*/ +static uint8 E220LoraModeConfig(enum LoraMode mode) +{ + // 模式和状态判断 + if (lora_state == STATE_BROKEN) + { + DDBG("E220LoraModeConfig-Fail: lora broken!\n"); + return -1; + } + if(mode == current_mode) + { + // 同一个模式直接返回,不输出信息 + return 0; + } + // 打开PIN驱动 + int pin_fd = PrivOpen(E220_PIN_DRIVER,O_RDWR); + if (pin_fd < 0) + { + DDBG("E220LoraModeConfig-Fail: open pin driver fail %s!\n",E220_PIN_DRIVER); + lora_state = STATE_BROKEN; + return -1; + } + // 配置管脚属性 + struct PinParam pin_param ={.cmd = GPIO_CONFIG_MODE,.mode = GPIO_CFG_OUTPUT,.pin = E220_M0_PATH}; + struct PrivIoctlCfg ioctl_config = {.ioctl_driver_type = PIN_TYPE, .args = &pin_param}; + if (0 != PrivIoctl(pin_fd,OPE_CFG,&ioctl_config)) + { + DDBG("E220LoraModeConfig-Fail: config m0 fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + pin_param.pin = E220_M1_PATH; + ioctl_config.args = &pin_param; + if (0 != PrivIoctl(pin_fd,OPE_CFG,&ioctl_config)) + { + DDBG("E220LoraModeConfig-Fail: config m1 fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + + struct PinStat pin_stat; + switch (mode) + { + case MODE_TRANSFER_MODE: + { + pin_stat.pin = E220_M0_PATH; + pin_stat.val = GPIO_LOW; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m0 low fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + pin_stat.pin = E220_M1_PATH; + pin_stat.val = GPIO_LOW; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m1 low fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + break; + } + case MODE_CONFIG_SLEEP: + { + pin_stat.pin = E220_M0_PATH; + pin_stat.val = GPIO_HIGH; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m0 high fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + pin_stat.pin = E220_M1_PATH; + pin_stat.val = GPIO_HIGH; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m1 high fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + break; + } + default: + DDBG("E220LoraModeConfig-Fail: unsupported lora mode!\n"); + PrivClose(pin_fd); + return -1; + } + PrivTaskDelay(E220_CONFIG_DELAY); + PrivClose(pin_fd); + // 记录当前模式,避免不必要的重复配置 + current_mode = mode; + DDBG("E220LoraModeConfig-Success: change to mode %s !\n",mode == MODE_CONFIG_SLEEP ? "MODE_CONFIG_SLEEP" : "MODE_TRANSFER_MODE"); + return 0; +} + +/** + * @brief: 通过指令获取硬件寄存器参数后将其保存并输出 + * @param adapter: 需要打开 +*/ +static void PrintRegister(char* prefix) +{ + // 打印数据 + DDBG("%s Register:",prefix); + DDBG("ADDH:%X,ADDL:%X,REG0:%X,",register_cfg.ADDH,register_cfg.ADDL,register_cfg.REG0); + DDBG("REG1:%X,REG2:%X,REG3:%X,",register_cfg.REG1,register_cfg.REG2,register_cfg.REG3); + DDBG("KEYH:%X,KEYL:%X\n",register_cfg.KEYH,register_cfg.KEYL); +} + +/** + * @brief: 根据配置的数据覆写硬件寄存器参数 + * @param adapter: 一个已经打开了UART串口的Lora硬件 + * @param start_addr: 需要覆写的寄存器起始地址 + * @param byte_length: 需要覆写的寄存器数量 + * @param args: 需要覆写的寄存器参数 + * @return: 0 -> Success; -1 -> Fail +*/ +static uint8 WriteRegister(struct Adapter *adapter,uint8 start_addr,uint8 byte_length,void* args) +{ + if (STATE_BROKEN == lora_state) + { + DDBG("WriteRegister-Fail: lora broken!\n"); + return -1; + } + // 切换硬件到配置模式 + E220LoraModeConfig(MODE_CONFIG_SLEEP); + // 构造寄存器覆写数据 + uint8 buffer[11] = {0}; + buffer[0] = 0XC0; + buffer[1] = start_addr; + buffer[2] = byte_length; + memcpy(&buffer[3],args,byte_length); + // 覆写寄存器参数 + if (PrivWrite(adapter->fd, (void *)buffer, byte_length + 3) < 0) + { + PrintRegister("WriteRegister-Fail"); + lora_state = STATE_BROKEN; + return -1; + } + // 等待配置完成后读取回写数据 + PrivTaskDelay(E220_CONFIG_DELAY); + PrivRead(adapter->fd, buffer, byte_length + 3); + PrintRegister("WriteRegister-Success"); + return 0; +} + +/** + * @brief: 根据默认的配置对一个已经打开UART串口的Lora硬件进行寄存器参数的初始化,开启后只会被调用一次 + * @param adapter: 需要打开 +*/ +static uint8 E220RegisterInit(struct Adapter *adapter) +{ + if(STATE_BROKEN == lora_state) + { + DDBG("E220RegisterInit-Fail: lora broken!\n"); + return -1; + } + if (lora_init == E220_YES) + { + DDBG("E220RegisterInit-Success: inited before!\n"); + return 0; + } + // 按照默认的参数进行寄存器初始化 + if (0 != WriteRegister(adapter,0X0,0X8,®ister_cfg)) + { + lora_state = STATE_BROKEN; + return -1; + } + // 防止重复初始化 + lora_init = E220_YES; + DDBG("E220RegisterConfig-Success!\n"); + return 0; +} + +uint8 E220Open(struct Adapter *adapter) +{ + if (lora_state == STATE_BROKEN) + { + DDBG("E220Open-Fail: lora broken!\n"); + return -1; + } + if (lora_state == STATE_OPENED) + { + // 避免重复打开 + return 0; + } + // 打开UART串口 + adapter->fd = PrivOpen(E220_UART_DRIVER,O_RDWR); + if (adapter->fd < 0) + { + DDBG("E220Open-Fail: open uart fail - %s \n",E220_UART_DRIVER); + lora_state = STATE_BROKEN; + return -1; + } + // 按照默认串口配置信息初始化串口 + struct PrivIoctlCfg ioctl_cfg; + ioctl_cfg.ioctl_driver_type = SERIAL_TYPE; + ioctl_cfg.args = &serial_cfg; + if (0 != PrivIoctl(adapter->fd, OPE_INT,&ioctl_cfg)) + { + DDBG("E220Open-Fail: uart config fail - %s \n",E220_UART_DRIVER); + lora_state = STATE_BROKEN; + return -1; + } + // 硬件的寄存器初始化 + if (0 != E220RegisterInit(adapter)) + { + DDBG("E220Open-Fail: register config fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + lora_state = STATE_OPENED; + DDBG("E220Open-Success: e220 open success!\n"); + return 0; +} + +uint8 E220Close(struct Adapter *adapter) +{ + if (lora_state != STATE_OPENED) + { + DDBG("E220Close-Success: broken or closed before!\n"); + return 0; + } + // 关闭UART串口 + if (adapter->fd > 0) + { + if (0 != PrivClose(adapter->fd)) + { + DDBG("E220Close-Failed: result != 0\n"); + lora_state = STATE_BROKEN; + return -1; + } + } + adapter->fd = -1; + lora_init = E220_NO; + lora_state = STATE_CLOSED; + DDBG("E220Close-Success: success!\n"); + return 0; +} + +uint8 E220Ioctl(struct Adapter *adapter, int config_item, void *args) +{ + if (STATE_OPENED != lora_state) + { + DDBG("E220Ioctl-Fail: open lora or the lora is broken!\n"); + return -1; + } + switch (config_item) + { + // 配置Lora串口超时时间 + case CONFIG_SERIAL_TIME_OUT: + { + int32 time_out = *((int32*)args); + if (time_out == serial_cfg.serial_timeout) + { + DDBG("ConfigSerialTimeOut-Success: the same time_out %d !\n", time_out); + return 0; + } + + serial_cfg.serial_timeout = time_out; + struct PrivIoctlCfg ioctl_cfg = { .ioctl_driver_type = SERIAL_TYPE,.args = &serial_cfg }; + // 修改串口配置信息 + if (0 != PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg)) + { + DDBG("ConfigSerialTimeOut-Fail: %d\n", serial_cfg.serial_timeout); + lora_state = STATE_BROKEN; + return -1; + } + DDBG("ConfigSerialTimeOut-Success: %d\n", serial_cfg.serial_timeout); + return 0; + } + // 配置Lora模式 + case CONFIG_LORA_MODE: + { + enum LoraMode mode = *((enum LoraMode*)args); + if (mode == current_mode) + { + DDBG("ConfigLoraMode-Success: the same mode %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP"); + return 0; + } + if (mode == MODE_TRANSFER_MODE || mode == MODE_CONFIG_SLEEP) + { + if (0 != E220LoraModeConfig(mode)) + { + DDBG("ConfigLoraMode-Fail: config mode fail %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP"); + return -1; + } + DDBG("ConfigLoraMode-Success: change lora mode to %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP"); + return 0; + } + DDBG("ConfigLoraMode-Fail: unsupported lora mode %x!\n",mode); + return -1; + } + // 配置Lora传输模式 + case CONFIG_TRANS_MODE: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigTransMode-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint8 trans_mode = *((uint8*)args); + if (trans_mode == TRANS_MODE_HYALINE || trans_mode == TRANS_MODE_ORIENTED) + { + // 判断参数是否有必要覆写 + if (register_cfg.REG3 & 0X40 == trans_mode) + { + DDBG("ConfigTransMode-Success: the same trans mode %x!\n",trans_mode); + return 0; + } + trans_mode = register_cfg.REG3 & 0xBF | trans_mode; + if (0 != WriteRegister(adapter,0X5,1,&trans_mode)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigTransMode-Fail: config trans mode fail %x!\n",trans_mode); + return -1; + } + register_cfg.REG3 = trans_mode; + DDBG("ConfigTransMode-Success: config trans mode success %x!\n",trans_mode); + return 0; + } + DDBG("ConfigTransMode-Fail: unsupported trans mode %x!\n",trans_mode); + return -1; + } + // 配置Lora空中速率 + case CONFIG_AIR_RATE: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigAirRate-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint8 air_rate = *((uint8*)args); + if (air_rate >= AIR_RATE_2D4K && air_rate <= AIR_RATE_62D5K) + { + // 判断参数是否有必要覆写 + if (register_cfg.REG0 & 0X7 == air_rate) + { + DDBG("ConfigAirRate-Success: the same air rate %x!\n",air_rate); + return 0; + } + air_rate = ((register_cfg.REG0 >> 3) << 3) | air_rate; + if (0 != WriteRegister(adapter,0X2,1,&air_rate)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigAirRate-Fail: write register fail %x!\n",air_rate); + return -1; + } + register_cfg.REG0 = air_rate; + DDBG("ConfigAirRate-Success: config air rate success %x!\n",air_rate & 0X7); + return 0; + } + DDBG("ConfigAirRate-Fail: unsupported air rate %x!\n",air_rate); + return -1; + } + // 配置Lora的波特率 + case CONFIG_BAUD_RATE: + { + uint8 arg_rate = *((uint8*)args); + uint8 temp_rate = arg_rate; + if (arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE || arg_rate == E220_DEFAULT_USED_BAUD_RATE_BYTE) + { + // 判断参数是否有必要覆写 + if (register_cfg.REG0 & 0XE0 == arg_rate) + { + DDBG("ConfigBaudRate-Success: the same baud rate %x!\n",arg_rate); + return 0; + } + temp_rate = register_cfg.REG0 & 0X1F | arg_rate; + // 先更改寄存器的波特率 + if (0 != WriteRegister(adapter,0X2,1,&temp_rate)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigBaudRate-Fail: write register config fail %x!\n",temp_rate); + return -1; + } + register_cfg.REG0 = temp_rate; + // 重新配置串口波特率 + serial_cfg.serial_baud_rate = arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE ? BAUD_RATE_9600 : BAUD_RATE_115200; + struct PrivIoctlCfg ioctl_cfg; + ioctl_cfg.ioctl_driver_type = SERIAL_TYPE; + ioctl_cfg.args = &serial_cfg; + // 重新配置串口波特率 + if (0 != PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigBaudRate-Fail: config serail fail %x!\n",temp_rate); + return -1; + } + DDBG("ConfigBaudRate-Success: config baud rate success %d !\n",arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE ? BAUD_RATE_9600 : BAUD_RATE_115200); + return 0; + } + DDBG("ConfigBaudRate-Fail: unsupported baud rate %x!\n",arg_rate); + return -1; + } + // 配置Lora地址参数 + case CONFIG_ADDRESS: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigAddress-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint16 address = *((uint16*)args); + if ((address >> 8) | 0XFF == register_cfg.ADDH && address & 0XFF == register_cfg.ADDL) + { + DDBG("ConfigAddress-Success: the same address %d!\n",address); + return 0; + } + register_cfg.ADDH = (address >> 8) & 0XFF; + register_cfg.ADDL = address & 0XFF; + if (0 != WriteRegister(adapter,0X0,2,®ister_cfg)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigAddress-Fail: write register fail %d!\n",address); + return -1; + } + DDBG("ConfigAddress-Success: config address success %d!\n",address); + return 0; + } + // 配置Lora信道参数 + case CONFIG_CHANNEL: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigChannel-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint8 channel = *((uint8*)args); + if (channel <= E220_MAX_CHANNEL_NUMBER) + { + if (register_cfg.REG2 == channel) + { + DDBG("ConfigChannel-Success: the same channel %d!\n",channel); + return 0; + } + register_cfg.REG2 = channel; + if (0 != WriteRegister(adapter,0X4,1,®ister_cfg.REG2)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigChannel-Fail: write register fail %d!\n",channel); + return -1; + } + DDBG("ConfigChannel-Success: config channel success %X !\n",channel); + return 0; + } + DDBG("ConfigChannel-Fail: unsupported channel %d!\n",channel); + return -1; + } + // 打印配置信息 + case CONFIG_SHOW: + { + DDBG("ConfigShow SerailTime: %d",serial_cfg.serial_timeout); + PrintRegister("CONFIG_SHOW"); + return 0; + } + default: + DDBG("E220Ioctl-Failed: config item unsupported %d!\n",config_item); + return -1; + } +} + +uint8 E220Send(struct Adapter *adapter, const void *buf, uint32 len) +{ + if (STATE_OPENED != lora_state) + { + DDBG("E220Send-Fail: open lora before send data!\n"); + return -1; + } + // 切换至传输模式 + if (0 != E220LoraModeConfig(MODE_TRANSFER_MODE)) + { + DDBG("E220Send-Fail: change mode transfer fail!\n"); + return -1; + } + + if (len != PrivWrite(adapter->fd, (void *)buf, len)) + { + DDBG("E220Send-Fail: send data fail!\n"); + return -1; + } + return 0; +} + +uint8 E220Recv(struct Adapter *adapter, void *buf, uint32 len) +{ + if (STATE_OPENED != lora_state) + { + DDBG("E220Recv-Fail: open lora before recv!\n"); + return -1; + } + // 确保传输模式 + if (0 != E220LoraModeConfig(MODE_TRANSFER_MODE)) + { + return -1; + } + + int recv_len = 0, recv_len_continue = 0; + + uint8 *recv_buf = PrivMalloc(len); + // 读取数据并返回已读取数据量 + recv_len = PrivRead(adapter->fd, recv_buf, len); + if (recv_len) { + while (recv_len < len) { + // 读取超时后退出循环 + recv_len_continue = PrivRead(adapter->fd, recv_buf + recv_len, len - recv_len); + if (recv_len_continue) { + recv_len += recv_len_continue; + } else { + recv_len = 0; + break; + } + } + memcpy(buf, recv_buf, len); + } + PrivFree(recv_buf); + return recv_len; +} diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.h b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.h new file mode 100755 index 000000000..b69d1a75d --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.h @@ -0,0 +1,146 @@ +/* +* 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. +*/ + +/** +* @file: e220.h +* @brief: E220 module underlying related configuration driver functions declarations +* @version: 0.1 +* @author: YUNFEI CHU +* @date: 2023/10/01 +* +*/ + +#include +#include + +#ifndef __E220_H__ +#define __E220_H__ + +// #define __DRIVER_DEBUG__ // 驱动调试信息输出开关 +#ifdef __DRIVER_DEBUG__ +#define DDBG(format,...) printf(format, ##__VA_ARGS__) +#else +#define DDBG(format,...) +#endif // __DRIVER_DEBUG__ + +#define E220_M0_PATH 32 // M0管脚位置 +#define E220_M1_PATH 33 // M1管脚位置 +#define E220_PIN_DRIVER "/dev/pin_dev" // PIN驱动名称 +#define E220_UART_DRIVER "/dev/uart2_dev2" // UART驱动名称 +#define E220_ADDRESS 0XA // 地址默认值 +#define E220_DAFAULT_SERIAL_TIMEOUT 3000 // 串口默认3S超时 +#define E220_DEFAULT_CONFIG_BAUD_RATE BAUD_RATE_9600 // 在进行寄存器参数配置时,除了波特率本身的配置外,其他的配置只能在9600进行配置 +#define E220_DEFAULT_CONFIG_BAUD_RATE_BYTE 0X60 // 9600波特率 +#define E220_DEFAULT_USED_BAUD_RATE BAUD_RATE_115200 // 在进行使用时可以切换为该波特率,但是在配置时需要切换回配置波特率(9600) +#define E220_DEFAULT_USED_BAUD_RATE_BYTE 0XE0 // 115200波特率 + +#define E220_MAX_CHANNEL_NUMBER 83 // E220最多支持84个信道,0~83 +#define E220_DEFAULT_CHANNEL 0XA // 默认通道 + +#define E220_DEFAULT_AIR_RATE 0X0 // 默认模块以2.4K的空中速率进行通信 + +#define E220_DEFAULT_TRANS_MODE 0X40 // 默认使用定点传输传输模式 +#define E220_CONFIG_DELAY 1000 // 配置的硬件响应时间 + +#define E220_YES 0XFF +#define E220_NO 0X00 + +enum LoraMode // E220工作模式,支持四种,配置模式与休眠模式一致 +{ + MODE_TRANSFER_MODE = 0, // M0 : M1 = 0 : 0 可发可收 + MODE_WOR_SEND, // M0 : M1 = 1 : 0 可发可收 + MODE_WOR_RECV, // M0 : M1 = 0 : 1 可收不发 + MODE_CONFIG_SLEEP // M0 : M1 = 1 : 1 不收不发 +}; + +enum LoraState // 硬件状态 +{ + STATE_CLOSED, // 硬件关闭 + STATE_OPENED, // 已正常开启 + STATE_BROKEN // 硬件损坏 +}; + +enum LoraTransMode // 模块传输模式 +{ + TRANS_MODE_HYALINE = 0X0, // 透明传输 + TRANS_MODE_ORIENTED = 0X40, // 定点传输 +}; + +enum LoraAirRate // 空中速率,通信双方必须设置一致的空中速率方可进行通信 +{ + AIR_RATE_2D4K = 0X0, // 2.4K + AIR_RATE_4D8K = 0X3, // 4.8K + AIR_RATE_9D6K = 0X4, // 9.6K + AIR_RATE_19D2K = 0X5, // 19.2K + AIR_RATE_38D4K = 0X6, // 38.4K + AIR_RATE_62D5K = 0X7 // 62.5K +}; + +enum LoraConfig // 支持IOCTL函数进行配置的配置项枚举,某些项只支持9600波特率配置 +{ + CONFIG_SERIAL_TIME_OUT = 0X0, // 串口超时时间 + CONFIG_LORA_MODE = 0X1, // 配置Lora模式 + CONFIG_TRANS_MODE = 0X2, // 配置Lora传输模式(9600) + CONFIG_AIR_RATE = 0X3, // 配置空中速率(9600) + CONFIG_BAUD_RATE = 0X4, // 配置波特率 + CONFIG_ADDRESS = 0X5, // 配置设备地址(9600) + CONFIG_CHANNEL = 0X6, // 配置设备信道(9600) + CONFIG_SHOW = 0X7 // 打印配置信息 +}; + +extern enum LoraMode current_mode; // 当前模块处于什么模式 +extern enum LoraState lora_state; // 当前模块的状态 + +/** + * @brief: 打开硬件设施,开启串口并按照配置的初始化参数配置相关硬件、串口 + * 重复开启会直接返回,开启失败后不能重复尝试 + * @param adapter: 任何状态 + * @return: 0 -> Success : -1 -> Fail +*/ +uint8 E220Open(struct Adapter *adapter); + +/** + * @brief: 关闭硬件设施,关闭串口,同时会将所有的数据回复到原始的配置状态 + * 关闭失败默认为硬件损坏,未开启不可关闭 + * @param adapter: 任何状态 + * @return: 0 -> Success (硬件不是开启状态即成功): -1 -> Fail (硬件关闭过错报错) +*/ +uint8 E220Close(struct Adapter *adapter); + +/** + * @brief: 给处于开启状态的硬件配置相关信息,包括串口配置,寄存器参数等等 + * 支持的配置类型为@LoraConfig + * @param adapter: 打开状态 + * @param config_item: 配置类型 + * @param args: 配置参数 + * @return: 0 -> Success : -1 -> Fail +*/ +uint8 E220Ioctl(struct Adapter *adapter, int config_item, void *args); + +/** + * @brief: 通过硬件发送指定长度的数据 + * @param adapter: 打开状态 + * @param buf: 数据缓存区 + * @param len: 缓存区大小 + * @return 0 -> Success(全部发送完毕) : -1 -> Fail(未发送或发送不完整) +*/ +uint8 E220Send(struct Adapter *adapter, const void *buf, uint32 len); + +/** + * @brief: 通过硬件接受指定长度的数据到缓冲区 + * @param adapter: 打开状态 + * @param buf: 接受缓冲区 + * @param len: 需要接受的数据长度,该长度小于等于接受缓存区长度 + * @return 0 -> 接收到了指定长度的数据 : -1 -> 未接受到指定长度的数据或者串口超时 +*/ +uint8 E220Recv(struct Adapter *adapter, void *buf, uint32 len); +#endif // __E220_H__ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.c b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.c new file mode 100755 index 000000000..bf0700679 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.c @@ -0,0 +1,1203 @@ +/* +* 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. +*/ + +/** +* @file: lora_mac.c +* @brief: Lora private network protocol related data structure implementation +* @version: 0.1 +* @author: YUNFEI CHU +* @date: 2023/10/01 +* +*/ + +#include "lora_mac.h" + +static const uint8 crc_hi_table[] = + { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + }; + +static const uint8 crc_lo_table[] = + { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, + 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, + 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, + 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, + 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, + 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, + 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, + 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, + 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, + 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, + 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, + 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, + 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + }; + +// 相关数据缓冲区,全局变量 +struct LoraFrame send_frame; // 数据帧发送缓冲区 +struct LoraFrame recv_frame; // 数据帧接受缓存区 +uint8 send_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据发送缓冲区 +uint8 recv_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据接受缓存区 +uint8 total_data_buffer[FRAME_MAX_DATA_LENGTH]; // 数据帧加数据的最大缓冲区 + +void InitLoraFrame(struct LoraFrame* frame) +{ + // 数据清空 + memset(frame,0,sizeof(struct LoraFrame)); + // 设置帧头、帧尾 + frame->begin_mark = FRAME_BEGIN_MARK; + frame->end_mark = FRAME_END_MARK; +} + +void ShowLoraFrame(struct LoraFrame* frame,char* prefix) +{ + printf("%s -> ",prefix); + printf("session_id:%d,dev_addr:%X,down_channel:%X,frame_confirm:%X ",frame->session_id,frame->dev_addr,frame->down_channel,frame->frame_confirm); + switch (frame->frame_type) + { + case E_G_JOIN: + printf("-E_G_JOIN-"); + break; + case E_G_QUIT: + printf("-E_G_QUIT-"); + break; + case E_G_DATA: + printf("-E_G_DATA-"); + printf("user_data:%s ",frame->user_data); + break; + case G_E_ANS: + printf("-G_E_ANS-"); + break; + } + printf("frame_attach:%d,crc_hi:%X,crc_lo:%X\n",frame->frame_attach,frame->crc_hi,frame->crc_lo); +} + +void CalCrcByte(uint8* data, uint8 data_length,unsigned int* temp,uint8* crc_hi,uint8* crc_lo) +{ + for (int i = 0; i < data_length; i++) + { + *temp = *crc_hi ^ *data++; + *crc_hi = *crc_lo ^ crc_hi_table[*temp]; + *crc_lo = crc_lo_table[*temp]; + } +} + +uint8 CalCrlLoraFrame(struct LoraFrame* frame) +{ + uint8 crc_hi = 0xFF; + uint8 crc_lo = 0xFF; + unsigned int temp; + // 转化为字节的方式进行数据访问 + uint8 *bit_arr = (uint8 *)frame; + // 计算除开帧头后面11位元数据的校验值 + CalCrcByte(bit_arr + FRAME_CRC_INDEX_BEGIN,FRAME_CRC_LENGTH,&temp,&crc_hi,&crc_lo); + if (frame->frame_type == E_G_DATA) + { + // 数据传输必须要携带数据,否则就是出错 + if (frame->frame_attach != 0 && frame->user_data != NULL) + { + // 计算用户数据的校验值 + CalCrcByte(frame->user_data,frame->frame_attach,&temp,&crc_hi,&crc_lo); + } + else + { + printf("CalCrlLoraFrame: have no data!\n"); + return -1; + } + } + frame->crc_lo = crc_lo; + frame->crc_hi = crc_hi; + return 0; +} + +uint8 CheckCrcLoraFrame(struct LoraFrame* frame) +{ + uint8 crc_hi_tmp = frame->crc_hi; + uint8 crc_lo_tmp = frame->crc_lo; + + if (0 == CalCrlLoraFrame(frame)) + { + if (crc_hi_tmp == frame->crc_hi && crc_lo_tmp == frame->crc_lo) + { + return 0; + } + } + return -1; +} + +static const uint16 header_mark = FRAME_BEGIN_MARK & 0XFF; // 数据帧头部 +static const uint16 tail_mark = FRAME_END_MARK & 0XFF; // 数据帧尾部 +static uint16 header_buffer = 0; // 用于数据帧头部检测 + +uint8 LoraRecvFrame(struct Adapter *adapter, struct LoraFrame *recv_buffer) +{ + if (lora_state != STATE_OPENED) + { + printf("LoraRecvFrame-Fail: lora closed or broken!\n"); + return -1; + } + // 初始化接受缓冲区 + memset(recv_buffer, 0, sizeof(struct LoraFrame)); + + header_buffer = 0; + // 用于查找数据帧的帧头 + uint8 *header_temp = (uint8*)(&header_buffer); + // 查找数据帧的开始标志 + while (1 == E220Recv(adapter, &header_temp[1], 1)) // 查找开始标志 + { + // 找到了开始标识的一半 + if (header_mark == header_temp[1]) + { + // 判断上一个字节是否是开始标志,若是,则已找到,若不是,则继续 + if (header_temp[1] == header_temp[0]) + { + recv_buffer->begin_mark = FRAME_BEGIN_MARK; + // 接收帧头以下,用户数据以上的数据 + if (FRAME_NECK_LENGTH == E220Recv(adapter, &recv_buffer->session_id, FRAME_NECK_LENGTH)) + { + // 查看是否有用户数据 + if (E_G_DATA == recv_buffer->frame_type) + { + // 如果携带了用户数据则接收用户数据 + if (0 != recv_buffer->frame_attach) + { + // 重置用户数据缓冲区 + memset(recv_data_buffer, 0, FRAME_MAX_USER_DATA_LENGTH); + // 接收用户数据 + if (recv_buffer->frame_attach != E220Recv(adapter, recv_data_buffer, recv_buffer->frame_attach)) + { + printf("LoraRecvFrame-Fail: recv user data fail!\n"); + header_buffer = 0; + break; + } + // 将数据嫁接到数据帧 + recv_buffer->user_data = recv_data_buffer; + } + } + // 开始接受最后的数据 + if (FRAME_FOOT_LENGTH != E220Recv(adapter,&recv_buffer->crc_hi,FRAME_FOOT_LENGTH)) + { + printf("LoraRecvFrame-Fail: recv foot frame fail!\n"); + header_buffer = 0; + break; + } + // 开始检查数据帧尾巴是否正确 + if (FRAME_END_MARK != recv_buffer->end_mark) + { + printf("LoraRecvFrame-Fail: frame end mark err!\n"); + header_buffer = 0; + break; + } + // 开始进行数据帧校验 + if (0 != CheckCrcLoraFrame(recv_buffer)) + { + printf("LoraRecvFrame-Fail: check frame crc fail!\n"); + header_buffer = 0; + break; + } + // 接受数据帧成功 + ShowLoraFrame(recv_buffer,"LoraRecvFrame"); + header_buffer = 0; + return 0; + } + else + { + printf("LoraRecvFrame-Fail: recv neck frame fail!\n"); + header_buffer = 0; + break; + } + } + } + // 将接受到的数据放置在上一个位置 + header_temp[0] = header_temp[1]; + } + header_buffer = 0; + return -1; +} + +uint8 LoraSendFrame(struct Adapter* adapter, struct LoraFrame* send_buffer,uint16 addr,uint8 channel) +{ + // 判断lora是否已经正确开启 + if (lora_state != STATE_OPENED) + { + printf("LoraSendFrame-Fail: lora closed or broken!\n"); + return -1; + } + // 计算数据帧的校验值 + if (0 != CalCrlLoraFrame(send_buffer)) + { + printf("LoraSendFrame-Fail: cal_crc fail!\n"); + return -1; + } + // 记录整个数据帧的长度 + int fram_length = 0; + // 重置数据发送缓冲区 + memset(&total_data_buffer,0,FRAME_MAX_DATA_LENGTH); + // 组装目标地址 + total_data_buffer[0] = (addr >> 8) & 0XFF; + total_data_buffer[1] = addr & 0XFF; + total_data_buffer[2] = channel; + fram_length += 3; + + // 数据帧前半部分 + memcpy(&total_data_buffer[fram_length],send_buffer,FRAME_META_LENGTH - FRAME_FOOT_LENGTH); + fram_length += (FRAME_META_LENGTH - FRAME_FOOT_LENGTH); + // 用户额外数据 + if (E_G_DATA == send_buffer->frame_type) + { + memcpy(&total_data_buffer[fram_length],send_buffer->user_data,send_buffer->frame_attach); + fram_length += send_buffer->frame_attach; + } + // 剩下来的数据 + memcpy(&total_data_buffer[fram_length],&send_buffer->crc_hi,FRAME_FOOT_LENGTH); + fram_length += FRAME_FOOT_LENGTH; + // 发送所有数据 + if (0 != E220Send(adapter,&total_data_buffer,fram_length)) + { + printf("LoraSendFrame-Fail: send fail!\n"); + return -1; + } + ShowLoraFrame(send_buffer,"LoraSendFrame"); + return 0; +} + +uint16 GetSessionId(void) +{ + return rand() & 0XFFFF; +} + +void SignalDetector(struct Adapter *adapter,uint8 download_channel,uint8 upload_channel) +{ + // 切换至上行信道,客户端上行通道为广播信息(地址为OXFFFF),该信道所有终端都可以接收到 + E220Ioctl(adapter,CONFIG_CHANNEL,&upload_channel); + // 监听信道 + uint8 temp_dst; + while (1 == E220Recv(adapter, &temp_dst, 1)) + { + printf("LoraSignalDetector-Fail: Channel occupied!\n"); + } + // 切换至切换至下行通道,准备发送数据 + E220Ioctl(adapter,CONFIG_CHANNEL,&download_channel); + printf("LoraSignalDetector-Success: Channel idle!\n"); +} + +int UsrAdapterLoraInit(void) +{ + struct Adapter *adapter = PrivMalloc(sizeof(struct Adapter)); + if (!adapter) + { + PrivFree(adapter); + return -1; + } + memset(adapter, 0, sizeof(struct Adapter)); + // Adapter注册函数,终端和网关需要自己实现并开放接口 + if (0 != UsrAdapterLoraRegister(adapter)) + { + printf("UsrAdapterLoraInit-Fail: register lora adapter error\n"); + PrivFree(adapter); + return -1; + } + // 获取函数操作集合,终端和网关需要自己实现并开放接口 + AdapterProductInfoType product_info = LoraAttach(adapter); + if (NULL == product_info) + { + printf("UsrAdapterLoraInit-Fail: e220 attach error\n"); + PrivFree(adapter); + return -1; + } + // 配置Adapter属性 + adapter->product_info_flag = 1; + adapter->info = product_info; + adapter->done = product_info->model_done; + adapter->adapter_status = REGISTERED; + // 创建信号量和互斥量 + PrivSemaphoreCreate(&adapter->sem, 0, 0); + PrivMutexCreate(&adapter->lock, 0); + + printf("UsrAdapterLoraInit-Success!\n"); + return 0; +} + +#ifdef AS_LORA_GATEWAY + +struct GatewayParam gateway_param = // 网关配置参数 +{ + .dev_addr = LORA_GATEWAY_ADDRESS, + .channel = LORA_GATEWAY_CHANNEL, + .recv_time = LORA_RECV_TIME, + .air_rate = LORA_AIR_RATE, + .frame_retry = LORA_FRAME_RETRY, + .gateway_state = GATEWAY_CLOSED, + .node_infos = {NULL}, + .node_count = 0, +}; + +struct PrivProtocolDone gateway_done = // 网关操作函数集合 +{ + .open = GatewayOpen, + .close = GatewayClose, + .ioctl = GatewayIoctl, + .setup = GatewaySetup, + .setdown = GatewaySetDown, + .netstat = GatewayNetState +}; + +int (*gateway_handlers[])(struct Adapter*,struct LoraFrame*) = // 网关处理程序 +{ + [E_G_JOIN] = GatewayJoinHandler, + [E_G_QUIT] = GatewayQuitHandler, + [E_G_DATA] = GatewayDataSendHandler, +}; + +static UtaskType gateway_task; // 网关任务 +static int32 gateway_task_id = -1; // 任务ID + +/** + * @brief: 根据地址和信道定位到终端的信息 +*/ +static struct EndNodeInfo* GetEndNodeInfo(uint16 node_addr,uint8 channel) +{ + for (int i = 0; i < gateway_param.node_count; i++) + { + if (node_addr == gateway_param.node_infos[i]->node_addr && channel == gateway_param.node_infos[i]->node_down_channel) + { + return gateway_param.node_infos[i]; + } + } + return NULL; +} + +int GatewayOpen(struct Adapter *adapter) +{ + switch (gateway_param.gateway_state) + { + case GATEWAY_WORKING: case GATEWAY_OPENED: + printf("GatewayOpen-Success: lora already opened before!\n"); + return 0; + case GATEWAY_BROKEN: + printf("GatewayOpen-Fail: lora broken!\n"); + return -1; + case GATEWAY_CLOSED: + PrivMutexObtain(&adapter->lock); + // 打开底层硬件 + if (0 != E220Open(adapter)) + { + gateway_param.gateway_state = GATEWAY_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 按照网关的配置信息配置底层的硬件 + uint32 recv_time = gateway_param.recv_time * 1000; + if (0 != E220Ioctl(adapter,CONFIG_CHANNEL,&gateway_param.channel) + || 0 != E220Ioctl(adapter,CONFIG_ADDRESS,&gateway_param.dev_addr) + || 0 != E220Ioctl(adapter,CONFIG_AIR_RATE,&gateway_param.air_rate) + || 0 != E220Ioctl(adapter,CONFIG_SERIAL_TIME_OUT,&recv_time)) + { + printf("GatewayOpen-Fail: config gateway fail -> channel: %X,address: %X,air_rate: %X!\n",gateway_param.channel,gateway_param.dev_addr,gateway_param.air_rate); + gateway_param.gateway_state = GATEWAY_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 更新网关的状态信息 + gateway_param.gateway_state = GATEWAY_OPENED; + printf("GatewayOpen-Success: channel: %X,address: %X!\n",gateway_param.channel,gateway_param.dev_addr); + PrivMutexAbandon(&adapter->lock); + return 0; + default: + printf("GatewayOpen-Fail: unknown gateway state!\n"); + return -1; + } +} + +int GatewayClose(struct Adapter *adapter) +{ + switch (gateway_param.gateway_state) + { + case GATEWAY_CLOSED: case GATEWAY_BROKEN: + printf("GatewayClose-Success: lora close or broken before!\n"); + return 0; + case GATEWAY_WORKING: + printf("GatewayClose-Fail: quit net before close!\n"); + return -1; + case GATEWAY_OPENED: + PrivMutexObtain(&adapter->lock); + if (0 != E220Close(adapter)) + { + gateway_param.gateway_state = GATEWAY_BROKEN; + printf("GatewayClose-Fail: close lora fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + gateway_param.gateway_state = GATEWAY_CLOSED; + printf("GatewayClose-Success\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + default: + printf("GatewayClose-Fail: unknown err!\n"); + return -1; + } +} + +int GatewayIoctl(struct Adapter *adapter, int cmd, void *args) +{ + switch (gateway_param.gateway_state) + { + case GATEWAY_WORKING: case GATEWAY_BROKEN: case GATEWAY_CLOSED: + printf("GatewayIoctl-Fail: cant be config when lora broken、working、clesed!\n"); + return -1; + case GATEWAY_OPENED: + PrivMutexObtain(&adapter->lock); + if (0 != E220Ioctl(adapter, cmd, args)) + { + gateway_param.gateway_state = GATEWAY_BROKEN; + printf("GatewayIoctl-Fail: ioctl fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + printf("GatewayIoctl-Success\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + default: + printf("GatewayIoctl-Fail: unknown err!\n"); + return -1; + } +} + +int GatewaySetup(struct Adapter *adapter) +{ + if (GATEWAY_BROKEN == gateway_param.gateway_state) + { + printf("GatewaySetup-Fail: lora broken!\n"); + return -1; + } + if (GATEWAY_CLOSED == gateway_param.gateway_state) + { + printf("GatewaySetup-Fail: please open lora before!\n"); + return -1; + } + if (GATEWAY_WORKING == gateway_param.gateway_state) + { + printf("GatewaySetup-Success: lora setup before!\n"); + return 0; + } + PrivMutexObtain(&adapter->lock); + // 配置硬件传输 + if (0 != E220Ioctl(adapter,CONFIG_LORA_MODE,MODE_TRANSFER_MODE)) + { + printf("GatewaySetup-Fail: config lora transfer fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + + gateway_task.prio = 24; + gateway_task.stack_size = 2048; + gateway_task.func_param = adapter; + gateway_task.func_entry = GatewayTask; + strncpy(gateway_task.name, "lora_gateway_task", strlen("lora_gateway_task")); + + gateway_task_id = UserTaskCreate(gateway_task); + + if (gateway_task_id < 0) + { + printf("GatewaySetup-Fail: create task fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + + if (0 == UserTaskStartup(gateway_task_id)) + { + gateway_param.gateway_state = GATEWAY_WORKING; + printf("GatewaySetup-Success: start task success!\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + } + else + { + UserTaskDelete(gateway_task_id); + gateway_task_id = -1; + printf("GatewaySetup-Fail: start task fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } +} + +int GatewaySetDown(struct Adapter *adapter) +{ + + if (GATEWAY_WORKING != gateway_param.gateway_state) + { + printf("GatewaySetDown-Success: not in working state!\n"); + return 0; + } + // 通知后台程序 + PrivSemaphoreAbandon(&adapter->sem); + PrivMutexObtain(&adapter->lock); + if (0 == UserTaskDelete(gateway_task_id)) + { + gateway_task_id = -1; + gateway_param.gateway_state = GATEWAY_OPENED; + printf("GatewaySetDown-Success!\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + } + printf("GatewaySetDown-Fail: delete task fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; +} + +int GatewayNetState(struct Adapter *adapter) +{ + printf("GatewayInfos: \n"); + printf("addr:%d,channel:%d,recv_time:%d,",gateway_param.dev_addr,gateway_param.channel,gateway_param.recv_time); + printf("air_rate:%d,state:%x,node_count:%d\n",gateway_param.air_rate,gateway_param.gateway_state,gateway_param.node_count); + for (int i = 0; i < gateway_param.node_count; i++) + { + struct EndNodeInfo *node = gateway_param.node_infos[i]; + printf("node_addr:%d,node_down_channel:%d!n",node->node_addr,node->node_down_channel); + } + return 0; +} + +void* GatewayTask(void* param) +{ + struct Adapter *gateway_adapter = (struct Adapter *)param; + if (gateway_adapter == NULL) + { + printf("GatewayTask-Fail: adapter is null!\n"); + return NULL; + } + + while (gateway_param.gateway_state != GATEWAY_WORKING) // 等待上一个函数修改网关状态 + { + PrivTaskDelay(100); + } + + while (1) // 业务大循环 + { + if (0 == PrivSemaphoreObtainNoWait(&gateway_adapter->sem)) + { + PrivTaskDelay(LORA_RECV_TIME); + continue; + } + PrivMutexObtain(&gateway_adapter->lock); + if (0 == LoraRecvFrame(gateway_adapter,&recv_frame)) + { + // 进行相应的处理 + if (0 == gateway_handlers[recv_frame.frame_type](gateway_adapter,&recv_frame)) + { + printf("GatewayTask: handle success!\n"); + } + else + { + printf("GatewayTask: handle fail!\n"); + } + } + PrivMutexAbandon(&gateway_adapter->lock); + } + + printf("GatewayTask: task exit!\n"); + return NULL; +} + +int GatewayJoinHandler(struct Adapter* adapter,struct LoraFrame* frame) +{ + // 获取终端的地址和信道,防止多个信道中同一个地址加入失败 + uint16 node_addr = frame->dev_addr; + uint8 node_channel = frame->down_channel; + struct EndNodeInfo* node = NULL; + // 从现有的数据中查找 + for (int i = 0; i < gateway_param.node_count; i++) + { + if (node_addr == gateway_param.node_infos[i]->node_addr && node_channel == gateway_param.node_infos[i]->node_down_channel) // 找到了这个终端,实现已经加入了 + { + node = gateway_param.node_infos[i]; + break; + } + } + // 如果事先没有加入就创建 + if (node == NULL) + { + if(gateway_param.node_count == LORA_GATEWAY_MAX_NODE) + { + printf("NodeJoinHandler: too much node!\n"); + return -1; + } + node = (struct EndNodeInfo *)PrivMalloc(sizeof(struct EndNodeInfo)); + memset(node, 0, sizeof(struct EndNodeInfo)); + node->node_addr = node_addr; + node->node_down_channel = node_channel; + gateway_param.node_infos[gateway_param.node_count] = node; + gateway_param.node_count++; + // 如果已经事先加入了,直接返回 + } + // 响应终端 + frame->frame_type = G_E_ANS; + frame->frame_confirm = FRAME_BYTE_NO; + frame->frame_attach = FRAME_BYTE_OK; + + if (0 != LoraSendFrame(adapter, frame,node->node_addr,node->node_down_channel)) + { + printf("NodeJoinHandler: response node fail!\n"); + return -1; + } + printf("NodeJoinHandler: join net success addr:%d,channel:%d!\n",node->node_addr,node->node_down_channel); + return 0; +} + +int GatewayQuitHandler(struct Adapter* adapter,struct LoraFrame* frame) +{ + uint16 node_addr = frame->dev_addr; + uint8 node_channel = frame->down_channel; + for (int i = 0; i < gateway_param.node_count; i++) // 查找是否存在这个终端 + { + if (node_addr == gateway_param.node_infos[i]->node_addr && node_channel == gateway_param.node_infos[i]->node_down_channel) + { + frame->frame_type = G_E_ANS; + frame->frame_confirm = FRAME_BYTE_NO; + frame->frame_attach = FRAME_BYTE_OK; + if (0 != LoraSendFrame(adapter, frame,gateway_param.node_infos[i]->node_addr,gateway_param.node_infos[i]->node_down_channel)) + { + printf("NodeQuitHandler: response node fail!\n"); + return -1; + } + // 释放相关的空间 + PrivFree(gateway_param.node_infos[i]); + for (int j = i; j < gateway_param.node_count - 1; j++) + { + gateway_param.node_infos[j] = gateway_param.node_infos[j+1]; + } + gateway_param.node_count--; + gateway_param.node_infos[gateway_param.node_count] = NULL; + break; + } + } + printf("NodeQuitHandler: quit net success or quit before!\n"); + return 0; +} + +int GatewayDataSendHandler(struct Adapter* adapter,struct LoraFrame* frame) +{ + // 查找该终端 + struct EndNodeInfo* node_info = GetEndNodeInfo(frame->dev_addr,frame->down_channel); + if (node_info == NULL) + { + printf("NodeDataSendHandler: no such node!\n"); + return 0; + } + // 响应数据上传 + frame->frame_type = G_E_ANS; + frame->frame_confirm = FRAME_BYTE_NO; + frame->frame_attach = LORA_OK; + if (0 != LoraSendFrame(adapter, frame,node_info->node_addr,node_info->node_down_channel)) + { + printf("NodeDataSendHandler: response fail!\n"); + return -1; + } + // TODO 数据上传在此处理 + printf("NodeDataSendHandler-Success: recv data: %s\n!",frame->user_data); + return 0; +} + +AdapterProductInfoType LoraAttach(struct Adapter *adapter) +{ + struct AdapterProductInfo *product_info = malloc(sizeof(struct AdapterProductInfo)); + if (!product_info) { + printf("LoraAttach-Failed-Node: malloc fail!\n"); + return NULL; + } + + strncpy(product_info->model_name, LORA_ADAPTER_NAME,sizeof(product_info->model_name)); + product_info->model_done = (void *)&gateway_done; + + return product_info; +} + +uint8 UsrAdapterLoraRegister(struct Adapter *adapter) +{ + strncpy(adapter->name, ADAPTER_LORA_NAME, NAME_NUM_MAX); + adapter->net_protocol = PRIVATE_PROTOCOL; + + adapter->net_role = GATEWAY; + adapter->net_role_id = gateway_param.channel; + adapter->adapter_param = (void *)(&gateway_param); + + adapter->adapter_status = UNREGISTERED; + + if (AdapterDeviceRegister(adapter) < 0) + { + printf("UsrAdapterLoraRegister-Fail: lora register error!\n"); + return -1; + } + + printf("UsrAdapterLoraRegister-Success!\n"); + return 0; +} +#else + +// 终端参数结构 +struct EndNodeParam node_param = +{ + .dev_addr = LORA_ADDRESS, + .down_channel = LORA_DOWN_CHANNEL, + .upload_channel = LORA_UP_CHANNEL, + .session_id = 0X0, + .node_state = NODE_STATE_CLOSED, + .air_rate = LORA_AIR_RATE, + .adr_enable = LORA_ADR_ENABLE, + .reconnect_enable = LORA_RECONNECT_ENABLE, + .recv_time = LORA_RECV_TIME, +}; +// 终端操作集合 +static struct PrivProtocolDone node_done = +{ + .open = NodeOpen, + .close = NodeClose, + .join = NodeJoin, + .send = NodeSend, + .quit = NodeQuit, + .netstat = NodeNetState +}; +// 空中速率枚举 +enum LoraAirRate air_rates[6] = + { + AIR_RATE_2D4K, + AIR_RATE_4D8K, + AIR_RATE_9D6K, + AIR_RATE_19D2K, + AIR_RATE_38D4K, + AIR_RATE_62D5K}; + +int air_rate_length = 6; + +/** + * @brief: 向特定信道以特定空中速率发送入网请求 + * @param adapter: 已打开的适配器 + * @param join_buffer: 已组装数据帧的缓冲区 + * @param upload_channel: 上行信道 + * @param air_rate: 空中速率 + * @return: 0 -> Success : -1 -> Fail(根据状态判定是否存在硬件损坏) +*/ +static uint8 TargetConnectGateway(struct Adapter *adapter,struct LoraFrame *join_buffer,uint8 upload_channel,enum LoraAirRate air_rate) +{ + if (0 != E220Ioctl(adapter, CONFIG_AIR_RATE, &air_rate)) // 配置硬件空中速率 + { + printf("TargetConnectGateway-Fail: config air rate fail %d!\n",air_rate); + node_param.node_state = NODE_STATE_BROKEN; + return -1; + } + node_param.air_rate = air_rate; + for (int i = 0; i < LORA_FRAME_RETRY; i++) + { + node_param.session_id = GetSessionId(); + send_frame.session_id = node_param.session_id; + + SignalDetector(adapter,node_param.down_channel,node_param.upload_channel); // 信道监听 + if (0 != LoraSendFrame(adapter, &send_frame, LORA_GATEWAY_ADDRESS,upload_channel)) // 发送入网请求 + { + node_param.node_state = NODE_STATE_BROKEN; + return -1; + } + PrivTaskDelay(LORA_TIME_ON_AIR * 1000); + while (0 == LoraRecvFrame(adapter, &recv_frame)) // 接收响应数据帧 + { + if (recv_frame.session_id == node_param.session_id) // 判断数据帧是否丢失 + { + if (G_E_ANS == recv_frame.frame_type && FRAME_BYTE_OK == recv_frame.frame_attach) // 成功加入网关 + { + node_param.node_state = NODE_STATE_CONNECT; // 更改终端状态 + node_param.upload_channel = upload_channel; + node_param.air_rate = air_rate; + printf("ConnectGateway-Success: upload_channel: %d, air_rate: %d !\n",upload_channel,air_rate); + return 0; + } + } + } + } + printf("ConnectGateway-Fail: upload_channel: %d, air_rate: %d !\n",upload_channel,air_rate); + return -1; +} + +int NodeOpen(struct Adapter *adapter) +{ + // 获取硬件控制权 + PrivMutexObtain(&adapter->lock); + // 硬件损坏 + if (NODE_STATE_BROKEN == node_param.node_state) + { + printf("NodeOpen-Fail: lora broken!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 硬件已打开 + if (NODE_STATE_CONNECT == node_param.node_state || NODE_STATE_DISCONNECT == node_param.node_state) + { + printf("NodeOpen-Success: opened before!\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + } + // 打开硬件设施 + if (0 != E220Open(adapter)) + { + printf("NodeOpen-Fail: lora open fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 配置硬件地址、信道、超时时间,空中速率 + int time_out = node_param.recv_time * 1000; + if (0 != E220Ioctl(adapter,CONFIG_ADDRESS,&node_param.dev_addr) + || 0 != E220Ioctl(adapter,CONFIG_CHANNEL,&node_param.down_channel) + || 0 != E220Ioctl(adapter,CONFIG_SERIAL_TIME_OUT,&time_out) + || 0 != E220Ioctl(adapter,CONFIG_AIR_RATE,&node_param.air_rate)) + { + printf("NodeOpen-Fail: condfi client fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + if (0 != E220Ioctl(adapter, CONFIG_LORA_MODE, &aim_mode)) + { + printf("NodeOpen-Fail: config lora sleep fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + printf("NodeOpen-Success: node open success address: %d, dwon_channel: %d, upload_channel: %d!\n",node_param.dev_addr,node_param.down_channel,node_param.upload_channel); + node_param.node_state = NODE_STATE_DISCONNECT; + PrivMutexAbandon(&adapter->lock); + return 0; +} + +int NodeClose(struct Adapter *adapter) +{ + if (NODE_STATE_BROKEN == node_param.node_state || NODE_STATE_CLOSED == node_param.node_state) + { + printf("NodeClose-Success: lora broken or cloded before!\n"); + return 0; + } + if (NODE_STATE_CONNECT == node_param.node_state) + { + printf("NodeClose-Fail: quit net before close node!\n"); + return -1; + } + PrivMutexObtain(&adapter->lock); + // 关闭硬件设施 + if (0 != E220Close(adapter)) + { + printf("NodeClose-Fail: lora close fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + printf("NodeClose-Success: node close success!\n"); + node_param.node_state = NODE_STATE_CLOSED; + PrivMutexAbandon(&adapter->lock); + return 0; +} + +int NodeJoin(struct Adapter *adapter, unsigned char *net_group) +{ + switch (node_param.node_state) + { + case NODE_STATE_BROKEN: + printf("NodeJoin-Fail: lora broken!\n"); + return -1; + case NODE_STATE_CLOSED: + printf("NodeJoin-Fail: open lora before join net!\n"); + return -1; + case NODE_STATE_CONNECT: + if (node_param.upload_channel == *net_group && NODE_STATE_CONNECT == node_param.node_state) + { + printf("NodeJoin-Success: joined before!\n"); + return 0; + } + // 退出当前网络 + if (NODE_STATE_CONNECT == node_param.node_state) + { + // 退出当前的网络,断开上行通道 + if (0 != NodeQuit(adapter, &node_param.upload_channel)) + { + printf("NodeJoin-Fail: quit net before fail!\n"); + return -1; + } + } + default: + // 构建入网数据帧 + InitLoraFrame(&send_frame); + send_frame.dev_addr = node_param.dev_addr; // 当前设备地址 + send_frame.down_channel = node_param.down_channel; // 下行通道信息,当前设备的信道 + send_frame.frame_type = E_G_JOIN; + send_frame.frame_confirm = LORA_OK; + // 获取终端控制权 + PrivMutexObtain(&adapter->lock); + // 以默认的空中速率尝试连接传入的网络 + if (0 == TargetConnectGateway(adapter, &send_frame, *net_group,node_param.air_rate)) + { + PrivMutexAbandon(&adapter->lock); + return 0; + } + else + { + // ADR自动寻找网关 + if (LORA_OK == node_param.adr_enable) + { + // 默认连接出错 + if (NODE_STATE_BROKEN == node_param.node_state) + { + printf("NodeJoin-Fail: send join frame fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 尝试连接其他的网关 + for (int i = 0; i < E220_MAX_CHANNEL_NUMBER; i++) + { + for (int j = 0; j < air_rate_length; j++) + { + printf("NodeJoin-Try: channel: %d air rate: %x!\n",i,air_rates[j]); + if (0 == TargetConnectGateway(adapter, &send_frame, i, air_rates[j])) + { + PrivMutexAbandon(&adapter->lock); + return 0; + } + if (NODE_STATE_BROKEN == node_param.node_state) + { + printf("NodeJoin-Fail: send join frame fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + } + } + // 不存在网关 + printf("NodeJoin-Fail: no gateway exits!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + else + { + printf("NodeJoin-Fail: the gateway config not exits!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + } + } +} + +int NodeSend(struct Adapter *adapter, const void *buf, size_t len) +{ + // 只有处于联网状态的终端才可发送数据 + if (NODE_STATE_CONNECT != node_param.node_state) + { + printf("NodeSend-Fail: open lora and join net before send data!\n"); + return -1; + } + if (len > FRAME_MAX_USER_DATA_LENGTH) // 数据量太大 + { + printf("NodeSend-Fail: to much data for send!\n"); + return -1; + } + // 构造数据帧并发送数据 + InitLoraFrame(&send_frame); // 初始化 + send_frame.dev_addr = node_param.dev_addr; + send_frame.down_channel = node_param.down_channel; + send_frame.frame_type = E_G_DATA; + send_frame.frame_confirm = LORA_OK; + // 复制待发送的数据 + memcpy(send_data_buffer,buf,len); + send_frame.user_data = send_data_buffer; + send_frame.frame_attach = len; + + PrivMutexObtain(&adapter->lock); + // 发送用户数据帧 + for (int i = 0; i < LORA_FRAME_RETRY; i++) + { + node_param.session_id = GetSessionId(); + send_frame.session_id = node_param.session_id; + + // 信道监听 + SignalDetector(adapter,node_param.down_channel,node_param.upload_channel); + // 发送数据帧 + if (0 != LoraSendFrame(adapter, &send_frame,LORA_GATEWAY_ADDRESS,node_param.upload_channel)) + { + PrivMutexAbandon(&adapter->lock); + node_param.node_state = NODE_STATE_BROKEN; + return -1; + } + // 等待信号到达 + PrivTaskDelay(LORA_TIME_ON_AIR * 1000); + // 准备数据缓冲区并等待网关响应 + while (0 == LoraRecvFrame(adapter, &recv_frame)) + { + // 判断数据是否丢失 + if (recv_frame.session_id == node_param.session_id) + { + // 判断是否发送成功 + if (G_E_ANS == recv_frame.frame_type && LORA_OK == recv_frame.frame_attach) + { + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + E220Ioctl(adapter,CONFIG_LORA_MODE,&aim_mode); + PrivMutexAbandon(&adapter->lock); + return 0; + } + + } + } + } + PrivMutexAbandon(&adapter->lock); + // 判定网关掉线 + node_param.node_state = NODE_STATE_DISCONNECT; + // 数据发送失败,需要重新联网 + if (LORA_OK == node_param.reconnect_enable) + { + printf("NodeSend-Fail: try to reconnect gateway!\n"); + if (0 == NodeJoin(adapter,&node_param.upload_channel)) + { + // 重新发送数据 + return NodeSend(adapter,buf,len); + } + printf("NodeSend-Fail: try to reconnect gateway fail!\n"); + } + return -1; +} + +int NodeQuit(struct Adapter *adapter, unsigned char *net_group) +{ + if (NODE_STATE_CONNECT != node_param.node_state) + { + printf("NodeQuit-Success: disconnected before!\n"); + return 0; + } + + if (node_param.upload_channel != *net_group) + { + printf("NodeQuit-Success: not such net!\n"); + return 0; + } + + // 构建退网数据帧 + InitLoraFrame(&send_frame); + send_frame.dev_addr = node_param.dev_addr; + send_frame.down_channel = node_param.down_channel; + send_frame.frame_type = E_G_QUIT; + send_frame.frame_confirm = FRAME_BYTE_OK; + + PrivMutexObtain(&adapter->lock); + // 发送用户数据帧 + for (int i = 0; i < LORA_FRAME_RETRY; i++) + { + node_param.session_id = GetSessionId(); + send_frame.session_id = node_param.session_id; + + // 信道监听 + SignalDetector(adapter,node_param.down_channel,node_param.upload_channel); + // 发送数据帧 + if (0 != LoraSendFrame(adapter, &send_frame,LORA_GATEWAY_ADDRESS,node_param.upload_channel)) + { + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 等待信号到达 + PrivTaskDelay(LORA_TIME_ON_AIR * 1000); + // 准备数据缓冲区并等待网关响应 + while (0 == LoraRecvFrame(adapter, &recv_frame)) + { + // 判断数据是否丢失 + if (recv_frame.session_id == node_param.session_id) + { + // 判断是否退出成功 + if (G_E_ANS == recv_frame.frame_type && FRAME_BYTE_OK == recv_frame.frame_attach) + { + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + E220Ioctl(adapter,CONFIG_LORA_MODE,&aim_mode); + PrivMutexAbandon(&adapter->lock); + return 0; + } + } + } + } + printf("NodeQuit-Fail: quit net fail %d!\n",*net_group); + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + E220Ioctl(adapter,CONFIG_LORA_MODE,&aim_mode); + PrivMutexAbandon(&adapter->lock); + return -1; +} + +int NodeNetState(struct Adapter *adapter) +{ + printf("EndNodeInfos: \n"); + printf("addr:%d,down_channel:%d,upload_channel:%d,",node_param.dev_addr,node_param.down_channel,node_param.upload_channel); + printf("state:%x,air_rate:%x,adr:%x,",node_param.node_state,node_param.air_rate,node_param.adr_enable); + printf("reconnect:%x,recv_time:%d \n",node_param.recv_time,node_param.reconnect_enable); + return 0; +} + +AdapterProductInfoType LoraAttach(struct Adapter *adapter) +{ + struct AdapterProductInfo *product_info = malloc(sizeof(struct AdapterProductInfo)); + if (!product_info) { + printf("LoraAttach-Failed-Node: malloc fail!\n"); + return NULL; + } + + strncpy(product_info->model_name, LORA_ADAPTER_NAME,sizeof(product_info->model_name)); + product_info->model_done = (void *)&node_done; + + return product_info; +} + +uint8 UsrAdapterLoraRegister(struct Adapter *adapter) +{ + strncpy(adapter->name, ADAPTER_LORA_NAME, NAME_NUM_MAX); + adapter->net_protocol = PRIVATE_PROTOCOL; + + adapter->net_role = CLIENT; + adapter->net_role_id = node_param.dev_addr; + adapter->adapter_param = (void *)(&node_param); + + adapter->adapter_status = UNREGISTERED; + + if (AdapterDeviceRegister(adapter) < 0) + { + printf("UsrAdapterLoraRegister-Fail: lora register error!\n"); + return -1; + } + + printf("UsrAdapterLoraRegister-Success!\n"); + return 0; +} +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.h b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.h new file mode 100755 index 000000000..1d6bd6627 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.h @@ -0,0 +1,324 @@ +/* +* 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. +*/ + +/** +* @file: lora_mac.h +* @brief: Definition of data structures related to the Lora private protocol +* @version: 0.1 +* @author: YUNFEI CHU +* @date: 2023/10/01 +* +*/ + +#include +#include +#include "../lora_driver/e220.h" + +// #define AS_LORA_GATEWAY + +/*数据帧**************************************************************************/ +#define FRAME_BEGIN_MARK 0x3C3C // 帧开始标志,高低字节相同 +#define FRAME_END_MARK 0X5A5A // 帧结束标志,高低字节相同 +#define FRAME_MAX_DATA_LENGTH 200 // 默认支持的最大数据包为200字节 +#define FRAME_ADDR_INFO_LENGTH 3 // 定点传输时前三字节为地址和信道共三个字节 +#define FRAME_META_LENGTH 14 // 数据帧相关信息长度 +#define FRAME_MAX_USER_DATA_LENGTH FRAME_MAX_DATA_LENGTH - FRAME_META_LENGTH - FRAME_ADDR_INFO_LENGTH +#define FRAME_CRC_INDEX_BEGIN 2 // CRC校验开始地址 +#define FRAME_CRC_LENGTH 8 // CRC校验元数据量长度 +#define FRAME_BYTE_OK 0XFF // 是/成功 +#define FRAME_BYTE_NO 0X00 // 否/失败 +#define FRAME_NECK_LENGTH 8 // 帧头以下,额外数据以上的长度 +#define FRAME_FOOT_LENGTH 4 // 额外数据以下至帧尾的数据长度 +#define FRAME_MARK_LENGTH 2 // 帧头帧尾的数据长度 + +enum FrameType // 数据帧类型枚举 +{ + E_G_JOIN = 0, // 客户端入网请求,需回复 + E_G_QUIT, // 客户端退网请求,不需回复 + E_G_DATA, // 客户端上传数据,需回复 + G_E_ANS, // 网关响应客户端请求 +}; + +struct LoraFrame // 数据帧消息类型 +{ + uint16 begin_mark; // 开始标志 0x3C3C + uint16 session_id; // 会话ID,客户端每次数据上传会随机设置,若是回复ID不一致则表示数据丢失 + uint16 dev_addr; // 设备地址,特指终端地址,网关地址固定为 0XFFFF + uint8 down_channel; // 下行通道,即终端所在的信道 + uint8 frame_type; // 数据帧的类型 + uint8 frame_confirm; // 该数据帧是否需要确认回复 + uint8 frame_attach; // 用于携带简单的数据,例如用户数据长度 + uint8 *user_data; // 需要额外携带的大量数据 + uint8 crc_hi; // 校验位高字节 + uint8 crc_lo; // 校验位低字节 + uint16 end_mark; // 结束标志 0X5A5A +}; + +extern struct LoraFrame send_frame; // 数据帧发送缓冲区 +extern struct LoraFrame recv_frame; // 数据帧接受缓存区 +extern uint8 send_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据发送缓冲区 +extern uint8 recv_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据接受缓存区 +extern uint8 total_data_buffer[FRAME_MAX_DATA_LENGTH]; // 数据帧加数据的最大缓冲区 + +/** + * @brief: 将数据帧缓冲区置为 0 ,并添加帧头,帧尾数据 + * @param frame: 数据帧缓冲区 +*/ +void InitLoraFrame(struct LoraFrame* frame); + +/** + * @brief: 打印数据帧结构信息,便于运行、开发调试 + * @param fram: 需要打印的数据帧 + * @param prefix: 那里打印的数据帧信息 +*/ +void ShowLoraFrame(struct LoraFrame* frame,char* prefix); + +/** + * @brief: 计算数据帧的校验值,除去帧头、帧尾、校验值本身,若是应该携带数据的类型未携带数据时报错 + * @return: 消息类型和数据不匹配时为 -1 +*/ +uint8 CalCrlLoraFrame(struct LoraFrame* frame); + +/** + * @brief: 校验数据帧的完整性 + * @return: 校验失败为 -1 +*/ +uint8 CheckCrcLoraFrame(struct LoraFrame* frame); +/*数据帧**************************************************************************/ + +/*基础操作************************************************************************/ +#define LORA_ADAPTER_NAME "e220" // Lora适配器名称 +#define LORA_OK 0XFF // 标识成功、确认等 +#define LORA_NO 0X00 // 标识失败、丢失等 +#define LORA_TIME_ON_AIR 2 // 估计的空中时间 +#define LORA_AIR_RATE 0 // 模块的空中速率 +#define LORA_FRAME_RETRY 5 // 数据帧重传次数,超过这个次数判定断网 +#define LORA_GATEWAY_MAX_NODE 20 // 每个网关支持的最大客户端个数,防止上行通道过于拥挤 +#define LORA_GATEWAY_ADDRESS 0XFFFF // 网关地址,单信道网关的监听地址 + +#ifdef AS_LORA_GATEWAY + +#define LORA_RECV_TIME 10 // 串口超时时间 +#define LORA_GATEWAY_CHANNEL 0XA // 网关默认信道 + +#else + +#define LORA_ADDRESS 0X9 // 节点默认地址 +#define LORA_RECV_TIME 5 // 节点默认串口超时时间 +#define LORA_DOWN_CHANNEL 0X9 // 节点默认下行信道 +#define LORA_UP_CHANNEL 0XA // 节点默认上行通道 +#define LORA_ADR_ENABLE LORA_NO // 节点是否开启网关自动搜索 +#define LORA_RECONNECT_ENABLE LORA_OK // 节点是否开启断网自动重联 + +#endif + +/** + * @brief: 从硬件获取一个完整的数据帧 + * @param recv_buffer: 数据帧缓冲区,在检测到带数据的数据帧时会自动关联数据缓冲区 + * @param prefix: 调用位置,便于调试 + * @return 0: 成功接受到一个数据帧(从侦测到数据帧头部开始、成功接受到帧尾部且数据校验成功) + * -1:串口超时;数据帧接受不完整;数据帧完整性校验失败; +*/ +uint8 LoraRecvFrame(struct Adapter* adapter, struct LoraFrame* recv_buffer); + +/** + * @brief: 通过硬件向指定的位置的信道发送数据 + * @param send_buffer: 数据帧缓冲区,在检测到带数据的数据帧时会自动发送关联数据缓冲区 + * @param addr: 目标的地址 + * @param channel: 目标的信道 + * @return 0: 成功发送指定长度的数据 + * -1:未能成功发送制定数据 +*/ +uint8 LoraSendFrame(struct Adapter* adapter, struct LoraFrame* send_buffer,uint16 addr,uint8 channel); + +/** + * @brief: 在一轮超时后未能接收到信号时判定信道空闲 + * @param download_channel: 下行信道 + * @param upload_channel: 上行信道 +*/ +void SignalDetector(struct Adapter *adapter,uint8 download_channel,uint8 upload_channel); + +/** + * @brief: 获取随机数,用于客户端和网关之间的会话 +*/ +uint16 GetSessionId(void); + +/** + * @brief: 获取操作函数集合,客户端和网关各自实现 +*/ +AdapterProductInfoType LoraAttach(struct Adapter *adapter); + +/** + * @brief: 注册函数,需要各自实现 +*/ +uint8 UsrAdapterLoraRegister(struct Adapter *adapter); + +/** + * @brief: 框架初始化函数,需要替换掉原有的初始化函数 +*/ +int UsrAdapterLoraInit(void); + +/*基础操作************************************************************************/ + +/*客户端操作************************************************************************/ +enum EndNodeState //节点状态 +{ + NODE_STATE_DISCONNECT = 0, // 硬件开启,但是没有联网,后台程序可能开启 + NODE_STATE_CONNECT, // 硬件开启,已经联网,后台程序可能开启 + NODE_STATE_BROKEN, // 硬件损坏,无后台程序 + NODE_STATE_CLOSED // 硬件关闭,无后台程序 +}; + +struct EndNodeParam // 节点参数结构 +{ + uint16 dev_addr; // 设备地址信息 + uint8 down_channel; // 下行通道,终端可以接收信息的信道 + uint8 upload_channel; // 上行通道,终端可以给网关发送信息的信道 + uint16 session_id; // 会话ID,用于判定数据帧是否存在丢失 + + enum EndNodeState node_state; // 客户端状态 + enum LoraAirRate air_rate; // 终端的空中速率 + uint8 adr_enable; // 终端是否开启自动寻找上行网关 + uint8 reconnect_enable; // 终端断网自动重联 + uint8 recv_time; // 接受下行消息的时间窗口长度,单位为秒 +}; + +extern struct EndNodeParam node_param; // 客户端参数 +extern int (*node_handlers[])(struct Adapter*,struct LoraFrame*); // 客户端处理流程 + +/** + * @brief: 打开终端硬件,按照配置的CLASS决定是否开启硬件接收和后台处理程序 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeOpen(struct Adapter *adapter); + +/** + * @brief: 关闭终端硬件设施和后台处理程序 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeClose(struct Adapter *adapter); + +/** + * @brief: 终端接入网关,向该上行通道发送入网请求 + * @param adapter: 已经打开的适配器 + * @param net_group: 需要加入的网络编号 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeJoin(struct Adapter *adapter, unsigned char *net_group); + +/** + * @brief: 发送数据到网关 + * @param adapter: 已经联网的适配器 + * @param buf: 待发送数据 + * @param len: 待发送数据长度 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeSend(struct Adapter *adapter, const void *buf, size_t len); + +/** + * @brief: 终端退出某个网络 + * @param adapter: 已经联网的适配器 + * @param net_group: 需要退出的网络编号 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeQuit(struct Adapter *adapter, unsigned char *net_group); + +/** + * @brief: 打印终端的信息 +*/ +int NodeNetState(struct Adapter *adapter); + +/*客户端操作************************************************************************/ + +/*网关操作*************************************************************************/ +struct EndNodeInfo // 网关存储的客户端信息 +{ + uint16 node_addr; // 客户端地址 + uint8 node_down_channel; // 客户端下行通道 + uint16 session_id; // 上一个命令的会话ID +}; + +enum GatewayState // 网关的状态枚举 +{ + GATEWAY_CLOSED, // Lora还没有打开 + GATEWAY_OPENED, // 已打开,但是未开始工作 + GATEWAY_WORKING, // 正常打开且已经开始正常工作 + GATEWAY_BROKEN // 模块损坏 +}; + +struct GatewayParam // 网关参数 +{ + uint16 dev_addr; // 设备ID + uint8 channel; // 网络编号 + + int recv_time; // 串口超时时间 + enum LoraAirRate air_rate; // 网关空中速率 + uint8 frame_retry; // 重传次数 + enum GatewayState gateway_state; // 网关状态 + struct EndNodeInfo *node_infos[LORA_GATEWAY_MAX_NODE]; // 客户端信息 + uint16 node_count; // 已链接客户端数量 +}; + +extern struct GatewayParam gateway_param; // 网关参数 +extern int (*gateway_handlers[])(struct Adapter*,struct LoraFrame*); // 处理程序 + +/** + * @brief: 打开Lora硬件设施,但是不会将其接入网络中 +*/ +int GatewayOpen(struct Adapter *adapter); + +/** + * @brief: 关闭硬件设施,需要先断网 +*/ +int GatewayClose(struct Adapter *adapter); + +/** + * @brief: 配置网关参数的配置接口,这个函数配置的是网关的参数 +*/ +int GatewayIoctl(struct Adapter *adapter, int cmd, void *args); + +/** + * @brief: 启用网关,使其可以为其他终端服务 +*/ +int GatewaySetup(struct Adapter *adapter); + +/** + * @brief: 停用网关,但是不会给网络中的终端下线通知 +*/ +int GatewaySetDown(struct Adapter *adapter); + +/** + * @brief: 打印网关相关信息 +*/ +int GatewayNetState(struct Adapter *adapter); + +/** + * @brief: 网关主任务,负责终端各项事务的自动化处理 +*/ +void* GatewayTask(void* adapter); + +/** + * @brief: 处理终端入网请求 +*/ +int GatewayJoinHandler(struct Adapter* adapter,struct LoraFrame* frame); + +/** + * @brief: 处理终端退网 +*/ +int GatewayQuitHandler(struct Adapter* adapter,struct LoraFrame* frame); + +/** + * @brief: 处理终端数据传输 +*/ +int GatewayDataSendHandler(struct Adapter* adapter,struct LoraFrame* frame); + +/*网关操作*************************************************************************/ \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.c b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.c new file mode 100755 index 000000000..5d8e279f0 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.c @@ -0,0 +1,117 @@ +/* +* 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. +*/ + +/** +* @file: test_lora_net_final.c +* @brief: Private protocol test functions +* @version: 0.1 +* @author: YUNFEI CHU +* @date: 2023/10/01 +* +*/ + +#include "test_lora_net_final.h" + +#ifdef AS_LORA_GATEWAY + +/** + * @brief: 网关测试函数 + * 1. 打开Lora硬件设施. + * 2. 接入网络. +*/ +void TestGateway(void) +{ + struct Adapter* gateway_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + // 打开设备 + if (0 != AdapterDeviceOpen(gateway_adapter)) + { + printf("TestGateway-Fail: open lora fail!\n"); + return; + } + printf("TestGateway-Info: open lora success!\n"); + // 接入网络 + if (0 != AdapterDeviceSetUp(gateway_adapter)) + { + printf("TestGateway-Fail: setup net fail!\n"); + return; + } + printf("TestGateway-Success: gateway set up success!\n"); + AdapterDeviceNetstat(gateway_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestGateway,TestGateway, PRIV_SHELL_CMD_MAIN_ATTR); + +/** + * 打印网关信息 +*/ +void TestGatewayInfo(void) +{ + struct Adapter* gateway_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + AdapterDeviceNetstat(gateway_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestGatewayInfo,TestGatewayInfo, PRIV_SHELL_CMD_MAIN_ATTR); + +#else + +/** + * @brief: 节点测试函数 + * 1. 打开Lora硬件设施. + * 2. 连接到网关. +*/ +void TestNodeUp(void) +{ + struct Adapter* node_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + // 打开设备 + if (0 != AdapterDeviceOpen(node_adapter)) + { + printf("TestNodeUp-Fail: open lora fail!\n"); + return; + } + AdapterDeviceNetstat(node_adapter); + // 接入网络 + uint8 net_id = 0XA; + if (0 != AdapterDeviceJoin(node_adapter,&net_id)) + { + printf("TestNodeUp-Fail: join net fail!\n"); + return; + } + AdapterDeviceNetstat(node_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestNodeUp,TestNodeUp, PRIV_SHELL_CMD_MAIN_ATTR); + +/** + * @brief: 发送数据 +*/ +void TestNodeSend(void) +{ + char* data = "Hello,Gateway-OtherNode!"; + struct Adapter* node_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + AdapterDeviceSend(node_adapter,data,strlen(data)); + AdapterDeviceNetstat(node_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestNodeSend,TestNodeSend, PRIV_SHELL_CMD_MAIN_ATTR); + +/** + * @brief: 循环发送数据 +*/ +void TestNodeSendCycle(void) +{ + int cycle_count = 5; + char* data = "Hello,Gateway-OtherNode-Cycle!"; + struct Adapter *node_adapter = (struct Adapter *)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + while (cycle_count -- > 0) + { + AdapterDeviceSend(node_adapter, data, strlen(data)); + AdapterDeviceNetstat(node_adapter); + } +} +PRIV_SHELL_CMD_FUNCTION(TestNodeSendCycle,TestNodeSendCycle, PRIV_SHELL_CMD_MAIN_ATTR); +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.h b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.h new file mode 100755 index 000000000..b80a83d03 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.h @@ -0,0 +1,49 @@ +/* +* 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. +*/ + +/** +* @file: test_lora_net_final.h +* @brief: Private protocol test function definition +* @version: 0.1 +* @author: YUNFEI CHU +* @date: 2023/10/01 +* +*/ + +#include +#include + +// #define AS_LORA_GATEWAY + +/** + * @brief: 节点测试函数 + * 1. 打开Lora硬件设施. + * 2. 连接到网关. +*/ +void TestNodeUp(void); + +/** + * @brief: 发送数据 +*/ +void TestNodeSend(void); + +/** + * @brief: 循环发送数据 +*/ +void TestNodeSendCycle(void); + +/** + * @brief: 网关测试函数 + * 1. 打开Lora硬件设施. + * 2. 接入网络. +*/ +void TestGateway(void); \ No newline at end of file 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); + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/Makefile b/APP_Framework/Applications/app_test/test_mqttclient/Makefile new file mode 100644 index 000000000..68e52ce07 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/Makefile @@ -0,0 +1,5 @@ +SRC_FILES += test_mqttclient.c + + +SRC_DIR += mqtt +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_mqttclient/README.md b/APP_Framework/Applications/app_test/test_mqttclient/README.md new file mode 100644 index 000000000..560f9dc0a --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/README.md @@ -0,0 +1,55 @@ +## 1. 简介 + +本代码实现了MQTT对服务器订阅主体并发送信息功能 + +## 2. 数据结构设计说明 + +### 2.1 MQTT数据结构定义 + +![image-20231007142702891](./imgs/图片4.png) + +### 2.2 数据解析与加密 + +![image-20231007143235579](./imgs/图片5.png) + +### 2.3 MQTT连接与订阅 + +![image-20231007143448685](./imgs/图片6.png) + +![image-20231007143607579](./imgs/图片7.png) + +## 3. 测试程序说明 + +MQTT基于TCP/IP协议,分为客户端与服务端。在本任务中,ARM终端作为客户端,与服务端连接并进行主题订阅。 + +以下代码设置了进行订阅时需要的Client ID、用户名、密码及订阅主题。 + +![image-20231007144555989](./imgs/图片8.png) + +客户端首先需要根据以上信息向服务端发起连接请求,验证用户名及密码 + +![image-20231007144710185](./imgs/图片9.png) + +随后进行主题订阅,接收服务端发布的消息 + +![image-20231007144823940](./imgs/图片10.png) + +在订阅过程中需要对获取的信息进行读取,同时每隔一段时间向服务端发送保持活性ping请求,以维持与服务端的连接 + +![image-20231007144921430](./imgs/图片11.png) + +## 4. 测试流程 + +测试流程为: + +首先执行setip命令,设置设备ip地址 + +![图片2](./imgs/图片2.png) + +随后执行“MqttSocketRecvTest 服务器ip”命令,订阅主题,然后在服务器端发布消息如图所示 + +![图片1](./imgs/pic1.png) + +最终设备端接收到信息 + +![图片3](./imgs/pic2.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/pic1.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/pic1.png new file mode 100644 index 000000000..461156520 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/pic1.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/pic2.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/pic2.png new file mode 100644 index 000000000..5b47004d1 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/pic2.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片10.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片10.png new file mode 100644 index 000000000..c9eee33cb Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片10.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片11.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片11.png new file mode 100644 index 000000000..60719777c Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片11.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片2.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片2.png new file mode 100644 index 000000000..eb573250b Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片2.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片4.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片4.png new file mode 100644 index 000000000..5d201d554 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片4.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片5.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片5.png new file mode 100644 index 000000000..d2363e630 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片5.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片6.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片6.png new file mode 100644 index 000000000..49e07db4a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片6.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片7.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片7.png new file mode 100644 index 000000000..4e06647ad Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片7.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片8.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片8.png new file mode 100644 index 000000000..b30340637 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片8.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片9.png b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片9.png new file mode 100644 index 000000000..56eede315 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_mqttclient/imgs/图片9.png differ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnect.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnect.h new file mode 100644 index 000000000..4d247a3c2 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnect.h @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2014, 2017 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + * Ian Craggs - fix for issue #64, bit order in connack response + *******************************************************************************/ + +#ifndef MQTTCONNECT_H_ +#define MQTTCONNECT_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + + +typedef union +{ + unsigned char all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + unsigned int username : 1; /**< 3.1 user name */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int will : 1; /**< will flag */ + unsigned int cleansession : 1; /**< clean session flag */ + unsigned int : 1; /**< unused */ + } bits; +#else + struct + { + unsigned int : 1; /**< unused */ + unsigned int cleansession : 1; /**< cleansession flag */ + unsigned int will : 1; /**< will flag */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int username : 1; /**< 3.1 user name */ + } bits; +#endif +} MQTTConnectFlags; /**< connect flags byte */ + + + +/** + * Defines the MQTT "Last Will and Testament" (LWT) settings for + * the connect packet. + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTW. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** The LWT topic to which the LWT message will be published. */ + MQTTString topicName; + /** The LWT payload. */ + MQTTString message; + /** + * The retained flag for the LWT message (see MQTTAsync_message.retained). + */ + unsigned char retained; + /** + * The quality of service setting for the LWT message (see + * MQTTAsync_message.qos and @ref qos). + */ + char qos; +} MQTTPacket_willOptions; + + +#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } + + +typedef struct +{ + /** The eyecatcher for this structure. must be MQTC. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 + */ + unsigned char MQTTVersion; + MQTTString clientID; + unsigned short keepAliveInterval; + unsigned char cleansession; + unsigned char willFlag; + MQTTPacket_willOptions will; + MQTTString username; + MQTTString password; +} MQTTPacket_connectData; + +typedef union +{ + unsigned char all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int reserved : 7; /**< unused */ + unsigned int sessionpresent : 1; /**< session present flag */ + } bits; +#else + struct + { + unsigned int sessionpresent : 1; /**< session present flag */ + unsigned int reserved: 7; /**< unused */ + } bits; +#endif +} MQTTConnackFlags; /**< connack flags byte */ + +#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ + MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } + +DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); +DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); +DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); + +DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); +DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); + +#endif /* MQTTCONNECT_H_ */ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnectClient.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnectClient.c new file mode 100644 index 000000000..5f3cc2963 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnectClient.c @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + +/** + * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. + * @param options the options to be used to build the connect packet + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_connectLength(MQTTPacket_connectData* options) +{ + int len = 0; + + FUNC_ENTRY; + + if (options->MQTTVersion == 3) + len = 12; /* variable depending on MQTT or MQIsdp */ + else if (options->MQTTVersion == 4) + len = 10; + + len += MQTTstrlen(options->clientID)+2; + if (options->willFlag) + len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2; + if (options->username.cstring || options->username.lenstring.data) + len += MQTTstrlen(options->username)+2; + if (options->password.cstring || options->password.lenstring.data) + len += MQTTstrlen(options->password)+2; + + FUNC_EXIT_RC(len); + return len; +} + + +/** + * Serializes the connect options into the buffer. + * @param buf the buffer into which the packet will be serialized + * @param len the length in bytes of the supplied buffer + * @param options the options to be used to build the connect packet + * @return serialized length, or error if 0 + */ +int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + MQTTConnectFlags flags = {0}; + int len = 0; + int rc = -1; + + FUNC_ENTRY; + if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = CONNECT; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, len); /* write remaining length */ + + if (options->MQTTVersion == 4) + { + writeCString(&ptr, "MQTT"); + writeChar(&ptr, (char) 4); + } + else + { + writeCString(&ptr, "MQIsdp"); + writeChar(&ptr, (char) 3); + } + + flags.all = 0; + flags.bits.cleansession = options->cleansession; + flags.bits.will = (options->willFlag) ? 1 : 0; + if (flags.bits.will) + { + flags.bits.willQoS = options->will.qos; + flags.bits.willRetain = options->will.retained; + } + + if (options->username.cstring || options->username.lenstring.data) + flags.bits.username = 1; + if (options->password.cstring || options->password.lenstring.data) + flags.bits.password = 1; + + writeChar(&ptr, flags.all); + writeInt(&ptr, options->keepAliveInterval); + writeMQTTString(&ptr, options->clientID); + if (options->willFlag) + { + writeMQTTString(&ptr, options->will.topicName); + writeMQTTString(&ptr, options->will.message); + } + if (flags.bits.username) + writeMQTTString(&ptr, options->username); + if (flags.bits.password) + writeMQTTString(&ptr, options->password); + + rc = ptr - buf; + + exit: FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into connack data - return code + * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) + * @param connack_rc returned integer value of the connack return code + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param len the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + MQTTConnackFlags flags = {0}; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != CONNACK) + goto exit; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + if (enddata - curdata < 2) + goto exit; + + flags.all = readChar(&curdata); + *sessionPresent = flags.bits.sessionpresent; + *connack_rc = readChar(&curdata); + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @param packettype the message type + * @return serialized length, or error if 0 + */ +int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) +{ + MQTTHeader header = {0}; + int rc = -1; + unsigned char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 2) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = packettype; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */ + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @return serialized length, or error if 0 + */ +int MQTTSerialize_disconnect(unsigned char* buf, int buflen) +{ + return MQTTSerialize_zero(buf, buflen, DISCONNECT); +} + + +/** + * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pingreq(unsigned char* buf, int buflen) +{ + return MQTTSerialize_zero(buf, buflen, PINGREQ); +} diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnectServer.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnectServer.c new file mode 100644 index 000000000..07c7cb537 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTConnectServer.c @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" +#include + +#define min(a, b) ((a < b) ? a : b) + + +/** + * Validates MQTT protocol name and version combinations + * @param protocol the MQTT protocol name as an MQTTString + * @param version the MQTT protocol version number, as in the connect packet + * @return correct MQTT combination? 1 is true, 0 is false + */ +int MQTTPacket_checkVersion(MQTTString* protocol, int version) +{ + int rc = 0; + + if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp", + min(6, protocol->lenstring.len)) == 0) + rc = 1; + else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT", + min(4, protocol->lenstring.len)) == 0) + rc = 1; + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into connect data structure + * @param data the connect data structure to be filled out + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param len the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len) +{ + MQTTHeader header = {0}; + MQTTConnectFlags flags = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = &buf[len]; + int rc = 0; + MQTTString Protocol; + int version; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != CONNECT) + goto exit; + + curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */ + + if (!readMQTTLenString(&Protocol, &curdata, enddata) || + enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ + goto exit; + + version = (int)readChar(&curdata); /* Protocol version */ + /* If we don't recognize the protocol version, we don't parse the connect packet on the + * basis that we don't know what the format will be. + */ + if (MQTTPacket_checkVersion(&Protocol, version)) + { + flags.all = readChar(&curdata); + data->cleansession = flags.bits.cleansession; + data->keepAliveInterval = readInt(&curdata); + if (!readMQTTLenString(&data->clientID, &curdata, enddata)) + goto exit; + data->willFlag = flags.bits.will; + if (flags.bits.will) + { + data->will.qos = flags.bits.willQoS; + data->will.retained = flags.bits.willRetain; + if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) || + !readMQTTLenString(&data->will.message, &curdata, enddata)) + goto exit; + } + if (flags.bits.username) + { + if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata)) + goto exit; /* username flag set, but no username supplied - invalid */ + if (flags.bits.password && + (enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata))) + goto exit; /* password flag set, but no password supplied - invalid */ + } + else if (flags.bits.password) + goto exit; /* password flag set without username - invalid */ + rc = 1; + } +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes the connack packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param connack_rc the integer connack return code to be used + * @param sessionPresent the MQTT 3.1.1 sessionPresent flag + * @return serialized length, or error if 0 + */ +int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent) +{ + MQTTHeader header = {0}; + int rc = 0; + unsigned char *ptr = buf; + MQTTConnackFlags flags = {0}; + + FUNC_ENTRY; + if (buflen < 2) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = CONNACK; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ + + flags.all = 0; + flags.bits.sessionpresent = sessionPresent; + writeChar(&ptr, flags.all); + writeChar(&ptr, connack_rc); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTDeserializePublish.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTDeserializePublish.c new file mode 100644 index 000000000..5014c46d3 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTDeserializePublish.c @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" +#include + +#define min(a, b) ((a < b) ? 1 : 0) + +/** + * Deserializes the supplied (wire) buffer into publish data + * @param dup returned integer - the MQTT dup flag + * @param qos returned integer - the MQTT QoS value + * @param retained returned integer - the MQTT retained flag + * @param packetid returned integer - the MQTT packet identifier + * @param topicName returned MQTTString - the MQTT topic in the publish + * @param payload returned byte buffer - the MQTT publish payload + * @param payloadlen returned integer - the length of the MQTT payload + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success + */ +int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, + unsigned char** payload, int32_t* payloadlen, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != PUBLISH) + goto exit; + *dup = header.bits.dup; + *qos = header.bits.qos; + *retained = header.bits.retain; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + if (!readMQTTLenString(topicName, &curdata, enddata) || + enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ + goto exit; + + if (*qos > 0) + *packetid = readInt(&curdata); + + *payloadlen = enddata - curdata; + *payload = curdata; + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + + +/** + * Deserializes the supplied (wire) buffer into an ack + * @param packettype returned integer - the MQTT packet type + * @param dup returned integer - the MQTT dup flag + * @param packetid returned integer - the MQTT packet identifier + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + *dup = header.bits.dup; + *packettype = header.bits.type; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + if (enddata - curdata < 2) + goto exit; + *packetid = readInt(&curdata); + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTFormat.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTFormat.c new file mode 100644 index 000000000..2eff31f89 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTFormat.c @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" + +#include + + +const char* MQTTPacket_names[] = +{ + "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", + "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", + "PINGREQ", "PINGRESP", "DISCONNECT" +}; + + +const char* MQTTPacket_getName(unsigned short packetid) +{ + return MQTTPacket_names[packetid]; +} + + +int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data) +{ + int strindex = 0; + + strindex = snprintf(strbuf, strbuflen, + "CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d", + (int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data, + (int)data->cleansession, data->keepAliveInterval); + if (data->willFlag) + strindex += snprintf(&strbuf[strindex], strbuflen - strindex, + ", will QoS %d, will retain %d, will topic %.*s, will message %.*s", + data->will.qos, data->will.retained, + data->will.topicName.lenstring.len, data->will.topicName.lenstring.data, + data->will.message.lenstring.len, data->will.message.lenstring.data); + if (data->username.lenstring.data && data->username.lenstring.len > 0) + strindex += snprintf(&strbuf[strindex], strbuflen - strindex, + ", user name %.*s", data->username.lenstring.len, data->username.lenstring.data); + if (data->password.lenstring.data && data->password.lenstring.len > 0) + strindex += snprintf(&strbuf[strindex], strbuflen - strindex, + ", password %.*s", data->password.lenstring.len, data->password.lenstring.data); + return strindex; +} + + +int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent) +{ + int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc); + return strindex; +} + + +int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, + unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen) +{ + int strindex = snprintf(strbuf, strbuflen, + "PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s", + dup, qos, retained, packetid, + (topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data, + payloadlen, (payloadlen < 20) ? payloadlen : 20, payload); + return strindex; +} + + +int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid) +{ + int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid); + if (dup) + strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup); + return strindex; +} + + +int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int requestedQoSs[]) +{ + return snprintf(strbuf, strbuflen, + "SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d", + dup, packetid, count, + topicFilters[0].lenstring.len, topicFilters[0].lenstring.data, + requestedQoSs[0]); +} + + +int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs) +{ + return snprintf(strbuf, strbuflen, + "SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]); +} + + +int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]) +{ + return snprintf(strbuf, strbuflen, + "UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s", + dup, packetid, count, + topicFilters[0].lenstring.len, topicFilters[0].lenstring.data); +} + + +#if defined(MQTT_CLIENT) +char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) +{ + int index = 0; + int rem_length = 0; + MQTTHeader header = {0}; + int strindex = 0; + + header.byte = buf[index++]; + index += MQTTPacket_decodeBuf(&buf[index], &rem_length); + + switch (header.bits.type) + { + + case CONNACK: + { + unsigned char sessionPresent, connack_rc; + if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1) + strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent); + } + break; + case PUBLISH: + { + unsigned char dup, retained, *payload; + unsigned short packetid; + int qos, payloadlen; + MQTTString topicName = MQTTString_initializer; + if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, + &payload, &payloadlen, buf, buflen) == 1) + strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, + topicName, payload, payloadlen); + } + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + { + unsigned char packettype, dup; + unsigned short packetid; + if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) + strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); + } + break; + case SUBACK: + { + unsigned short packetid; + int maxcount = 1, count = 0; + int grantedQoSs[1]; + if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1) + strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs); + } + break; + case UNSUBACK: + { + unsigned short packetid; + if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1) + strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid); + } + break; + case PINGREQ: + case PINGRESP: + case DISCONNECT: + strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); + break; + } + return strbuf; +} +#endif + +#if defined(MQTT_SERVER) +char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) +{ + int index = 0; + int rem_length = 0; + MQTTHeader header = {0}; + int strindex = 0; + + header.byte = buf[index++]; + index += MQTTPacket_decodeBuf(&buf[index], &rem_length); + + switch (header.bits.type) + { + case CONNECT: + { + MQTTPacket_connectData data; + int rc; + if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1) + strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data); + } + break; + case PUBLISH: + { + unsigned char dup, retained, *payload; + unsigned short packetid; + int qos, payloadlen; + MQTTString topicName = MQTTString_initializer; + if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, + &payload, &payloadlen, buf, buflen) == 1) + strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, + topicName, payload, payloadlen); + } + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + { + unsigned char packettype, dup; + unsigned short packetid; + if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) + strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); + } + break; + case SUBSCRIBE: + { + unsigned char dup; + unsigned short packetid; + int maxcount = 1, count = 0; + MQTTString topicFilters[1]; + int requestedQoSs[1]; + if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count, + topicFilters, requestedQoSs, buf, buflen) == 1) + strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);; + } + break; + case UNSUBSCRIBE: + { + unsigned char dup; + unsigned short packetid; + int maxcount = 1, count = 0; + MQTTString topicFilters[1]; + if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1) + strindex = MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters); + } + break; + case PINGREQ: + case PINGRESP: + case DISCONNECT: + strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); + break; + } + strbuf[strbuflen] = '\0'; + return strbuf; +} +#endif diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTFormat.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTFormat.h new file mode 100644 index 000000000..47b0c4143 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTFormat.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#if !defined(MQTTFORMAT_H) +#define MQTTFORMAT_H + +#include "StackTrace.h" +#include "MQTTPacket.h" + +const char* MQTTPacket_getName(unsigned short packetid); +int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data); +int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent); +int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, + unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen); +int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid); +int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int requestedQoSs[]); +int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs); +int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]); +char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); +char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); + +#endif diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPacket.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPacket.c new file mode 100644 index 000000000..4f1f95a78 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPacket.c @@ -0,0 +1,412 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Sergio R. Caprile - non-blocking packet read functions for stream transport + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" + +#include + +/** + * Encodes the message length according to the MQTT algorithm + * @param buf the buffer into which the encoded data is written + * @param length the length to be encoded + * @return the number of bytes written to buffer + */ +int MQTTPacket_encode(unsigned char* buf, int length) +{ + int rc = 0; + + FUNC_ENTRY; + do + { + char d = length % 128; + length /= 128; + /* if there are more digits to encode, set the top bit of this digit */ + if (length > 0) + d |= 0x80; + buf[rc++] = d; + } while (length > 0); + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Decodes the message length according to the MQTT algorithm + * @param getcharfn pointer to function to read the next character from the data source + * @param value the decoded length returned + * @return the number of bytes read from the socket + */ +int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) +{ + unsigned char c; + int multiplier = 1; + int len = 0; +#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 + + FUNC_ENTRY; + *value = 0; + do + { + int rc = MQTTPACKET_READ_ERROR; + + if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) + { + rc = MQTTPACKET_READ_ERROR; /* bad data */ + goto exit; + } + rc = (*getcharfn)(&c, 1); + if (rc != 1) + goto exit; + *value += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); +exit: + FUNC_EXIT_RC(len); + return len; +} + + +int MQTTPacket_len(int rem_len) +{ + rem_len += 1; /* header byte */ + + /* now remaining_length field */ + if (rem_len < 128) + rem_len += 1; + else if (rem_len < 16384) + rem_len += 2; + else if (rem_len < 2097151) + rem_len += 3; + else + rem_len += 4; + return rem_len; +} + + +static unsigned char* bufptr; + +int bufchar(unsigned char* c, int count) +{ + int i; + + for (i = 0; i < count; ++i) + *c = *bufptr++; + return count; +} + + +int MQTTPacket_decodeBuf(unsigned char* buf, int* value) +{ + bufptr = buf; + return MQTTPacket_decode(bufchar, value); +} + + +/** + * Calculates an integer from two bytes read from the input buffer + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the integer value calculated + */ +int readInt(unsigned char** pptr) +{ + unsigned char* ptr = *pptr; + int len = 256*(*ptr) + (*(ptr+1)); + *pptr += 2; + return len; +} + + +/** + * Reads one character from the input buffer. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the character read + */ +char readChar(unsigned char** pptr) +{ + char c = **pptr; + (*pptr)++; + return c; +} + + +/** + * Writes one character to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param c the character to write + */ +void writeChar(unsigned char** pptr, char c) +{ + **pptr = c; + (*pptr)++; +} + + +/** + * Writes an integer as 2 bytes to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param anInt the integer to write + */ +void writeInt(unsigned char** pptr, int anInt) +{ + **pptr = (unsigned char)(anInt / 256); + (*pptr)++; + **pptr = (unsigned char)(anInt % 256); + (*pptr)++; +} + + +/** + * Writes a "UTF" string to an output buffer. Converts C string to length-delimited. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param string the C string to write + */ +void writeCString(unsigned char** pptr, const char* string) +{ + int len = strlen(string); + writeInt(pptr, len); + memcpy(*pptr, string, len); + *pptr += len; +} + + +int getLenStringLen(char* ptr) +{ + int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); + return len; +} + + +void writeMQTTString(unsigned char** pptr, MQTTString mqttstring) +{ + if (mqttstring.lenstring.len > 0) + { + writeInt(pptr, mqttstring.lenstring.len); + memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); + *pptr += mqttstring.lenstring.len; + } + else if (mqttstring.cstring) + writeCString(pptr, mqttstring.cstring); + else + writeInt(pptr, 0); +} + + +/** + * @param mqttstring the MQTTString structure into which the data is to be read + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the data: do not read beyond + * @return 1 if successful, 0 if not + */ +int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) +{ + int rc = 0; + + FUNC_ENTRY; + /* the first two bytes are the length of the string */ + if (enddata - (*pptr) > 1) /* enough length to read the integer? */ + { + mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */ + if (&(*pptr)[mqttstring->lenstring.len] <= enddata) + { + mqttstring->lenstring.data = (char*)*pptr; + *pptr += mqttstring->lenstring.len; + rc = 1; + } + } + mqttstring->cstring = NULL; + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string + * @param mqttstring the string to return the length of + * @return the length of the string + */ +int MQTTstrlen(MQTTString mqttstring) +{ + int rc = 0; + + if (mqttstring.cstring) + rc = strlen(mqttstring.cstring); + else + rc = mqttstring.lenstring.len; + return rc; +} + + +/** + * Compares an MQTTString to a C string + * @param a the MQTTString to compare + * @param bptr the C string to compare + * @return boolean - equal or not + */ +int MQTTPacket_equals(MQTTString* a, char* bptr) +{ + int alen = 0, + blen = 0; + char *aptr; + + if (a->cstring) + { + aptr = a->cstring; + alen = strlen(a->cstring); + } + else + { + aptr = a->lenstring.data; + alen = a->lenstring.len; + } + blen = strlen(bptr); + + return (alen == blen) && (strncmp(aptr, bptr, alen) == 0); +} + + +/** + * Helper function to read packet data from some source into a buffer + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param getfn pointer to a function which will read any number of bytes from the needed source + * @return integer MQTT packet type, or -1 on error + * @note the whole message must fit into the caller's buffer + */ +int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) +{ + int rc = -1; + MQTTHeader header = {0}; + int len = 0; + int rem_len = 0; + + /* 1. read the header byte. This has the packet type in it */ + if ((*getfn)(buf, 1) != 1) + goto exit; + + len = 1; + /* 2. read the remaining length. This is variable in itself */ + MQTTPacket_decode(getfn, &rem_len); + len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */ + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if((rem_len + len) > buflen) + goto exit; + if (rem_len && ((*getfn)(buf + len, rem_len) != rem_len)) + goto exit; + + header.byte = buf[0]; + rc = header.bits.type; +exit: + return rc; +} + +/** + * Decodes the message length according to the MQTT algorithm, non-blocking + * @param trp pointer to a transport structure holding what is needed to solve getting data from it + * @param value the decoded length returned + * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error + */ +static int MQTTPacket_decodenb(MQTTTransport *trp) +{ + unsigned char c; + int rc = MQTTPACKET_READ_ERROR; + + FUNC_ENTRY; + if(trp->len == 0){ /* initialize on first call */ + trp->multiplier = 1; + trp->rem_len = 0; + } + do { + int frc; + if (trp->len >= MAX_NO_OF_REMAINING_LENGTH_BYTES) + goto exit; + if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1) + goto exit; + if (frc == 0){ + rc = 0; + goto exit; + } + ++(trp->len); + trp->rem_len += (c & 127) * trp->multiplier; + trp->multiplier *= 128; + } while ((c & 128) != 0); + rc = trp->len; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + +/** + * Helper function to read packet data from some source into a buffer, non-blocking + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param trp pointer to a transport structure holding what is needed to solve getting data from it + * @return integer MQTT packet type, 0 for call again, or -1 on error + * @note the whole message must fit into the caller's buffer + */ +int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp) +{ + int rc = -1, frc; + MQTTHeader header = {0}; + + switch(trp->state){ + default: + trp->state = 0; + /*FALLTHROUGH*/ + case 0: + /* read the header byte. This has the packet type in it */ + if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1) + goto exit; + if (frc == 0) + return 0; + trp->len = 0; + ++trp->state; + /*FALLTHROUGH*/ + /* read the remaining length. This is variable in itself */ + case 1: + if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR) + goto exit; + if(frc == 0) + return 0; + trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */ + if((trp->rem_len + trp->len) > buflen) + goto exit; + ++trp->state; + /*FALLTHROUGH*/ + case 2: + if(trp->rem_len){ + /* read the rest of the buffer using a callback to supply the rest of the data */ + if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1) + goto exit; + if (frc == 0) + return 0; + trp->rem_len -= frc; + trp->len += frc; + if(trp->rem_len) + return 0; + } + header.byte = buf[0]; + rc = header.bits.type; + break; + } + +exit: + trp->state = 0; + return rc; +} + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPacket.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPacket.h new file mode 100644 index 000000000..a1c5038d8 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPacket.h @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ +#include + +#ifndef MQTTPACKET_H_ +#define MQTTPACKET_H_ + +#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ +extern "C" { +#endif + +#if defined(WIN32_DLL) || defined(WIN64_DLL) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#elif defined(LINUX_SO) + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#else + #define DLLImport + #define DLLExport +#endif + +enum errors +{ + MQTTPACKET_BUFFER_TOO_SHORT = -2, + MQTTPACKET_READ_ERROR = -1, + MQTTPACKET_READ_COMPLETE +}; + +enum msgTypes +{ + CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, + PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, + PINGREQ, PINGRESP, DISCONNECT +}; + +/** + * Bitfields for the MQTT header byte. + */ +typedef union +{ + unsigned char byte; /**< the whole byte */ +#if defined(REVERSED) + struct + { + unsigned int type : 4; /**< message type nibble */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int retain : 1; /**< retained flag bit */ + } bits; +#else + struct + { + unsigned int retain : 1; /**< retained flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int type : 4; /**< message type nibble */ + } bits; +#endif +} MQTTHeader; + +typedef struct +{ + int len; + char* data; +} MQTTLenString; + +typedef struct +{ + char* cstring; + MQTTLenString lenstring; +} MQTTString; + +#define MQTTString_initializer {NULL, {0, NULL}} + +int MQTTstrlen(MQTTString mqttstring); + +#include "MQTTConnect.h" +#include "MQTTPublish.h" +#include "MQTTSubscribe.h" +#include "MQTTUnsubscribe.h" +#include "MQTTFormat.h" + +DLLExport int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid); +DLLExport int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen); + +int MQTTPacket_len(int rem_len); +DLLExport int MQTTPacket_equals(MQTTString* a, char* b); + +DLLExport int MQTTPacket_encode(unsigned char* buf, int length); +int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); +int MQTTPacket_decodeBuf(unsigned char* buf, int* value); + +int readInt(unsigned char** pptr); +char readChar(unsigned char** pptr); +void writeChar(unsigned char** pptr, char c); +void writeInt(unsigned char** pptr, int anInt); +int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); +void writeCString(unsigned char** pptr, const char* string); +void writeMQTTString(unsigned char** pptr, MQTTString mqttstring); + +DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)); + +typedef struct { + int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */ + void *sck; /* pointer to whatever the system may use to identify the transport */ + int multiplier; + int rem_len; + int len; + char state; +}MQTTTransport; + +int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp); + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ +} +#endif + + +#endif /* MQTTPACKET_H_ */ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPublish.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPublish.h new file mode 100644 index 000000000..88aca927e --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTPublish.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTPUBLISH_H_ +#define MQTTPUBLISH_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, + MQTTString topicName, unsigned char* payload, int payloadlen); + +DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, + unsigned char** payload, int32_t* payloadlen, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); +DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); +DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); + +#endif /* MQTTPUBLISH_H_ */ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSerializePublish.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSerializePublish.c new file mode 100644 index 000000000..77a58b54a --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSerializePublish.c @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + + +/** + * Determines the length of the MQTT publish packet that would be produced using the supplied parameters + * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0) + * @param topicName the topic name to be used in the publish + * @param payloadlen the length of the payload to be sent + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen) +{ + int len = 0; + + len += 2 + MQTTstrlen(topicName) + payloadlen; + if (qos > 0) + len += 2; /* packetid */ + return len; +} + + +/** + * Serializes the supplied publish data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param dup integer - the MQTT dup flag + * @param qos integer - the MQTT QoS value + * @param retained integer - the MQTT retained flag + * @param packetid integer - the MQTT packet identifier + * @param topicName MQTTString - the MQTT topic in the publish + * @param payload byte buffer - the MQTT publish payload + * @param payloadlen integer - the length of the MQTT payload + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, + MQTTString topicName, unsigned char* payload, int payloadlen) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = 0; + + FUNC_ENTRY; + if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.bits.type = PUBLISH; + header.bits.dup = dup; + header.bits.qos = qos; + header.bits.retain = retained; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeMQTTString(&ptr, topicName); + + if (qos > 0) + writeInt(&ptr, packetid); + + memcpy(ptr, payload, payloadlen); + ptr += payloadlen; + + rc = ptr - buf; + +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + + +/** + * Serializes the ack packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param type the MQTT packet type + * @param dup the MQTT dup flag + * @param packetid the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) +{ + MQTTHeader header = {0}; + int rc = 0; + unsigned char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 4) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.bits.type = packettype; + header.bits.dup = dup; + header.bits.qos = (packettype == PUBREL) ? 1 : 0; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ + writeInt(&ptr, packetid); + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes a puback packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid); +} + + +/** + * Serializes a pubrel packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid); +} + + +/** + * Serializes a pubrel packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid); +} + + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribe.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribe.h new file mode 100644 index 000000000..4b702bd59 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribe.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTSUBSCRIBE_H_ +#define MQTTSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[], int32_t requestedQoSs[]); + +DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, + int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); + +DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); + +DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int32_t* count, int32_t grantedQoSs[], unsigned char* buf, int len); + + +#endif /* MQTTSUBSCRIBE_H_ */ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribeClient.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribeClient.c new file mode 100644 index 000000000..dc131882d --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribeClient.c @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + +/** + * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters + * @param count the number of topic filter strings in topicFilters + * @param topicFilters the array of topic filter strings to be used in the publish + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[]) +{ + int i; + int len = 2; /* packetid */ + + for (i = 0; i < count; ++i) + len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */ + return len; +} + + +/** + * Serializes the supplied subscribe data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied bufferr + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the topicFilters and reqQos arrays + * @param topicFilters - array of topic filter names + * @param requestedQoSs - array of requested QoS + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int32_t requestedQoSs[]) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = 0; + int i = 0; + + FUNC_ENTRY; + if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = SUBSCRIBE; + header.bits.dup = dup; + header.bits.qos = 1; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + { + writeMQTTString(&ptr, topicFilters[i]); + writeChar(&ptr, requestedQoSs[i]); + } + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + + +/** + * Deserializes the supplied (wire) buffer into suback data + * @param packetid returned integer - the MQTT packet identifier + * @param maxcount - the maximum number of members allowed in the grantedQoSs array + * @param count returned integer - number of members in the grantedQoSs array + * @param grantedQoSs returned array of integers - the granted qualities of service + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int32_t* count, int32_t grantedQoSs[], unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != SUBACK) + goto exit; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + if (enddata - curdata < 2) + goto exit; + + *packetid = readInt(&curdata); + + *count = 0; + while (curdata < enddata) + { + if (*count > maxcount) + { + rc = -1; + goto exit; + } + grantedQoSs[(*count)++] = readChar(&curdata); + } + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribeServer.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribeServer.c new file mode 100644 index 000000000..5579645fe --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTSubscribeServer.c @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + + +/** + * Deserializes the supplied (wire) buffer into subscribe data + * @param dup integer returned - the MQTT dup flag + * @param packetid integer returned - the MQTT packet identifier + * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays + * @param count - number of members in the topicFilters and requestedQoSs arrays + * @param topicFilters - array of topic filter names + * @param requestedQoSs - array of requested QoS + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], + int requestedQoSs[], unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = -1; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != SUBSCRIBE) + goto exit; + *dup = header.bits.dup; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + *packetid = readInt(&curdata); + + *count = 0; + while (curdata < enddata) + { + if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) + goto exit; + if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */ + goto exit; + requestedQoSs[*count] = readChar(&curdata); + (*count)++; + } + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes the supplied suback data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the grantedQoSs array + * @param grantedQoSs - array of granted QoS + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs) +{ + MQTTHeader header = {0}; + int rc = -1; + unsigned char *ptr = buf; + int i; + + FUNC_ENTRY; + if (buflen < 2 + count) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = SUBACK; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */ + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + writeChar(&ptr, grantedQoSs[i]); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribe.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribe.h new file mode 100644 index 000000000..355ca9a42 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribe.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTUNSUBSCRIBE_H_ +#define MQTTUNSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]); + +DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], + unsigned char* buf, int len); + +DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); + +DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); + +#endif /* MQTTUNSUBSCRIBE_H_ */ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribeClient.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribeClient.c new file mode 100644 index 000000000..e7ec53021 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribeClient.c @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + +/** + * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters + * @param count the number of topic filter strings in topicFilters + * @param topicFilters the array of topic filter strings to be used in the publish + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[]) +{ + int i; + int len = 2; /* packetid */ + + for (i = 0; i < count; ++i) + len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/ + return len; +} + + +/** + * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the topicFilters array + * @param topicFilters - array of topic filter names + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = -1; + int i = 0; + + FUNC_ENTRY; + if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = UNSUBSCRIBE; + header.bits.dup = dup; + header.bits.qos = 1; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + writeMQTTString(&ptr, topicFilters[i]); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into unsuback data + * @param packetid returned integer - the MQTT packet identifier + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen) +{ + unsigned char type = 0; + unsigned char dup = 0; + int rc = 0; + + FUNC_ENTRY; + rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen); + if (type == UNSUBACK) + rc = 1; + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribeServer.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribeServer.c new file mode 100644 index 000000000..42b6102a7 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/MQTTUnsubscribeServer.c @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + + +/** + * Deserializes the supplied (wire) buffer into unsubscribe data + * @param dup integer returned - the MQTT dup flag + * @param packetid integer returned - the MQTT packet identifier + * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays + * @param count - number of members in the topicFilters and requestedQoSs arrays + * @param topicFilters - array of topic filter names + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], + unsigned char* buf, int len) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != UNSUBSCRIBE) + goto exit; + *dup = header.bits.dup; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + *packetid = readInt(&curdata); + + *count = 0; + while (curdata < enddata) + { + if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) + goto exit; + (*count)++; + } + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes the supplied unsuback data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid) +{ + MQTTHeader header = {0}; + int rc = 0; + unsigned char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 2) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = UNSUBACK; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ + + writeInt(&ptr, packetid); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/Makefile b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/Makefile new file mode 100644 index 000000000..7eb8d3244 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/Makefile @@ -0,0 +1,13 @@ + SRC_FILES +=MQTTPacket.c\ + MQTTConnectClient.c \ + MQTTConnectServer.c \ + MQTTDeserializePublish.c \ + MQTTFormat.c \ + MQTTSerializePublish.c \ + MQTTSubscribeClient.c \ + MQTTSubscribeServer.c \ + MQTTUnsubscribeClient.c \ + MQTTUnsubscribeServer.c \ + transport.c + +include $(KERNEL_ROOT)/compiler.mk \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/StackTrace.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/StackTrace.h new file mode 100644 index 000000000..2808a0d18 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/StackTrace.h @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - fix for bug #434081 + *******************************************************************************/ + +#ifndef STACKTRACE_H_ +#define STACKTRACE_H_ + +#include +#define NOSTACKTRACE 1 + +#if defined(NOSTACKTRACE) +#define FUNC_ENTRY +#define FUNC_ENTRY_NOLOG +#define FUNC_ENTRY_MED +#define FUNC_ENTRY_MAX +#define FUNC_EXIT +#define FUNC_EXIT_NOLOG +#define FUNC_EXIT_MED +#define FUNC_EXIT_MAX +#define FUNC_EXIT_RC(x) +#define FUNC_EXIT_MED_RC(x) +#define FUNC_EXIT_MAX_RC(x) + +#else + +#if defined(WIN32) +#define inline __inline +#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM) +#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1) +#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM) +#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM) +#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM) +#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1) +#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM) +#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM) +#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM) +#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM) +#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM) +#else +#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM) +#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1) +#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM) +#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM) +#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM) +#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1) +#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM) +#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM) +#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM) +#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM) +#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM) + +void StackTrace_entry(const char* name, int line, int trace); +void StackTrace_exit(const char* name, int line, void* return_value, int trace); + +void StackTrace_printStack(FILE* dest); +char* StackTrace_get(unsigned long); + +#endif + +#endif + + + + +#endif /* STACKTRACE_H_ */ diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/transport.c b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/transport.c new file mode 100644 index 000000000..fb38fab28 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/transport.c @@ -0,0 +1,59 @@ +/* + * 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 transport.c + * @brief mqtt transport function + * @version 3.0 + * @author AIIT XUOS Lab + * @date 2023.8.10 + */ + +#include "transport.h" +#include "lwip/opt.h" +#include "lwip/arch.h" +#include "lwip/api.h" +#include "lwip/inet.h" +#include "lwip/sockets.h" +#include "string.h" + +static int mysock; + + +int32_t transport_sendPacketBuffer( uint8_t* buf, int32_t buflen) +{ + int32_t rc; + rc = write(mysock, buf, buflen); + return rc; +} + + +int transport_getdata(unsigned char* buf, int count) +{ + int32_t rc; + + rc = recv(mysock, buf, count, 0); + lw_print("get data : %lx\n",rc); + return rc; +} + + +int32_t transport_close(void) +{ + + int rc; +// rc = close(mysock); + rc = shutdown(mysock, SHUT_WR); + rc = recv(mysock, NULL, (size_t)0, 0); + rc = close(mysock); + return rc; +} diff --git a/APP_Framework/Applications/app_test/test_mqttclient/mqtt/transport.h b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/transport.h new file mode 100644 index 000000000..40ca65148 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/mqtt/transport.h @@ -0,0 +1,40 @@ +/* + * 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 transport.h + * @brief mqtt transport function + * @version 3.0 + * @author AIIT XUOS Lab + * @date 2023.8.10 + */ + +#ifndef __TRANSPORT_H +#define __TRANSPORT_H + +#include + + +int32_t transport_sendPacketBuffer( uint8_t* buf, int32_t buflen); + + +int transport_getdata(unsigned char* buf, int count); + + +int32_t transport_open(int8_t* servip, int32_t port); + + +int32_t transport_close(void); + + + +#endif diff --git a/APP_Framework/Applications/app_test/test_mqttclient/test_mqttclient.c b/APP_Framework/Applications/app_test/test_mqttclient/test_mqttclient.c new file mode 100644 index 000000000..2ceb1b892 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/test_mqttclient.c @@ -0,0 +1,748 @@ +/* + * 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 test_mqttclient.c + * @brief mqtt subscribe fuction test + * @version 3.0 + * @author AIIT XUOS Lab + * @date 2023.8.10 + */ + +#include +#include "test_mqttclient.h" +#include +#ifdef ADD_XIZI_FEATURES +#include +#include +#include "lwip/sys.h" +#include "lwip/api.h" + +#include "mqtt/MQTTPacket.h" +#include "mqtt/MQTTSubscribe.h" +#include "mqtt/transport.h" +#include + +#endif + +#ifdef ADD_NUTTX_FEATURES +#include +#include +#include +#include "stdio.h" +#endif + + +#define MQTT_DEMO_BUF_SIZE 65535 +#define MQTT_DEMO_SEND_TIMES 20 +#define LWIP_MQTT_DEMO_TASK_STACK_SIZE 4096 +#define LWIP_MQTT_DEMO_TASK_PRIO 20 + +static char mqtt_demo_ipaddr[] = {192, 168, 130, 77}; +static char mqtt_demo_netmask[] = {255, 255, 254, 0}; +static char mqtt_demo_gwaddr[] = {192, 168, 130, 1}; + +static pthread_t mqtt_client_task; +static pthread_t mqtt_server_task; + +static uint16_t mqtt_socket_port = 1883; +static char mqtt_ip_str[128] = {192,168,100,1}; + +void MqttSocketConfigParam(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(mqtt_ip_str, ip_str); + if(port) + mqtt_socket_port = port; + return; + } + + if(sscanf(ip_str, "%d.%d.%d.%d", &ip1, &ip2, &ip3, &ip4)) { + printf("config ip %s\n", ip_str); + strcpy(mqtt_ip_str, ip_str); + } +} + +MQTT_USER_MSG mqtt_user_msg; + +uint8_t MQTT_Connect(void) +{ + MQTTPacket_connectData data = MQTTPacket_connectData_initializer; + uint8_t buf[200]; + int buflen = sizeof(buf); + int len = 0; + data.clientID.cstring = CLIENT_ID; //随机 + data.keepAliveInterval = KEEPLIVE_TIME; //保持活跃 + data.username.cstring = USER_NAME; //用户名 + data.password.cstring = PASSWORD; //密钥 + data.MQTTVersion = MQTT_VERSION; //3表示3.1版本,4表示3.11版本 + data.cleansession = 1; + //组装消息 + len = MQTTSerialize_connect((unsigned char *)buf, buflen, &data); + //发送消息 + transport_sendPacketBuffer(buf, len); + + /* 等待连接响应 */ + if (MQTTPacket_read(buf, buflen, transport_getdata) == CONNACK) + { + unsigned char sessionPresent, connack_rc; + if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0) + { + lw_print("无法连接,错误代码是: %d!\n", connack_rc); + return Connect_NOK; + } + else + { + lw_print("用户名与密钥验证成功,MQTT连接成功!\n"); + return Connect_OK; + } + } + else + lw_print("MQTT连接无响应!\n"); + return Connect_NOTACK; +} + + +int32_t MQTT_PingReq(int32_t sock) +{ + int32_t len; + uint8_t buf[200]; + int32_t buflen = sizeof(buf); + fd_set readfd; + struct timeval tv; + tv.tv_sec = 5; + tv.tv_usec = 0; + + FD_ZERO(&readfd); + FD_SET(sock,&readfd); + + len = MQTTSerialize_pingreq(buf, buflen); + transport_sendPacketBuffer(buf, len); + + //等待可读事件 + if(select(sock+1,&readfd,NULL,NULL,&tv) == 0) + return -1; + + //有可读事件 + if(FD_ISSET(sock,&readfd) == 0) + return -2; + + if(MQTTPacket_read(buf, buflen, transport_getdata) != PINGRESP) + return -3; + + return 0; + +} + + +/************************************************************************ +** 函数名称: MQTTSubscribe +** 函数功能: 订阅消息 +** 入口参数: int32_t sock:套接字 +** int8_t *topic:主题 +** enum QoS pos:消息质量 +** 出口参数: >=0:发送成功 <0:发送失败 +** 备 注: +************************************************************************/ +int32_t MQTTSubscribe(int32_t sock,char *topic,enum QoS pos) +{ + static uint32_t PacketID = 0; + uint16_t packetidbk = 0; + int32_t conutbk = 0; + uint8_t buf[100]; + int32_t buflen = sizeof(buf); + MQTTString topicString = MQTTString_initializer; + int32_t len; + int32_t req_qos,qosbk; + + fd_set readfd; + struct timeval tv; + tv.tv_sec = 2; + tv.tv_usec = 0; + + FD_ZERO(&readfd); + FD_SET(sock,&readfd); + + //复制主题 + topicString.cstring = (char *)topic; + //订阅质量 + req_qos = pos; + + //串行化订阅消息 + len = MQTTSerialize_subscribe(buf, buflen, 0, PacketID++, 1, &topicString, &req_qos); + //发送TCP数据 + if(transport_sendPacketBuffer(buf, len) < 0) + return -1; + + //等待可读事件--等待超时 + if(select(sock+1,&readfd,NULL,NULL,&tv) == 0) + return -2; + //有可读事件--没有可读事件 + if(FD_ISSET(sock,&readfd) == 0) + return -3; + + //等待订阅返回--未收到订阅返回 + if(MQTTPacket_read(buf, buflen, transport_getdata) != SUBACK) + return -4; + + //拆订阅回应包 + if(MQTTDeserialize_suback(&packetidbk,1, &conutbk, &qosbk, buf, buflen) != 1) + return -5; + + //检测返回数据的正确性 + if((qosbk == 0x80)||(packetidbk != (PacketID-1))) + return -6; + + //订阅成功 + return 0; +} + +int32_t ReadPacketTimeout(int32_t sock,uint8_t *buf,int32_t buflen,uint32_t timeout) +{ + fd_set readfd; + struct timeval tv; + if(timeout != 0) + { + tv.tv_sec = timeout; + tv.tv_usec = 0; + FD_ZERO(&readfd); + FD_SET(sock,&readfd); + + + if(select(sock+1,&readfd,NULL,NULL,&tv) == 0) + return -1; + + if(FD_ISSET(sock,&readfd) == 0) + return -1; + } + + return MQTTPacket_read(buf, buflen, transport_getdata); +} + +void deliverMessage(MQTTString *TopicName,MQTTMessage *msg,MQTT_USER_MSG *mqtt_user_msg) +{ + //消息质量 + mqtt_user_msg->msgqos = msg->qos; + //保存消息 + memcpy(mqtt_user_msg->msg,msg->payload,msg->payloadlen); + mqtt_user_msg->msg[msg->payloadlen] = 0; + //保存消息长度 + mqtt_user_msg->msglenth = msg->payloadlen; + //消息主题 + memcpy((char *)mqtt_user_msg->topic,TopicName->lenstring.data,TopicName->lenstring.len); + mqtt_user_msg->topic[TopicName->lenstring.len] = 0; + //消息ID + mqtt_user_msg->packetid = msg->id; + //标明消息合法 + mqtt_user_msg->valid = 1; +} + +void UserMsgCtl(MQTT_USER_MSG *msg) +{ + //这里处理数据只是打印,用户可以在这里添加自己的处理方式 + lw_print("****收到订阅的消息******\n"); + //���غ�����Ϣ + switch(msg->msgqos) + { + case 0: + lw_print("MQTT>>消息质量QoS0\n"); + break; + case 1: + lw_print("MQTT>>消息质量QoS1\n"); + break; + case 2: + lw_print("MQTT>>消息质量QoS2\n"); + break; + default: + lw_print("MQTT>>错误的消息质量\n"); + break; + } + lw_print("MQTT>>消息主题:%s\n",msg->topic); + lw_print("MQTT>>消息内容:%s\n",msg->msg); + lw_print("MQTT>>消息长度:%d\n",msg->msglenth); + Proscess(msg->msg); + //处理完后销毁数据 + msg->valid = 0; +} + + +void mqtt_pktype_ctl(uint8_t packtype,uint8_t *buf,uint32_t buflen) +{ + MQTTMessage msg; + int32_t rc; + MQTTString receivedTopic; + uint32_t len; + lw_print("packtype:%d\n",packtype); + switch(packtype) + { + case PUBLISH: + + if(MQTTDeserialize_publish(&msg.dup,(int*)&msg.qos, &msg.retained, &msg.id, &receivedTopic, + (unsigned char **)&msg.payload, &msg.payloadlen, buf, buflen) != 1) + return; + + deliverMessage(&receivedTopic,&msg,&mqtt_user_msg); + + + if(msg.qos == QOS0) + { + //QOS0-不需要ACK + //直接处理数据 + UserMsgCtl(&mqtt_user_msg); + return; + } + //发送PUBACK消息 + if(msg.qos == QOS1) + { + len =MQTTSerialize_puback(buf,buflen,mqtt_user_msg.packetid); + if(len == 0) + return; + //发送返回 + if(transport_sendPacketBuffer(buf,len)<0) + return; + //返回后处理消息 + UserMsgCtl(&mqtt_user_msg); + return; + } + + //对于质量2,只需要发送PUBREC就可以了 + if(msg.qos == QOS2) + { + len = MQTTSerialize_ack(buf, buflen, PUBREC, 0, mqtt_user_msg.packetid); + if(len == 0) + return; + //发送返回 + transport_sendPacketBuffer(buf,len); + } + break; + case PUBREL: + //解析包数据,必须包ID相同才可以 + rc = MQTTDeserialize_ack(&msg.type,&msg.dup, &msg.id, buf,buflen); + if((rc != 1)||(msg.type != PUBREL)||(msg.id != mqtt_user_msg.packetid)) + return ; + //收到PUBREL,需要处理并抛弃数据 + if(mqtt_user_msg.valid == 1) + { + //返回后处理消息 + UserMsgCtl(&mqtt_user_msg); + } + //串行化PUBCMP消息 + len = MQTTSerialize_pubcomp(buf,buflen,msg.id); + if(len == 0) + return; + //发送返回--PUBCOMP + transport_sendPacketBuffer(buf,len); + break; + case PUBACK://等级1客户端推送数据后,服务器返回 + break; + case PUBREC://等级2客户端推送数据后,服务器返回 + break; + case PUBCOMP://等级2客户端推送PUBREL后,服务器返回 + break; + default: + break; + } +} + + +static void *MqttSocketRecvTask(void *arg) +{ +MQTT_START: + lw_print("Recv begin**********\n"); + int fd = -1, clientfd; + int recv_len; + int ret; + char *recv_buf; + struct sockaddr_in mqtt_addr; + socklen_t addr_len; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + lw_print("Socket error\n"); + return NULL; + } + + struct sockaddr_in mqtt_sock; + mqtt_sock.sin_family = AF_INET; + mqtt_sock.sin_port = htons(mqtt_socket_port); + mqtt_sock.sin_addr.s_addr = inet_addr(mqtt_ip_str); + + memset(&(mqtt_sock.sin_zero), 0, sizeof(mqtt_sock.sin_zero)); + + ret = connect(fd, (struct sockaddr *)&mqtt_sock, sizeof(struct sockaddr)); + + if (ret < 0) { + lw_print("Unable to connect %s:%d = %d\n", mqtt_ip_str, mqtt_socket_port, ret); + close(fd); + return NULL; + } + + lw_print("MQTT connect %s:%d success, begin to verify username and password.\n", mqtt_ip_str, mqtt_socket_port); + + if(MQTT_Connect() != Connect_OK) + { + lw_print("MQTT verify failed.\n"); + shutdown(fd, SHUT_WR); + recv(fd, NULL, (size_t)0, 0); + close(fd); + PrivTaskDelay(1000); + goto MQTT_START; + } + + lw_print("MQTT subscribe begin.\n"); + if(MQTTSubscribe(fd,(char *)TOPIC,QOS1) < 0) + { + lw_print("MQTT subscribe failed.\n"); + shutdown(fd, SHUT_WR); + recv(fd, NULL, (size_t)0, 0); + close(fd); + return NULL; + } + + lw_print("subscribe success.\n"); + + fd_set readfd; + uint8_t no_mqtt_msg_exchange = 1; + uint8_t buf[MSG_MAX_LEN]; + int32_t buflen = sizeof(buf); + int32_t type; + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 10; + + int32_t curtick=0; + + while(1) + { + // lw_print("waiting********\n"); + curtick +=1; + no_mqtt_msg_exchange = 1; + + FD_ZERO(&readfd); + FD_SET(fd,&readfd); + select(fd+1,&readfd,NULL,NULL,&tv); + + if(FD_ISSET(fd,&readfd) != 0) + { + + type = ReadPacketTimeout(fd,buf,buflen,0); + if(type != -1) + { + lw_print("ctl***********\n"); + mqtt_pktype_ctl(type,buf,buflen); + + no_mqtt_msg_exchange = 0; + + } + } + + if( curtick >(2*10000)) + { + curtick =0; + //判断是否有数据交换 + if(no_mqtt_msg_exchange == 0) + { + //如果有数据交换,这次就不需要发送PING消息 + continue; + } + + if(MQTT_PingReq(fd) < 0) + { + //重连服务器 + lw_print("发送保持活性ping失败....\n"); + goto CLOSE; + } + + + lw_print("发送保持活性ping作为心跳成功....\n"); + + no_mqtt_msg_exchange = 0; + } + } + +CLOSE: + lw_print("MQTT subscribe failed.\n"); + shutdown(fd, SHUT_WR); + recv(fd, NULL, (size_t)0, 0); + close(fd); + return NULL; + + +} + + + +void MqttSocketRecvTest(int argc, char *argv[]) +{ + if(argc >= 2) { + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + MqttSocketConfigParam(argv[1]); + } + + + // ip4_addr_t dns_ip; + // netconn_gethostbyname(HOST_NAME, &dns_ip); + // char* host_ip = ip_ntoa(&dns_ip); + // lw_print("host name : %s , host_ip : %s\n",HOST_NAME,host_ip); + // MqttSocketConfigParam(host_ip); + + +#ifdef ADD_XIZI_FEATURES + lwip_config_tcp(0, mqtt_demo_ipaddr, mqtt_demo_netmask, mqtt_demo_gwaddr); + + pthread_attr_t attr; + attr.schedparam.sched_priority = LWIP_MQTT_DEMO_TASK_PRIO; + attr.stacksize = LWIP_MQTT_DEMO_TASK_STACK_SIZE; +#endif + +#ifdef ADD_NUTTX_FEATURES + pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; + attr.priority = LWIP_mqtt_DEMO_TASK_PRIO; + attr.stacksize = LWIP_mqtt_DEMO_TASK_STACK_SIZE; +#endif + + PrivTaskCreate(&mqtt_server_task, &attr, &MqttSocketRecvTask, NULL); + PrivTaskStartup(&mqtt_server_task); +} + +PRIV_SHELL_CMD_FUNCTION(MqttSocketRecvTest, a tcp send sample, PRIV_SHELL_CMD_MAIN_ATTR); + + +typedef struct +{ + uint8_t humi_high8bit; //ԭʼ���ݣ�ʪ�ȸ�8λ + uint8_t humi_low8bit; //ԭʼ���ݣ�ʪ�ȵ�8λ + uint8_t temp_high8bit; //ԭʼ���ݣ��¶ȸ�8λ + uint8_t temp_low8bit; //ԭʼ���ݣ��¶ȸ�8λ + uint8_t check_sum; //У��� + double humidity; //ʵ��ʪ�� + double temperature; //ʵ���¶� +} DHT11_Data_TypeDef; + + +uint16_t GetNextPackID(void) +{ + static uint16_t pubpacketid = 0; + return pubpacketid++; +} + +int32_t WaitForPacket(int32_t sock,uint8_t packettype,uint8_t times) +{ + int32_t type; + uint8_t buf[MSG_MAX_LEN]; + uint8_t n = 0; + int32_t buflen = sizeof(buf); + do + { + //读取数据包 + type = ReadPacketTimeout(sock,buf,buflen,2); + if(type != -1) + mqtt_pktype_ctl(type,buf,buflen); + n++; + }while((type != packettype)&&(n < times)); + //收到期望的包 + if(type == packettype) + return 0; + else + return -1; +} + +int32_t MQTTMsgPublish(int32_t sock, char *topic, int8_t qos, uint8_t* msg) +{ + int8_t retained = 0; //保留标志位 + uint32_t msg_len; //数据长度 + uint8_t buf[MSG_MAX_LEN]; + int32_t buflen = sizeof(buf),len; + MQTTString topicString = MQTTString_initializer; + uint16_t packid = 0,packetidbk; + + //填充主题 + topicString.cstring = (char *)topic; + + //填充数据包ID + if((qos == QOS1)||(qos == QOS2)) + { + packid = GetNextPackID(); + } + else + { + qos = QOS0; + retained = 0; + packid = 0; + } + + msg_len = strlen((char *)msg); + + //推送消息 + len = MQTTSerialize_publish(buf, buflen, 0, qos, retained, packid, topicString, (unsigned char*)msg, msg_len); + if(len <= 0) + return -1; + if(transport_sendPacketBuffer(buf, len) < 0) + return -2; + + //质量等级0,不需要返回 + if(qos == QOS0) + { + return 0; + } + + //等级1 + if(qos == QOS1) + { + //等待PUBACK + if(WaitForPacket(sock,PUBACK,5) < 0) + return -3; + return 1; + + } + //等级2 + if(qos == QOS2) + { + //等待PUBREC + if(WaitForPacket(sock,PUBREC,5) < 0) + return -3; + //发送PUBREL + len = MQTTSerialize_pubrel(buf, buflen,0, packetidbk); + if(len == 0) + return -4; + if(transport_sendPacketBuffer(buf, len) < 0) + return -6; + //等待PUBCOMP + if(WaitForPacket(sock,PUBREC,5) < 0) + return -7; + return 2; + } + //等级错误 + return -8; +} + + +static void *MqttSocketSendTask(void *arg) +{ + + int fd = -1, clientfd; + int recv_len; + int ret; + char *recv_buf; + struct sockaddr_in mqtt_addr; + socklen_t addr_len; + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + lw_print("Socket error\n"); + return NULL; + } + + struct sockaddr_in mqtt_sock; + mqtt_sock.sin_family = AF_INET; + mqtt_sock.sin_port = htons(mqtt_socket_port); + mqtt_sock.sin_addr.s_addr = inet_addr(mqtt_ip_str); + + memset(&(mqtt_sock.sin_zero), 0, sizeof(mqtt_sock.sin_zero)); + + ret = connect(fd, (struct sockaddr *)&mqtt_sock, sizeof(struct sockaddr)); + + if (ret < 0) { + lw_print("Unable to connect %s:%d = %d\n", mqtt_ip_str, mqtt_socket_port, ret); + close(fd); + return NULL; + } + + lw_print("MQTT connect %s:%d success, begin to verify hostname and password.\n", mqtt_ip_str, mqtt_socket_port); + + if(MQTT_Connect() != Connect_OK) + { + lw_print("MQTT verify failed.\n"); + shutdown(fd, SHUT_WR); + recv(fd, NULL, (size_t)0, 0); + close(fd); + return NULL; + } + + lw_print("MQTT subscribe begin.\n"); + if(MQTTSubscribe(fd,(char *)TOPIC,QOS1) < 0) + { + lw_print("MQTT subscribe failed.\n"); + shutdown(fd, SHUT_WR); + recv(fd, NULL, (size_t)0, 0); + close(fd); + return NULL; + } + + lw_print("subscribe success.\n"); + + + + uint8_t no_mqtt_msg_exchange = 1; + uint32_t curtick=0; + uint8_t res; + + cJSON* cJSON_Data = NULL; + cJSON_Data = cJSON_Data_Init(); + DHT11_Data_TypeDef* recv_data; + + double a,b; + while(1) + { + curtick+=1; + char* p ="Hello,here is hc"; + ret = MQTTMsgPublish(fd,(char*)TOPIC,QOS0,(uint8_t*)p); + if(ret >= 0) + { + no_mqtt_msg_exchange = 0; + PrivTaskDelay(1000); + } + } +} + + +void MqttSocketSendTest(int argc, char *argv[]) +{ + if(argc >= 2) { + lw_print("lw: [%s] target ip %s\n", __func__, argv[1]); + MqttSocketConfigParam(argv[1]); + } + + + // ip4_addr_t dns_ip; + // netconn_gethostbyname(HOST_NAME, &dns_ip); + // char* host_ip = ip_ntoa(&dns_ip); + // lw_print("host name : %s , host_ip : %s\n",HOST_NAME,host_ip); + // MqttSocketConfigParam(host_ip); + + +#ifdef ADD_XIZI_FEATURES + lwip_config_tcp(0, mqtt_demo_ipaddr, mqtt_demo_netmask, mqtt_demo_gwaddr); + + pthread_attr_t attr; + attr.schedparam.sched_priority = LWIP_MQTT_DEMO_TASK_PRIO; + attr.stacksize = LWIP_MQTT_DEMO_TASK_STACK_SIZE; +#endif + +#ifdef ADD_NUTTX_FEATURES + pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER; + attr.priority = LWIP_mqtt_DEMO_TASK_PRIO; + attr.stacksize = LWIP_mqtt_DEMO_TASK_STACK_SIZE; +#endif + + PrivTaskCreate(&mqtt_client_task, &attr, &MqttSocketSendTask, NULL); + PrivTaskStartup(&mqtt_client_task); +} + +PRIV_SHELL_CMD_FUNCTION(MqttSocketSendTest, a tcp send sample, PRIV_SHELL_CMD_MAIN_ATTR); + diff --git a/APP_Framework/Applications/app_test/test_mqttclient/test_mqttclient.h b/APP_Framework/Applications/app_test/test_mqttclient/test_mqttclient.h new file mode 100644 index 000000000..d52cfd6d3 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_mqttclient/test_mqttclient.h @@ -0,0 +1,99 @@ +/* + * 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 test_mqttclient.h + * @brief mqtt subscribe fuction test + * @version 3.0 + * @author AIIT XUOS Lab + * @date 2023.8.10 + */ + +#include + +#define MSG_MAX_LEN 1024 +#define MSG_TOPIC_LEN 50 +#define KEEPLIVE_TIME 650 +#define MQTT_VERSION 4 + +#ifdef LWIP_DNS +#define HOST_NAME "iot-06z00im0uwa0ki2.mqtt.iothub.aliyuncs.com" //服务器域名 +#else +#define HOST_NAME "iot-06z00im0uwa0ki2.mqtt.iothub.aliyuncs.com" //服务器IP地址 +#endif + + +//#define HOST_IP "129.204.201.235" +#define HOST_PORT 1883 //由于是TCP连接,端口必须是1883 + +#define CLIENT_ID "iw3rn3pa11K.test|securemode=2,signmethod=hmacsha256,timestamp=1689296035604|" //随机的id +#define USER_NAME "test&iw3rn3pa11K" //用户名 +#define PASSWORD "7b948d22fe46f0f63d1a403376d26e7cb298abc227d29e44311d7040307a71f8" //秘钥 + + + +#define TOPIC "/iw3rn3pa11K/test/user/Test" //订阅的主题 + +#define TEST_MESSAGE "test_message" //发送测试消息 + + +// #define CLIENT_ID "hc123456789" //随机的id +// #define USER_NAME "xiuos" //用户名 +// #define PASSWORD "xiuos" //秘钥 + + +enum QoS +{ QOS0 = 0, + QOS1, + QOS2 +}; + +enum MQTT_Connect +{ + Connect_OK = 0, + Connect_NOK, + Connect_NOTACK +}; + +//数据交互结构体 +typedef struct __MQTTMessage +{ + uint32_t qos; + uint8_t retained; + uint8_t dup; + uint16_t id; + uint8_t type; + void *payload; + int32_t payloadlen; +}MQTTMessage; + +//用户接收消息结构体 +typedef struct __MQTT_MSG +{ + uint8_t msgqos; //消息质量 + uint8_t msg[MSG_MAX_LEN]; //消息 + uint32_t msglenth; //消息长度 + uint8_t topic[MSG_TOPIC_LEN]; //主题 + uint16_t packetid; //消息ID + uint8_t valid; //标明消息是否有效 +}MQTT_USER_MSG; + +//发送消息结构体 +typedef struct +{ + int8_t topic[MSG_TOPIC_LEN]; + int8_t qos; + int8_t retained; + + uint8_t msg[MSG_MAX_LEN]; + uint8_t msglen; +} mqtt_recv_msg_t, *p_mqtt_recv_msg_t, mqtt_send_msg_t, *p_mqtt_send_msg_t; \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_radix_tree/README.md b/APP_Framework/Applications/app_test/test_radix_tree/README.md index b2bfe9cb9..fb4efcb6f 100644 --- a/APP_Framework/Applications/app_test/test_radix_tree/README.md +++ b/APP_Framework/Applications/app_test/test_radix_tree/README.md @@ -9,13 +9,15 @@ 基数树节点设计为: ```c -typedef struct _node { -​ void* value; -​ struct _node* next[NODE_SIZE]; -} node; +typedef struct radix_node +{ + void *value; + struct radix_node *child[NODE_SIZE]; + struct radix_node *parent; +} radix_node; ``` -其中,节点在树中的路径即为键,`value` 存储值,`NODE_SIZE` 定义为 128,足以容纳所有 ASCII 值。 +其中,节点在树中的路径即为键,为`unsigned int`类型,`value` 存储值,`NODE_SIZE` 定义为 4,即每个树节点包含2个bit位,可以根据实际需求调整。 一共实现了 5 个函数,分别为: @@ -32,20 +34,19 @@ typedef struct _node { 测试程序定义了以下键值对: ```c -char keys[][MAX_WORD_LEN] = { +char values[][16] = { "what", "where", "why", "how", "hello!", "apple", - "12345" -}; -int values[] = {1, 2, 3, 4, 5, 6, 7}; + "12345"}; +unsigned int keys[] = {1, 2, 3, 4, 5, 6, 7}; ``` 1. 程序的第一部分创建了基数树,并且将定义的 7 个键值对的前 6 个插入了基数树,然后分别查找 7 个键,前 6 个均可以找到对应的值,最后一个未插入,因此无法找到 -2. 程序的第二部分从基数树中删除了 `where` 和 `how` 两个键,再次分别查找 7 个键,删除的键值对和未插入的键值对均无法找到 +2. 程序的第二部分从基数树中删除了 `where` 和 `how` 两个值的键,再次分别查找 7 个键,删除的键值对和未插入的键值对均无法找到 3. 程序的第三部分重新插入了已删除的 `where` 和未插入过的 `12345` ,再次分别查找 7 个键,新插入的值可以检索到 4. 程序的第四部分将基数树销毁,再次分别查找 7 个键,所有的键值对均无法找到 diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig6.png b/APP_Framework/Applications/app_test/test_radix_tree/fig6.png index 80bf28136..a8f0ab9c0 100644 Binary files a/APP_Framework/Applications/app_test/test_radix_tree/fig6.png and b/APP_Framework/Applications/app_test/test_radix_tree/fig6.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/fig7.png b/APP_Framework/Applications/app_test/test_radix_tree/fig7.png index 7fad83c9e..4b1073ba4 100644 Binary files a/APP_Framework/Applications/app_test/test_radix_tree/fig7.png and b/APP_Framework/Applications/app_test/test_radix_tree/fig7.png differ diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c index c54a49805..a2c96e15e 100644 --- a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.c @@ -1,23 +1,27 @@ /** -* @file: test_radix_tree.c -* @brief: Implement a simple radix tree -* @version: 1.0 -* @date: 2023/5/24 -*/ + * @file: test_radix_tree.c + * @brief: Implement a simple radix tree + * @version: 1.0 + * @date: 2023/5/24 + */ -#include #include "test_radix_tree.h" /** * @description: Create a radix tree node * @return node pointer */ -node* CreateNode() +radix_node *CreateNode() { - node* n = (node*)malloc(sizeof(node)); - n->value = NULL; - for (int i = 0; i < NODE_SIZE; i++) { - n->next[i] = NULL; + radix_node *n = (radix_node *)malloc(sizeof(radix_node)); + if (n != NULL) + { + n->parent = NULL; + n->value = NULL; + for (int i = 0; i < NODE_SIZE; ++i) + { + n->child[i] = NULL; + } } return n; } @@ -29,21 +33,32 @@ node* CreateNode() * @param value - new node value * @return void */ -void InsertNode(node* root, const char* key, void* value) +int InsertNode(radix_node *root, unsigned int key, void *value) { - if (root == NULL) { - return; + if (root == NULL) + { + return -1; // The root node is empty } - node* cur = root; - size_t len = strlen(key); - for (size_t i = 0; i < len; i++) { - uint8_t b = (uint8_t)key[i]; - if (cur->next[b] == NULL) { - cur->next[b] = CreateNode(); + radix_node *cur = root; + int temp; + for (int i = radix_tree_height - 1; i >= 0; --i) + { + temp = CHECK_BITS(key, i); + if (!cur->child[temp]) + { + cur->child[temp] = CreateNode(); + if (!cur->child[temp]) + return -2; // Failed to apply for a node + cur->child[temp]->parent = cur; } - cur = cur->next[b]; + cur = cur->child[temp]; } + if (cur->value == value) + return -3; // Repeat insertion + if (cur->value != NULL) + return -4; // Already occupied cur->value = value; + return 0; } /** @@ -52,38 +67,27 @@ void InsertNode(node* root, const char* key, void* value) * @param key - key which is needed to delete * @return void */ -void DeleteNode(node* root, const char* key) +void DeleteNode(radix_node *root, unsigned int key) { - if (root == NULL) { + if (root == NULL) + { return; } - node** cur = &root; - size_t len = strlen(key); - for (size_t i = 0; i < len; i++) { - uint8_t b = (uint8_t)key[i]; - if ((*cur)->next[b] == NULL) { - return; - } - cur = &((*cur)->next[b]); - } - - if ((*cur)->value == NULL) { - return; - } - - (*cur)->value = NULL; - - int has_children = 0; - for (int i = 0; i < NODE_SIZE; i++) { - if ((*cur)->next[i] != NULL) { - has_children = 1; + radix_node *cur = root; + int temp; + for (int i = radix_tree_height - 1; i >= 0; --i) + { + temp = CHECK_BITS(key, i); + cur = cur->child[temp]; + if (!cur) break; - } - } - if (!has_children) { - free(*cur); - (*cur) = NULL; } + + if (!cur) + return; + + cur->parent->child[temp] = NULL; + free(cur); } /** @@ -92,20 +96,23 @@ void DeleteNode(node* root, const char* key) * @param key - key which is needed to find * @return value pointer corresponding to key */ -void* FindNode(node* root, const char* key) +void *FindNode(radix_node *root, unsigned int key) { - if (root == NULL) { + if (root == NULL) + { return NULL; } - node* cur = root; - size_t len = strlen(key); - for (size_t i = 0; i < len; i++) { - uint8_t b = (uint8_t)key[i]; - if (cur->next[b] == NULL) { - return NULL; - } - cur = cur->next[b]; + radix_node *cur = root; + int temp; + for (int i = radix_tree_height - 1; i >= 0; --i) + { + temp = CHECK_BITS(key, i); + cur = cur->child[temp]; + if (!cur) + break; } + if (!cur) + return NULL; return cur->value; } @@ -114,73 +121,88 @@ void* FindNode(node* root, const char* key) * @param root - radix tree root * @return void */ -void DestroyTree(node* root) +void DestroyTree(radix_node *root) { - if (root == NULL) { + if (root == NULL) + { return; } - for (int i = 0; i < NODE_SIZE; i++) { - DestroyTree(root->next[i]); + for (int i = 0; i < NODE_SIZE; i++) + { + DestroyTree(root->child[i]); } free(root); } void TestRadix() { - char keys[][MAX_WORD_LEN] = { + char values[][16] = { "what", "where", "why", "how", "hello!", "apple", - "12345" - }; - int values[] = {1, 2, 3, 4, 5, 6, 7}; + "12345"}; + unsigned int keys[] = {1, 2, 3, 4, 5, 6, 7}; printf("\nCreate tree and add key & value:\n"); - node* root = CreateNode(); - if (!root) printf("Create node failed.\n"); + radix_node *root = CreateNode(); + if (!root) + printf("Create node failed.\n"); int num = sizeof(keys) / sizeof(keys[0]); - for (int i = 0; i < num - 1; ++i) { + for (int i = 0; i < num - 1; ++i) + { InsertNode(root, keys[i], &values[i]); } - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + for (int i = 0; i < num; ++i) + { + char *v = (char *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } printf("\nDelete \"where\" and \"how\":\n"); DeleteNode(root, keys[1]); DeleteNode(root, keys[3]); - - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + + for (int i = 0; i < num; ++i) + { + char *v = (char *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } printf("\nInsert \"where\" and \"12345\":\n"); InsertNode(root, keys[1], &values[1]); InsertNode(root, keys[6], &values[6]); - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + for (int i = 0; i < num; ++i) + { + char *v = (char *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } printf("\nDestroy tree:\n"); DestroyTree(root); root = NULL; - for (int i = 0; i < num; ++i) { - int* v = (int*)FindNode(root, keys[i]); - if (v) printf("keys[%d] \"%s\"'v = %d, values[%d] = %d\n", i, keys[i], *v, i, values[i]); - else printf("keys[%d] \"%s\" not found\n", i, keys[i]); + for (int i = 0; i < num; ++i) + { + char *v = (char *)FindNode(root, keys[i]); + if (v) + printf("keys[%d] %x, values[%d] = %s\n", i, keys[i], i, v); + else + printf("keys[%d] %x not found\n", i, keys[i]); } } diff --git a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h index ea9e7b7fa..cef53561f 100644 --- a/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h +++ b/APP_Framework/Applications/app_test/test_radix_tree/test_radix_tree.h @@ -1,20 +1,27 @@ /** -* @file: test_radix_tree.h -* @brief: Implement a simple radix tree -* @version: 1.0 -* @date: 2023/5/24 -*/ + * @file: test_radix_tree.h + * @brief: Implement a simple radix tree + * @version: 1.0 + * @date: 2023/5/24 + */ -#define NODE_SIZE 128 -#define MAX_WORD_LEN 128 +#include -typedef struct _node { - void* value; - struct _node* next[NODE_SIZE]; -} node; +#define NODE_SIZE 4 +#define BITS 2 +#define CHECK_BITS(key, pos) ((((unsigned int)(key)) << (sizeof(int) * 8 - (pos + 1) * BITS)) >> (sizeof(int) * 8 - BITS)) -node* CreateNode(); -void InsertNode(node* root, const char* key, void* value); -void DeleteNode(node* root, const char* key); -void* FindNode(node* root, const char* key); -void DestroyTree(node* root); \ No newline at end of file +const int radix_tree_height = sizeof(void *) * 8 / BITS; // Height of tree + +typedef struct radix_node +{ + void *value; + struct radix_node *child[NODE_SIZE]; + struct radix_node *parent; +} radix_node; + +radix_node *CreateNode(); +int InsertNode(radix_node *root, unsigned int key, void *value); +void DeleteNode(radix_node *root, unsigned int key); +void *FindNode(radix_node *root, unsigned int key); +void DestroyTree(radix_node *root); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_webserver/README.md b/APP_Framework/Applications/app_test/test_webserver/README.md new file mode 100644 index 000000000..fe0461c4e --- /dev/null +++ b/APP_Framework/Applications/app_test/test_webserver/README.md @@ -0,0 +1,67 @@ +# 基于矽璓已实现的Lwip,在ARM上实现基本的Web服务## + +## 1. 简介 +基于矽璓已实现的Lwip,在ARM上实现基本的Web服务,其功能支持IP、GateWay、DNS、子网掩码等网络参数的设置。 +主要功能为:web服务等待Http请求的到来,解析Http报文后,设置本机的网络参数信息,在终端打印设置后的网络参数信息,并返回浏览器客户端成功响应。 + +## 2. 数据结构设计说明 +Server的网络结构参数为: +``` +struct ServerNet +{ + char local_ip[20]; + char local_mask[20]; + char local_gw[20]; + char local_dns[20]; +}ServerNet; +``` + +主要实现了以下几个函数: +``` +TcpThread: 建立socket监听,循环等待网络通信事件的到来; +HandleHttpRequest:处理并解析Http报文,得到网络参数信息; +AddrUpdate: 根据到来的参数,更新本机网络参数; +LwipShowIP:显示网络参数信息,包括IP、GateWay、DNS、子网掩码; +LwipSetIP: 设置网络参数信息,包括IP、GateWay、DNS、子网掩码; +ShowDns: 显示dns网址; +SetDns: 设置dns网址; +``` +除此之外,还包括两个工具函数: +``` +SubstractServerNet: 截取http报文的请求体,格式为:ip=192.168.1.9&netmask=255.255.255.0&iproute=192.168.1.1&dns=114.114.114.114 +substring: 截取字符串 + +``` + +## 3. 测试程序说明 +1. 通过数据线将开发板连接电脑,并通过网线连接使开发板接入家庭局域网。 +2. 将TestWebserver注册为shell 命令,编译前通过menuconfig打开lwip和TestWebserver功能。 +3. 将编译成功的XiZi-edu-arm32.bin文件烧录到arm板子。烧录bin文件前,需要将开发板boot引脚拉高(盖上跳帽),上电后再按下reset按键,之后可以点击烧写工具的执行按钮,等待烧写完成。 +4. 将boot引脚拉低(摘下跳帽),按下reset按键,使用串口工具显示xiuos的命令行界面。 +5. 将开发板的本机ip通过setip命令设置为与电脑端在同一个局域网之下,利用ping命令检测成功通信。 +6. 运行TestWebserver命令验证功能。 + +## 4. 运行结果(##需结合运行测试截图按步骤说明##) + + +烧录程序,boot引脚拉高,按下reset按键。 + + + +点击执行烧录,程序烧录成功。 + + + +打开串口调试工具,xiuos程序成功在开发板上运行。 + + + +利用setip命令,修改开发板ip,使得开发板和电脑在同一个局域网内,并使用ping命令测试。 + + + +在服务端运行TestWebserver命令,打开index.html,设置网络参数信息,点击提交,发送http请求。 + + + +web服务收到http请求后并打印post请求报文,以及修改后的开发板的网络参数信息,包括IP、子网掩码、GateWay和DNS。 diff --git a/APP_Framework/Applications/app_test/test_webserver/index.html b/APP_Framework/Applications/app_test/test_webserver/index.html new file mode 100644 index 000000000..d0514189a --- /dev/null +++ b/APP_Framework/Applications/app_test/test_webserver/index.html @@ -0,0 +1,33 @@ + + + + + 表单 + + + + +
+

设置路由信息

+ + +

IP地址:

+

子网掩码:

+

默认网关:

+

DNS服务器

+

+ + +

+ +
+ + + diff --git a/APP_Framework/Applications/app_test/test_webserver/photos/1.png b/APP_Framework/Applications/app_test/test_webserver/photos/1.png new file mode 100644 index 000000000..2c3980afd Binary files /dev/null and b/APP_Framework/Applications/app_test/test_webserver/photos/1.png differ diff --git a/APP_Framework/Applications/app_test/test_webserver/photos/2.png b/APP_Framework/Applications/app_test/test_webserver/photos/2.png new file mode 100644 index 000000000..e2fc46ebd Binary files /dev/null and b/APP_Framework/Applications/app_test/test_webserver/photos/2.png differ diff --git a/APP_Framework/Applications/app_test/test_webserver/photos/3.png b/APP_Framework/Applications/app_test/test_webserver/photos/3.png new file mode 100644 index 000000000..0db94c2f8 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_webserver/photos/3.png differ diff --git a/APP_Framework/Applications/app_test/test_webserver/photos/4.png b/APP_Framework/Applications/app_test/test_webserver/photos/4.png new file mode 100644 index 000000000..7f337458a Binary files /dev/null and b/APP_Framework/Applications/app_test/test_webserver/photos/4.png differ diff --git a/APP_Framework/Applications/app_test/test_webserver/photos/5.png b/APP_Framework/Applications/app_test/test_webserver/photos/5.png new file mode 100644 index 000000000..85ab2f41e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_webserver/photos/5.png differ diff --git a/APP_Framework/Applications/app_test/test_webserver/photos/6.png b/APP_Framework/Applications/app_test/test_webserver/photos/6.png new file mode 100644 index 000000000..37b6e162f Binary files /dev/null and b/APP_Framework/Applications/app_test/test_webserver/photos/6.png differ diff --git a/APP_Framework/Applications/app_test/test_webserver/test_webserver.c b/APP_Framework/Applications/app_test/test_webserver/test_webserver.c new file mode 100644 index 000000000..a5f15f2c1 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_webserver/test_webserver.c @@ -0,0 +1,218 @@ +/** +* @file: test_webserver.c +* @brief: a application of test webserver function +* @version: 1.0 +* @author: Yao wenying +* @date: 2023/08/20 +*/ + + +#include +#include "test_webserver.h" + +/**设置ip,网关,子网掩码这部分参考lwip_config_demo.c*/ +void LwipShowIP() +{ + // find default netdev + lw_notice("\r\n************************************************\r\n"); + lw_notice(" Network Configuration\r\n"); + lw_notice("************************************************\r\n"); + lw_notice(" ETH0 IPv4 Address : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_ipaddr)[0], ((u8_t*)&lwip_eth0_ipaddr)[1], + ((u8_t*)&lwip_eth0_ipaddr)[2], ((u8_t*)&lwip_eth0_ipaddr)[3]); + lw_notice(" ETH0 IPv4 Subnet mask : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_eth0_netmask)[0], ((u8_t*)&lwip_eth0_netmask)[1], + ((u8_t*)&lwip_eth0_netmask)[2], ((u8_t*)&lwip_eth0_netmask)[3]); + lw_notice(" ETH0 IPv4 Gateway : %u.%u.%u.%u\r\n", ((u8_t*)&lwip_gwaddr)[0], ((u8_t*)&lwip_eth0_gwaddr)[1], + ((u8_t*)&lwip_eth0_gwaddr)[2], ((u8_t*)&lwip_eth0_gwaddr)[3]); + lw_notice("************************************************\r\n"); +} + +uint8_t enet_idd = 0; +void LwipSetIP(int argc, char *argv[]) +{ + if(argc >= 4) + { + printf("lw: [%s] ip %s mask %s gw %s netport %s\n", __func__, argv[1], argv[2], argv[3], argv[4]); + sscanf(argv[1], "%d.%d.%d.%d", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]); + sscanf(argv[2], "%d.%d.%d.%d", &lwip_netmask[0], &lwip_netmask[1], &lwip_netmask[2], &lwip_netmask[3]); + sscanf(argv[3], "%d.%d.%d.%d", &lwip_gwaddr[0], &lwip_gwaddr[1], &lwip_gwaddr[2], &lwip_gwaddr[3]); + sscanf(argv[4], "%d", &enet_idd); + + if(0 == enet_idd) + { + printf("save eth0 info\n"); + memcpy(lwip_eth0_ipaddr, lwip_ipaddr, 20); + memcpy(lwip_eth0_netmask, lwip_netmask, 20); + memcpy(lwip_eth0_gwaddr, lwip_gwaddr, 20); + } + else if(1 == enet_idd) + { + printf("save eth1 info\n"); + memcpy(lwip_eth1_ipaddr, lwip_ipaddr, 20); + memcpy(lwip_eth1_netmask, lwip_netmask, 20); + memcpy(lwip_eth1_gwaddr, lwip_gwaddr, 20); + } + } + else if(argc == 2) + { + printf("lw: [%s] set eth0 ipaddr %s \n", __func__, argv[1]); + sscanf(argv[1], "%d.%d.%d.%d", &lwip_ipaddr[0], &lwip_ipaddr[1], &lwip_ipaddr[2], &lwip_ipaddr[3]); + memcpy(lwip_eth0_ipaddr, lwip_ipaddr, strlen(lwip_ipaddr)); + } + // sys_thread_new("SET ip address", LwipSetIPTask, &enet_id, LWIP_TASK_STACK_SIZE, LWIP_DEMO_TASK_PRIO); + printf("lw: [%s] config netport id[%d]\n", __func__, enet_idd); + lwip_config_net(enet_idd, lwip_ipaddr, lwip_netmask, lwip_gwaddr); +} + +void ShowDns(){ + const ip_addr_t *dns = dns_getserver(0); + char *dns_ip = ipaddr_ntoa(dns); + printf("lw: eth0 dns is %s \n", dns_ip); +} + +void SetDns(char* ip){ + ip_addr_t dns; + ipaddr_aton(ip, &dns); + dns_setserver(0, &dns); + printf("ETH0 Set DNS Success!\n"); +} + +void AddrUpdate() +{ + char *argv[5]; + argv[1] = ServerNet.local_ip; + argv[2] = ServerNet.local_mask; + argv[3] = ServerNet.local_gw ; + argv[4] = 0; + LwipSetIP(4, argv); + LwipShowIP(); + SetDns(ServerNet.local_dns); + ShowDns(); +} + +char *substring(char *dst,char *src,int start,int len) +{ + char *p=dst; + char *q=src; + int length=strlen(src); + if(start>=length||start<0) + return NULL; + if(len>length) + len=length-start; + q+=start; + while(len--) + { + *(p++)=*(q++); + } + *(p++)='\0'; + return dst; +} + +void SubstractServerNet(char * params){ + char *p =params; + p +=3; + char *end = strchr(p, '&'); + int len = (int)(end-p); + substring(ServerNet.local_ip, p, 0,len); + p +=len; + p += 9; + end = strchr(p, '&'); + len = (int)(end-p); + substring(ServerNet.local_mask , p, 0, len); + p +=len; + p +=9; + end = strchr(p, '&'); + len = (int)(end-p); + substring(ServerNet.local_gw, p, 0, len); + p +=len; + p += 5; + strcpy(ServerNet.local_dns, p); +} + + +void HandleHttpRequest(char *params){ + SubstractServerNet(params); + AddrUpdate(); //修改本机参数信息; +} + +//设置socket +//循环监听socket并修改网络信息,单线程 +void tcpecho_thread(){ + int sock=-1, connected; + char *recv_data; + struct sockaddr_in server_addr, client_addr; + socklen_t sin_size; + int recv_data_len; + + recv_data = (char *) malloc(RECV_DATA); + + if(recv_data == NULL){ + printf("No memory"); + goto __exit; + } + + sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0){ + printf("Socket error\n"); + goto __exit; + } + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(PORT); + memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); + + if(bind(sock,(struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == 1){ + printf("Unable to bind\n"); + goto __exit; + } + if (listen(sock, 5) == -1) + { + printf("Listen error\n"); + goto __exit; + } + + while(1){ + sin_size = sizeof(struct sockaddr_in); + connected = accept(sock, (struct sockaddr *)&client_addr, &sin_size); + + printf("new client connected from (%s, %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + + { + int flag = 1; + + setsockopt(connected, + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (void *) &flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + } + + + recv_data_len = recv(connected, recv_data, RECV_DATA, 0); + + printf("recv %d len data\n",recv_data_len); + recv_data[recv_data_len] = '\0'; + printf("%s\n", recv_data); + + char * params = strstr(recv_data, "ip="); + HandleHttpRequest(params); + + char *response = "HTTP/1.1 200 OK\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n"; + write(connected,response,strlen(response)); + if (connected >= 0) + closesocket(connected); + + connected = -1; + } + __exit: + if (sock >= 0) closesocket(sock); + if (recv_data) free(recv_data); +} + +void TestWebserver(){ + tcpecho_thread(); +} + +PRIV_SHELL_CMD_FUNCTION(TestWebserver, Implement web_server, PRIV_SHELL_CMD_MAIN_ATTR); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_webserver/test_webserver.h b/APP_Framework/Applications/app_test/test_webserver/test_webserver.h new file mode 100644 index 000000000..105ff13c7 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_webserver/test_webserver.h @@ -0,0 +1,43 @@ +/** +* @file: test_webserver.h +* @brief: a application of test webserver function +* @version: 1.0 +* @author: Yao wenying +* @date: 2023/08/20 +*/ + + +#ifndef __WEBSERVER_H__ +#define __WEBSERVER_H__ + +#include "lwip/opt.h" +#include +#include "lwip/api.h" +#include "lwip/sys.h" +#include "netif/ethernet.h" +#include "lwip/tcpip.h" +#include "sys_arch.h" +#include +#include "dns.h" +#include "string.h" + + +#define PORT 80 +#define RECV_DATA 1024 +struct ServerNet +{ + char local_ip[20]; + char local_mask[20]; + char local_gw[20]; + char local_dns[20]; +}ServerNet; + +void LwipShowIP(); +void LwipSetIP(int argc, char *argv[]); +void ShowDns(); +void SetDns(); +void AddrUpdate(); //更新本机网络参数信息 +void HandleHttpRequest(); //解析报文,并返回响应 +void TcpThread();//建立socket监听,循环等待网络通信事件到来 + +#endif \ No newline at end of file diff --git a/APP_Framework/lib/cJSON/Makefile b/APP_Framework/lib/cJSON/Makefile index 79f1fc37d..8d2e20e45 100755 --- a/APP_Framework/lib/cJSON/Makefile +++ b/APP_Framework/lib/cJSON/Makefile @@ -1,3 +1,4 @@ SRC_FILES := cJSON.c +SRC_FILES += cJSON_Process.c include $(KERNEL_ROOT)/compiler.mk diff --git a/APP_Framework/lib/cJSON/cJSON_Process.c b/APP_Framework/lib/cJSON/cJSON_Process.c new file mode 100644 index 000000000..12490a10b --- /dev/null +++ b/APP_Framework/lib/cJSON/cJSON_Process.c @@ -0,0 +1,85 @@ +#include "cJSON_Process.h" +#include + + + + + +cJSON* cJSON_Data_Init(void) +{ + cJSON* cJSON_Root = NULL; //json根节点 + + + cJSON_Root = cJSON_CreateObject(); /*创建项目*/ + if(NULL == cJSON_Root) + { + return NULL; + } + cJSON_AddStringToObject(cJSON_Root, NAME, DEFAULT_NAME); /*添加元素 键值对*/ + cJSON_AddNumberToObject(cJSON_Root, TEMP_NUM, DEFAULT_TEMP_NUM); + cJSON_AddNumberToObject(cJSON_Root, HUM_NUM, DEFAULT_HUM_NUM); + + char* p = cJSON_Print(cJSON_Root); /*p 指向的字符串是json格式的*/ + + p = NULL; + + return cJSON_Root; + +} +uint8_t cJSON_Update(const cJSON * const object,const char * const string,void *d) +{ + cJSON* node = NULL; //json根节点 + node = cJSON_GetObjectItem(object,string); + if(node == NULL) + return 0; + if(cJSON_IsBool(node)) + { + int *b = (int*)d; + + cJSON_GetObjectItem(object,string)->type = *b ? cJSON_True : cJSON_False; + + return 1; + } + else if(cJSON_IsString(node)) + { + cJSON_GetObjectItem(object,string)->valuestring = (char*)d; + + return 1; + } + else if(cJSON_IsNumber(node)) + { + double *num = (double*)d; + + cJSON_GetObjectItem(object,string)->valuedouble = (double)*num; + + return 1; + } + else + return 1; +} + +void Proscess(void* data) +{ + + cJSON *root,*json_name,*json_temp_num,*json_hum_num; + root = cJSON_Parse((char*)data); //解析成json形式 + + json_name = cJSON_GetObjectItem( root , NAME); //获取键值内容 + json_temp_num = cJSON_GetObjectItem( root , TEMP_NUM ); + json_hum_num = cJSON_GetObjectItem( root , HUM_NUM ); + + lw_print("name:%s\n temp_num:%f\n hum_num:%f\n", + json_name->valuestring, + json_temp_num->valuedouble, + json_hum_num->valuedouble); + + cJSON_Delete(root); //释放内存 +} + + + + + + + + diff --git a/APP_Framework/lib/cJSON/cJSON_Process.h b/APP_Framework/lib/cJSON/cJSON_Process.h new file mode 100644 index 000000000..5dcdd3bf5 --- /dev/null +++ b/APP_Framework/lib/cJSON/cJSON_Process.h @@ -0,0 +1,23 @@ +#ifndef _CJSON_PROCESS_H_ +#define _CJSON_PROCESS_H_ +#include "cJSON.h" +#include "stdint.h" + + +#define NAME "name" +#define TEMP_NUM "temp" +#define HUM_NUM "hum" + +#define DEFAULT_NAME "fire" +#define DEFAULT_TEMP_NUM 25.0 +#define DEFAULT_HUM_NUM 50.0 + + +#define UPDATE_SUCCESS 1 +#define UPDATE_FAIL 0 + +cJSON* cJSON_Data_Init(void); +uint8_t cJSON_Update(const cJSON * const object,const char * const string,void * d); +void Proscess(void* data); +#endif +