402 lines
16 KiB
C
402 lines
16 KiB
C
#include "ec20ftp.h"
|
||
#include <stdlib.h>
|
||
#include "syslib.h" //#define STR2(R) STR1(R)
|
||
|
||
/********************************************************************************
|
||
* @file ec20ftp.c
|
||
* @author 江苏九比特信息系统有限公司 Mr.Wang
|
||
* @version V1.0.0
|
||
* @date 11-Dec-2018
|
||
* @brief 提供Quectel模块EC20关于FTP硬件驱动程序
|
||
******************************************************************************
|
||
* @attention
|
||
* 约定基本名词如下:
|
||
* contextID:链路ID
|
||
* EC20模块链路ID范围1~16,每一个链路ID都会对应一个本地IP;。
|
||
* 本驱动强制规定,FTP协议默认只用一个链路ID(即contextID=3)用于ftp链路。
|
||
*******************************************************************************/
|
||
|
||
/*****************************************
|
||
*内部使用的常变量定义
|
||
****************************************/
|
||
uint8_t ftpLocalIp[MAX_IP_LEN] ; //FTP链路本地IPP
|
||
#define FTP_CMDPACK_LEN 256 //FTP命令的最大长度
|
||
|
||
/********************************************************
|
||
ec20模块FTP相关AT指令处理
|
||
*********************************************************/
|
||
enum eFtpCmdNum
|
||
{
|
||
SETCONTEXTID =0, SETACCOUNT =1, SETFILETYPE =2, SETTRANSMODE =3, SETRSTIMEOUT =4,
|
||
FTPLOGIN =5, GETLOGINSTATE =6, SETDIRECTORY =7, GETDIRECTORY =8, LISTFILENAME =9,
|
||
GETFILESIZE =10, DOWNFILE =11, GETHASDOWNSIZE =12, RENAMEFILE =13, FTPLOGOUT = 14
|
||
} ;//枚举ec20模块FTP相关指令枚举
|
||
|
||
volatile EC20_CMD_DATA_s sFtpCmd[15]=
|
||
{
|
||
// cmdNum cmdStr, timeout(100ms), trueStr, trueOffset falseStr revResult rtyNum
|
||
{SETCONTEXTID, "AT+QFTPCFG=\"contextid\",%d\r\n", 10, "OK", -1, "ERROR", TIMEOUT, 2 }, //手册回码等待150S
|
||
{SETACCOUNT, "AT+QFTPCFG=\"account\",\"%s\",\"%s\"\r\n",10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //账户、密码
|
||
{SETFILETYPE, "AT+QFTPCFG=\"filetype\",%d\r\n" , 10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //文件类型
|
||
{SETTRANSMODE, "AT+QFTPCFG=\"transmode\",%d\r\n" , 10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //传输模式
|
||
{SETRSTIMEOUT, "AT+QFTPCFG=\"rsptimeout\",%d\r\n" , 10, "OK" , -1, "ERROR", TIMEOUT, 2 }, //返回超时时间
|
||
|
||
{FTPLOGIN, "AT+QFTPOPEN=\"%s\",%d\r\n", (20*10), "OK", -1, "ERROR", TIMEOUT, 2 }, //FTP登录
|
||
{GETLOGINSTATE, "AT+QFTPSTAT\r\n", 10, "+QFTPSTAT: 0", -1, "ERROR", TIMEOUT, 2 }, //(0:Opening 1:idle 2:Transferring 3:Closing 4:Closed)
|
||
{SETDIRECTORY, "AT+QFTPCWD=\"%s\"\r\n" , 10, "+QFTPCWD: 0,0", -1, "ERROR", TIMEOUT, 2 }, //ftp目录路径
|
||
{GETDIRECTORY, "AT+QFTPPWD\r\n" , 10, "+QFTPPWD: 0", -1, "ERROR", TIMEOUT, 2 }, //传输模式
|
||
{LISTFILENAME, "AT+QFTPNLST=\"%s\"\r\n" , 10, "+QFTPNLST: 0", -1, "ERROR", TIMEOUT, 2 }, //ftp目录路径 列出FTP目录中所有文件的文件名字
|
||
|
||
{GETFILESIZE, "AT+QFTPSIZE=\"%s\"\r\n" , 10, "+QFTPSIZE: 0", -1, "ERROR", TIMEOUT, 2 }, //文件名 获取文件名文件大小:+QFTPSIZE: 0,36048
|
||
{DOWNFILE, "AT+QFTPGET=\"%s\",\"COM:\",%d,%d\r\n" , 40, "\r\nOK\r\n\r\n+QFTPGET: 0", -1, "ERROR", TIMEOUT, 1 }, //文件名+起始偏移量+下载字节数 下载文件
|
||
{GETHASDOWNSIZE, "AT+QFTPLEN\r\n" , 10, "+OK", -1, "ERROR", TIMEOUT, 2 }, //ftp目录路径 列出FTP目录中所有文件的文件名字
|
||
{RENAMEFILE, "AT+QFTPRENAME=\"%s\",\"%s\"\r\n" , 20, "+QFTPRENAME: 0", -1, "ERROR", TIMEOUT, 2 }, //旧文件名+新文件名
|
||
{FTPLOGOUT, "AT+QFTPCLOSE\r\n", (2*10), "+QFTPCLOSE", -1, "ERROR", TIMEOUT, 2 }, //FTP退出登录
|
||
} ; //EC20模块module相关指令的EC20_CMD_DATA_s结构体类型参数
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: static const char *FtpCmdNumToString(enum eFtpCmdNum result)
|
||
* 功能说明: 输出枚举成员名的字符串指针。
|
||
* 入口参数: eFtpCmdNum类型的枚举
|
||
* 出口参数: 为枚举的成员名字符串指针
|
||
**************************************************************************************************/
|
||
static inline const char *FtpCmdNumToString(enum eFtpCmdNum result)
|
||
{
|
||
switch (result)
|
||
{
|
||
ENUM_CHIP_TYPE_CASE(SETCONTEXTID)
|
||
ENUM_CHIP_TYPE_CASE(SETACCOUNT)
|
||
ENUM_CHIP_TYPE_CASE(SETFILETYPE)
|
||
ENUM_CHIP_TYPE_CASE(SETTRANSMODE)
|
||
ENUM_CHIP_TYPE_CASE(SETRSTIMEOUT)
|
||
|
||
ENUM_CHIP_TYPE_CASE(FTPLOGIN)
|
||
ENUM_CHIP_TYPE_CASE(GETLOGINSTATE)
|
||
ENUM_CHIP_TYPE_CASE(SETDIRECTORY)
|
||
ENUM_CHIP_TYPE_CASE(GETDIRECTORY)
|
||
ENUM_CHIP_TYPE_CASE(LISTFILENAME)
|
||
|
||
ENUM_CHIP_TYPE_CASE(GETFILESIZE)
|
||
ENUM_CHIP_TYPE_CASE(DOWNFILE)
|
||
ENUM_CHIP_TYPE_CASE(GETHASDOWNSIZE)
|
||
ENUM_CHIP_TYPE_CASE(RENAMEFILE)
|
||
|
||
ENUM_CHIP_TYPE_CASE(FTPLOGOUT)
|
||
}
|
||
return "无此命令";
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: RunResult Ftp_Config( void )
|
||
* 功能说明: 对FTP相关参数进行配置
|
||
* 入口参数:
|
||
* @param1 *psFtp FtpP_s结构体变量指针
|
||
* 出口参数:
|
||
* @param1 runResult RunResult枚举类型变量,返回函数运行结果
|
||
**************************************************************************************************/
|
||
RunResult Ftp_Config( FtpP_s *psFtp )
|
||
{
|
||
RunResult runResult = TIMEOUT ;
|
||
runResult = EC20_SendFtpCmd(SETCONTEXTID, NULL, psFtp->contextId ) ;
|
||
if( runResult != RUNOK )
|
||
return runResult ;
|
||
runResult = EC20_SendFtpCmd(SETACCOUNT, NULL, psFtp->userName, psFtp->password ) ;
|
||
AppLogPrintf("配置FTP服务器Username:%s, Password:%s .", psFtp->userName, psFtp->password ) ;
|
||
if( runResult != RUNOK )
|
||
return runResult ;
|
||
EC20_SendFtpCmd(SETFILETYPE, NULL, psFtp->eFiletype ) ;
|
||
EC20_SendFtpCmd(SETTRANSMODE, NULL, psFtp->eTransmode ) ;
|
||
EC20_SendFtpCmd(SETRSTIMEOUT, NULL, psFtp->rsptimeout ) ;
|
||
return runResult ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: RunResult Ftp_PDP_Init( FtpP_s *psFtp )
|
||
* 功能说明: 初始化FTP链路
|
||
* 出口参数:
|
||
* @param1 runResult RunResult枚举类型变量,返回函数运行结果
|
||
**************************************************************************************************/
|
||
RunResult Ftp_PDP_Init( FtpP_s *psFtp )
|
||
{
|
||
RunResult runResult = TIMEOUT ;
|
||
uint8_t *ftpLocalIp = portMalloc(MAX_IP_LEN) ;
|
||
runResult = Query_Context( psFtp->contextId, ftpLocalIp ) ; //查询psFtp->contextId是否激活
|
||
if( RUNOK == runResult ) /*psFtp->contextId已激活*/ //去激活->再次激活
|
||
{
|
||
// runResult = Deact_Context(HTTP_CONTEXTID) ;
|
||
// if( RUNOK != runResult ) /*HTTP_CONTEXTID去激活失败*/ //直接返回错误
|
||
// {
|
||
// return RUNERR ;
|
||
// }
|
||
return RUNOK ;
|
||
}
|
||
runResult = ActivePDP(psFtp->contextId, ftpLocalIp) ;
|
||
if( RUNOK == runResult )
|
||
{
|
||
AppLogPrintf("FTP本地IP:%s", ftpLocalIp) ;
|
||
}
|
||
portFree(ftpLocalIp) ;
|
||
return(runResult) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: Ftp_Login( FtpP_s *psFtp )
|
||
* 功能说明: 登录FTP服务器
|
||
* 入口参数:
|
||
* @param1 *psFtp FtpP_s结构体变量指针
|
||
* 出口参数:
|
||
* @param1 runResult RunResult枚举类型变量,返回函数运行结果
|
||
**************************************************************************************************/
|
||
RunResult Ftp_Login(FtpP_s *psFtp)
|
||
{
|
||
RunResult runResult = TIMEOUT ;
|
||
runResult = EC20_SendFtpCmd(FTPLOGIN, NULL, psFtp->ftpServerIP, psFtp->ftpServerPort ) ;
|
||
if( runResult != RUNOK )
|
||
return runResult ;
|
||
|
||
int checkCsTimes = 10 ; //10*2S等待FTP进入idle状态
|
||
while( checkCsTimes-- )
|
||
{
|
||
Wait_For_Nms(30) ;
|
||
runResult = EC20_SendFtpCmd(GETLOGINSTATE, NULL ) ;
|
||
if( RUNOK == runResult )
|
||
{
|
||
if( (sFtpCmd[GETLOGINSTATE].trueOffset > 0)&&
|
||
(ec20AtBuf[sFtpCmd[GETLOGINSTATE].trueOffset+13] == 0x31))
|
||
{
|
||
runResult =RUNOK ;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
runResult =RUNERR ;
|
||
}
|
||
}
|
||
}
|
||
return (runResult) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: Ftp_Logout( FtpP_s *psFtp )
|
||
* 功能说明: 登出FTP服务器
|
||
* 入口参数:
|
||
* @param1 *psFtp FtpP_s结构体变量指针
|
||
* 出口参数:
|
||
* @param1 runResult RunResult枚举类型变量,返回函数运行结果
|
||
**************************************************************************************************/
|
||
RunResult Ftp_Logout(FtpP_s *psFtp)
|
||
{
|
||
RunResult runResult = TIMEOUT ;
|
||
runResult = EC20_SendFtpCmd(FTPLOGOUT, NULL ) ;
|
||
return runResult ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: Ftp_Set_Dir( FtpP_s *psFtp )
|
||
* 功能说明: 设置Ftp操作目录
|
||
* 入口参数:
|
||
* @param1 *psFtp FtpP_s结构体变量指针
|
||
* 出口参数:
|
||
* @param1 runResult RunResult枚举类型变量,返回函数运行结果
|
||
**************************************************************************************************/
|
||
RunResult Ftp_Set_Dir( FtpP_s *psFtp )
|
||
{
|
||
RunResult runResult = TIMEOUT ;
|
||
runResult = EC20_SendFtpCmd(SETDIRECTORY, NULL, psFtp->ftpDirectory ) ; //设置用户目录
|
||
if( runResult != RUNOK )
|
||
return runResult ;
|
||
|
||
runResult = EC20_SendFtpCmd(GETDIRECTORY, NULL ) ;
|
||
if( RUNOK == runResult )
|
||
{
|
||
if( (sFtpCmd[GETDIRECTORY].trueOffset > 0)&&
|
||
(kmp(ec20AtBuf, (const char*)psFtp->ftpDirectory) > 0)) //确认查询到的目录为用户设置的目录
|
||
{
|
||
runResult =RUNOK ;
|
||
}
|
||
else
|
||
{
|
||
runResult =RUNERR ;
|
||
}
|
||
}
|
||
return (runResult) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: RunResult Ftp_Find_File( uint8_t *dir, uint8_t *fileName )
|
||
* 功能说明: 在dir目录中查找名为fileName的文件
|
||
* 入口参数:
|
||
* @param1 *dir 查找的目录
|
||
* @param2 *fileName 所查找的文件文件名
|
||
* 出口参数:
|
||
* @param1 RUNOK 在目录中能找到文件
|
||
* @param2 RUNTIMEOUT 在目录中不能找到文件
|
||
* @param3 RUNERR 列出dir目录中文件名出错
|
||
* 说 明: ec20AtBuf[EC20_ATBUF_LEN] 256字节, 所以当dir中文件名总字节数大于(EC20_ATBUF_LEN-50)时也会出现查找失败。
|
||
**************************************************************************************************/
|
||
RunResult Ftp_Find_File( uint8_t *dir, uint8_t *fileName )
|
||
{
|
||
if( RUNOK != EC20_SendFtpCmd(LISTFILENAME, NULL, dir ))
|
||
return RUNERR ;
|
||
if( kmp(ec20AtBuf, (const char*)fileName) > 0 )
|
||
return RUNOK ;
|
||
else
|
||
return TIMEOUT ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: u32 Ftp_Get_FileSize( uint8_t *fileName )
|
||
* 功能说明: 查询*fileName 文件的大小
|
||
* 入口参数:
|
||
* @param1 *fileName 索要查询文件大小的文件名
|
||
* 出口参数:
|
||
* @param1 u32 返回文件大小(bytes)
|
||
**************************************************************************************************/
|
||
u32 Ftp_Get_FileSize( uint8_t *fileName )
|
||
{
|
||
RunResult runResult = TIMEOUT ;
|
||
u32 fileSize = 0 ;
|
||
uint8_t fileSizeBuf[7] = {0} ;
|
||
runResult = EC20_SendFtpCmd(GETFILESIZE, NULL, fileName ) ; //设置用户目录
|
||
if( runResult != RUNOK )
|
||
return 0 ;
|
||
|
||
CopyValues(fileSizeBuf, (uint8_t*)&ec20AtBuf[ sFtpCmd[GETFILESIZE].trueOffset+13], 0x0D, 6) ;
|
||
fileSize = atoi((const char*)fileSizeBuf) ;
|
||
return (fileSize) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: RunResult Ftp_File_Rename( uint8_t *oldName, uint8_t *newName )
|
||
* 功能说明: 文件重命名
|
||
* 入口参数:
|
||
* @param1 *oldName 旧文件名
|
||
* @param2 *newName 新文件名
|
||
* 出口参数:
|
||
* @param1 runResult RunResult枚举类型数据
|
||
**************************************************************************************************/
|
||
RunResult Ftp_File_Rename( uint8_t *oldName, uint8_t *newName )
|
||
{
|
||
RunResult runResult = TIMEOUT ;
|
||
runResult = EC20_SendFtpCmd(RENAMEFILE, NULL, oldName, newName ) ; //
|
||
return (runResult) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: int Ftp_Down_File( uint8_t *fileName, uint32_t startPos, uint16_t transLen)
|
||
* 功能说明: 从名为fileName的文件startPos位置开始下载transLen长度的数据
|
||
* 入口参数:
|
||
* @param1 *fileName 下载的文件文件名
|
||
* @param2 startPos 开始下载的位置
|
||
* @param3 transLen 下载的字节数
|
||
* 出口参数:
|
||
* @param1 headPos 如果校验正确的话返回"CONNECT"在ec20FtpBuf内的偏移地址, 错误返回-1
|
||
* 注 意:下载文件时,EC20串口模式切换到FTP_MODE,届时接收数据将会存储在ec20FtpBuf缓冲区中
|
||
* ec20FtpBuf数据校验方法是判断CONNECT和+QFTPGET: 0,2048之间的字节数是否为我们请求的字节数,
|
||
* “CONNECT 0x0D 0x0A 数据 0x0D 0x0A OK 0x0D 0x0A 0x0D 0x0A +QFTPGET: 0,2048”
|
||
**************************************************************************************************/
|
||
int Ftp_Down_File( uint8_t *fileName, uint32_t startPos, uint16_t transLen)
|
||
{
|
||
int headPos = -1 ;
|
||
uint8_t revTimes = 0 ;
|
||
ftpDataMode = true ; //EC20串口接收模式切换到FTP模式
|
||
Ec20FtpBufReset() ; //FTP接收缓冲区初始化
|
||
UARTx_SendString(EC20_UART, (uint8_t *)sFtpCmd[DOWNFILE].cmdStr, fileName, startPos, transLen ) ;
|
||
while( revTimes++ < sFtpCmd[DOWNFILE].timeout*2 )
|
||
{
|
||
Wait_For_Nms(15) ;
|
||
headPos = kmp(ec20FtpBuf, "CONNECT")+9 ; //下载的文件数据开始的地方相对用户数据开始处 在ec20FtpBuf中的偏移量
|
||
sFtpCmd[DOWNFILE].trueOffset = kmp(&ec20FtpBuf[transLen+headPos], sFtpCmd[DOWNFILE].trueStr) ; //+QFTPGET: 0,2048在相对用户数据结尾处 在ec20FtpBuf中的偏移量
|
||
if( sFtpCmd[DOWNFILE].trueOffset == 0)
|
||
{
|
||
if( headPos >= 0 ) //CONNECT和+QFTPGET: 0,2048之间的字节数 是否正常
|
||
{
|
||
break ;
|
||
}
|
||
else
|
||
{
|
||
headPos = -1 ;
|
||
break ;
|
||
}
|
||
}
|
||
else if( kmp(ec20FtpBuf, sFtpCmd[DOWNFILE].falseStr) >= 0)
|
||
{
|
||
headPos = -1 ;
|
||
break ;
|
||
}
|
||
}
|
||
ftpDataMode = false ; //EC20串口接收模式切出FTP模式
|
||
return (headPos) ;
|
||
}
|
||
|
||
/**************************************************************************************************
|
||
* 名 称: RunResult EC20_SendFtpCmd( uint8_t cmdNum, char *format,... )
|
||
* 功能说明: MCU串口向EC20发送Tcp相关命令
|
||
* 入口参数:
|
||
* @param1 cmdNum EC20_CMD_DATA_s中cmdNum成员命令编号
|
||
* @param2 char *format,... 可变参变量
|
||
* 出口参数:
|
||
* @param1 status RunResult枚举类型变量,返回函数运行结果
|
||
**************************************************************************************************/
|
||
RunResult EC20_SendFtpCmd( uint8_t cmdNum, char *format,... )
|
||
{
|
||
uint8_t revTimes = 0 ;
|
||
RunResult status = TIMEOUT ;
|
||
uint8_t retryTimes = sFtpCmd[cmdNum].rtyNum ;
|
||
char *cmdPack = NULL ;
|
||
format = sFtpCmd[cmdNum].cmdStr ;
|
||
cmdPack = portMalloc(FTP_CMDPACK_LEN*sizeof(uint8_t)) ;
|
||
if( cmdPack == NULL )
|
||
{
|
||
ErrorLogPrintf("EC20 FtpCmdPack 内存分配失败!") ;
|
||
portFree(cmdPack) ;
|
||
return RUNERR ;
|
||
}
|
||
va_list ap;
|
||
va_start (ap, format);
|
||
int outLen = vsnprintf(cmdPack, FTP_CMDPACK_LEN, (const char*)format, ap); //vsprintf (temp, cmd, ap); //到此为止,所有的参数情况已经汇总到temp了
|
||
if((outLen<=0)||( outLen > FTP_CMDPACK_LEN))
|
||
{
|
||
ErrorLogPrintf("FTP cmdPack 溢出!--增加FTP_CMDPACK_LEN数值。") ;
|
||
status = RUNERR ;
|
||
goto tcpCmdOut ;
|
||
}
|
||
while(retryTimes--)
|
||
{
|
||
Ec20AtBufReset() ;
|
||
revTimes = 0 ;
|
||
UARTx_SendData(EC20_UART, cmdPack, outLen ) ; //DMA发送
|
||
while( revTimes++ < sFtpCmd[cmdNum].timeout )
|
||
{
|
||
Wait_For_Nms(100) ;
|
||
sFtpCmd[cmdNum].trueOffset = kmp(ec20AtBuf, sFtpCmd[cmdNum].trueStr) ;
|
||
if( sFtpCmd[cmdNum].trueOffset >= 0)
|
||
{
|
||
status = RUNOK ;
|
||
goto tcpCmdOut ;
|
||
}
|
||
else if( kmp(ec20AtBuf, sFtpCmd[cmdNum].falseStr) >= 0)
|
||
{
|
||
status = RUNERR ;
|
||
goto tcpCmdOut ;
|
||
}
|
||
}
|
||
Wait_For_Nms( 1000 ) ;
|
||
}
|
||
tcpCmdOut:portFree(cmdPack) ;
|
||
va_end(ap) ;
|
||
DebugLogPrintf("%s %s\r\n[%s]", FtpCmdNumToString((enum eFtpCmdNum)cmdNum), RunResultToString(status), ec20AtBuf ) ;
|
||
return (status) ;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|