//===================================================================
//文件名称：uecom.c（uecom驱动源程序文件）
//===================================================================
#include "uecom.h"    //包含对应驱动头文件，说明在头文件中
#include "includes.h"
//文件级变量
#define AT_reBuf_length 200
static uint_8 AT_reBuf[200];   //存放UE的AT指令的返回结果
static uint_8 AT_reFlag = 0;   //AT指令成功返回的标志,成功时该值为接收到的AT指令返回的数据长度
static uint_8 AT_reLength = 0; //AT指令返回的数据长度
static uint_8 AT_haveCommand=0;//标志当前是否有AT指令发送
static uint_8 frameHead[2] = "V!"; //帧头
static uint_8 frameTail[2] = "S$"; //帧尾
static uint_8 IMSI[16]={0};        //存储初始化后读取到的IMSI号
static uint_8 IMEI[16]={0};        //存储初始化后读取到的IMEI号
static uint_8 locationFlag=0;     //接收到一行有效定位信息标志
static uint_8 locationData[100];     //存放接收到的一行有效定位信息

//static uint_8 Type[20] = "12345678901234567890";
static uint_8 Type[25] = "200-2AE   NB-DX-3616";
static uint_8 uecomVersion[5] = "V6.5";

//======================内部函数声明====================================
uint_8 uecom_sendCmd(uint_8 *cmd,uint_16 maxDelayMs,uint_16 maxTimes);
void uint_to_str(uint_32 ch,char *str);
void frameEncode(uint_8 *imsi,uint_8 *data,uint_16 dlen,
		         uint_8 *frame,uint_16 *framelen);
uint_8 uecom_internal_getIMEI(uint_8 *dest);
uint_8 uecom_internal_getIMSI(uint_8 *dest);
uint_8 uecom_recv(uint_8 ch,uint_16 *dataLen, uint_8 *data);
uint_8 uecom_recvLocation(uint_8 ch, uint_8 *data);
uint_8 uecom_transparentRecv(uint_8 ch,uint_16 *dataLen, uint_8 *data);

//======================内部函数声明结束===================================

//========================变量声明======================================
//函数中使用到的AT指令，【20180815】

#define AT        "AT\r\n"             //测试与通信模组通讯的指令
#define IPR       "AT+IPR=115200\r\n"  //将模块波特率设置为115200
#define ATE_0     "ATE0\r\n" //关闭回显（发送该命令之后，UE模块不会把原来的命令返回）
#define GSN       "AT+GSN\r\n"          //获取设备的IMEI号指令
#define CIMI      "AT+CIMI\r\n"         //获取设备的IMSI号指令

#define CFUN      "AT+CFUN=1\r\n"       //设置电话功能
#define COPS      "AT+COPS=1,2,\"46011\"\r\n"//设置运营商,46011表示的是电信的
#define MSPCHSC   "AT*MSPCHSC=1\r\n"         //设置NB支持的算法，旧算法
#define CPSMS     "AT+CPSMS=0\r\n"           //关闭 PSM 功能
#define EGACT_0   "AT+EGACT=0,1,\"ctnb\"\r\n"//去激活PDP,其中APN是电信的
#define EGACT_1   "AT+EGACT=1,1,\"ctnb\"\r\n"//激活PDP,其中APN是电信的
#define CSQ       "AT+CSQ\r\n"               //获取信号强度

#define ESOC      "AT+ESOC=1,1,1\r\n"        //创建TCP套接字
#define ESOSETRPT "AT+ESOSETRPT=1\r\n"   //设置接收数据的回显形式为“原始数据”
#define ESOCL     "AT+ESOCL=0\r\n"       //关闭套接字
#define MENGINFO  "AT*MENGINFO=0\r\n"    //获取当前小区信息

#define ZGMODE_1 "AT+ZGMODE=1\r\n"    //设置GPS定位模式（AGPS模式）
#define ZGNMEA_2 "AT+ZGNMEA=2\r\n"    //设置GPS数据NMEA上报格式模式（RMC格式）
#define ZGRUN_2 "AT+ZGRUN=2\r\n"      //开启GPS服务（追踪获取数据）

//AT命令是否执行成功的标志
#define OK        "OK"
#define ERROR     "ERROR"
//========================变量声明结束====================================

//=====================================================================
//函数名称：uecom_power
//函数返回：无
//参数说明：state:通信模组电源控制命令。可取值为（使用宏常数）：
//         UECOM_OFF：关闭通信模组电源；
//         UECOM_ON：打开通信模组电源；
//         UECOM_REBOOT：重启通信模组（先关闭，延时，再开启）。
//功能概要：控制通信模块供电状态
//内部调用：gpio_init，delay_ms
//修改日期：【20180718】,LXD
//=====================================================================
void uecom_power(uint_8 state)    //控制通信模块供电状态
{
    switch(state)
    {
    case UECOM_OFF:
        gpio_init(POWER_CONTROL,1,0);break;
    case UECOM_ON:
        gpio_init(POWER_CONTROL,1,1);break;
    case UECOM_REBOOT:
        gpio_init(POWER_CONTROL,1,0);        //关闭
        Delay_ms(2000);                //延时2秒
        gpio_init(POWER_CONTROL,1,1);break;  //打开
    }
}

//====================================================================
//函数名称：uecom_init
//函数返回： 0：成功；1：与UE模块串口通讯失败；2：获取SIM卡的IMSI号失败；
//参数说明： 无
//功能概要：uecom模块初始化。（1）建立与通讯模组的串口通讯；（2）获取SIM卡的IMSI号。
//修改日期：【20180718】,LXD
//====================================================================
uint_8 uecom_init(void)
{
    //（1）变量声明和赋初值
    static uint_8 i=1;
    uint_8 ret_val ;
    uint_8 flag;
    //（2）使用串口与通信模组通信，首先初始化串口
    uart_init(UART_UE, 115200);  //初始化UE模块串口，波特率为115200
    uart_enable_re_int(UART_UE); //打开UE模块串口中断
    //（3）测试与GPRS模块的通讯
    if(uecom_sendCmd((uint_8 *)AT,100,2))
    {
    	uecom_power(UECOM_REBOOT);   //给UE模块重新供电
    	Delay_ms(8000);
    }
	if(uecom_sendCmd((uint_8 *)AT,100,3))
	    goto uecom_init_err1;     //UE模块通讯失败
    //（4）关闭回显（发送AT命令之后，UE模块不会把原来的命令返回）
    if(uecom_sendCmd((uint_8 *)ATE_0,200,3))
        goto uecom_init_err1;    //关闭回显失败
    //（5）查询模块的IMEI,并将IMEI号放在dest数组的前15个字节
    if(uecom_internal_getIMEI(IMEI))
        goto uecom_init_err1;    //IMEI查询失败
    //（6）查询模块的IMSI,并将IMSI号放在dest数组的15-29下标的空间中
    if(uecom_internal_getIMSI(IMSI))
        goto uecom_init_err2;    //IMSI查询失败
    ret_val = 0;
    goto uecom_init_exit;
     //错误退出
uecom_init_err1:
    ret_val = 1;                 //与UE模块串口通讯失败
    goto uecom_init_exit;
uecom_init_err2:
    ret_val = 2;                 //获取SIM卡的IMSI号失败
    goto uecom_init_exit;
    //退出处
uecom_init_exit:
    return ret_val;
}

//=====================================================================
//函数名称： uecom_linkBase
//函数返回： 0：成功建立与铁塔建立连接；1：连接不上铁塔；
//参数说明：无
//功能概要：与网络运营商的基站（铁塔）建立连接
//修改日期：【20180718】,LXD
//=====================================================================
uint_8 uecom_linkBase(void)
{
	//（1）变量声明、赋初值
	uint_8 ret=1;
	//（2）设置电话功能为全功能
	if(uecom_sendCmd((uint_8 *)CFUN,1500,3))
		goto uecom_linkBase_exit;      //设置电话功能为全功能失败
	//（3）设置网络运营商
	uecom_sendCmd((uint_8 *)COPS,2000,2);
	//（4）设置NB支持的扰码算法
	uecom_sendCmd((uint_8 *)MSPCHSC,500,2);
	//（5）关闭 PSM 功能（因为不涉及功耗测试）
	if(uecom_sendCmd((uint_8 *)CPSMS,200,2))
		goto uecom_linkBase_exit;      //关闭 PSM 功能失败
	//（6）激活PDP
	if(uecom_sendCmd((uint_8 *)EGACT_1,10000,1))
	{
		uecom_sendCmd((uint_8 *)EGACT_0,10000,1);
		Delay_ms(5000);
		if(uecom_sendCmd((uint_8 *)EGACT_1,10000,2))
			goto uecom_linkBase_exit;  //激活PDP失败
	}
	//至此，没有失败退出，成功！
	ret = 0;
uecom_linkBase_exit:
	    return ret;
}

//=====================================================================
//函数名称：uecom_linkCS
//函数返回： 0：成功建立TCP连接；1：建立TCP连接失败
//参数说明： IP:待连接服务器的IP地址； port:待连接服务器的端口号
//功能概要：与指定的服务器和端口建立TCP连接。
//修改日期：【20180718】,LXD
//=====================================================================
uint_8 uecom_linkCS(uint_8 *ip,uint_8* port)
{
    //（1）变量声明和赋初值
    uint_8 i;
    uint_8 flag;
    uint_8 ret_val=1;
    uint_8 ESOCON[50] = "";    //用存放建立TCP连接的指令
    uint_8 loc;
    //（2）组建建立TCP连接的指令
    strcat((char *)ESOCON,"AT+ESOCON=0,");
    loc = strlen(ESOCON);
    for(i=0; i<5; i++)//添加端口号（最多5位）
    {
        if(port[i] == 0)
        {
            break;
        }
        ESOCON[loc+i] = port[i];
    }
    strcat((char *)ESOCON,",\"");
    loc = strlen(ESOCON);
    for(i=0; i<15; i++)//添加端IP（最多15位）
    {
        if(ip[i] == 0)
        {
           break;
        }
        ESOCON[loc+i] = ip[i];
    }
    strcat((char *)ESOCON,"\"\r\n");
    //（3）创建TCP套接字
    uecom_sendCmd(ESOCL,4000,1);    //先关闭一下套接字（防止通信模组未断电已经建立套接字）
    if(uecom_sendCmd(ESOC,4000,1))
    {
    	uecom_sendCmd(ESOCL,4000,1);    //关闭套接字
    	if(uecom_sendCmd(ESOC,1000,1))
    	{
    		goto uecom_linkCS_exit;
    	}
    }
    //（4）建立TCP连接
    if(uecom_sendCmd(ESOCON,4000,2))
    {
		goto uecom_linkCS_exit;
    }
    //（5）设置接收数据的回显形式为“原始数据”
    if(uecom_sendCmd(ESOSETRPT,4000,2))
    {
		goto uecom_linkCS_exit;
    }
    ret_val = 0;
    //退出处
uecom_linkCS_exit:
    return ret_val;
}

//=====================================================================
//函数名称：uecom_send
//函数返回：  0：发送成功；1：开启发送模式失败；2：数据发送失败
//参数说明： data:待发送数据缓存区，传入参数
//		           length:待发送数据的长度
//功能概要：将数据通过已经建立的TCP通道发送出去。最多500个字节。
//修改日期：【20180718】,LXD
//=====================================================================
uint_8 uecom_send(uint_16 length, uint_8 *data)
{
    //（1）变量声明，赋初值
    uint_8 ret_val;
    char dataLen[5]="";        //存放帧长(字符串格式)
    char ESOSENDRAW[24]= "";   //存放发开启发送数据的指令
    uint_8 frame[500];         //存放数据帧
    uint_16 frameLen;          //存放帧长
    frameEncode(IMSI,data, length,frame,&frameLen);//组帧
    uint_to_str(frameLen,dataLen);     //将待发送数据的长度转换为字符串格式
    //（2）开启发送模式
    strcat(ESOSENDRAW,"AT+ESOSENDRAW=0,");
    strcat(ESOSENDRAW, dataLen);
    strcat(ESOSENDRAW, "\r\n");
    uart_send_string(UART_UE,ESOSENDRAW);
    //（3）延时等待开启发送模式
	if(uecom_sendCmd("wait",2000,1))
	{
		ret_val = 1;
		goto uecom_send_exit;      //开启发送模式失败
	}
    //（4）开始发送数据
    uart_sendN(UART_UE ,frameLen,frame);
    //（5）判断数据是否发送成功
    if(uecom_sendCmd("SEND",2000,1))
    {
    	ret_val = 2;
    	goto uecom_send_exit;         //数据发送失败
    }
    //至此数据发送成功
    ret_val = 0;
    uecom_send_exit:
    return ret_val;
}


//=====================================================================
//函数名称：uecom_transparentSend
//函数返回：  0：发送成功；1：开启发送模式失败；2：数据发送失败
//参数说明： data:待发送数据缓存区，传入参数
//		           length:待发送数据的长度
//功能概要：透明发送，将数据通过已经建立的TCP通道发送出去。最多500个字节。
//修改日期：【20180922】,LXD
//=====================================================================
uint_8 uecom_transparentSend(uint_16 length, uint_8 *data)
{
    //（1）变量声明，赋初值
    uint_8 ret_val;
    char dataLen[5]="";        //存放帧长(字符串格式)
    char ESOSENDRAW[24]= "";   //存放发开启发送数据的指令
    uint_8 frame[500];         //存放数据帧
    uint_16 frameLen;          //存放帧长
//    frameEncode(IMSI,data, length,frame,&frameLen);//组帧
    uint_to_str(length,dataLen);
    //（2）开启发送模式
    strcat(ESOSENDRAW,"AT+ESOSENDRAW=0,");
    strcat(ESOSENDRAW, dataLen);
    strcat(ESOSENDRAW, "\r\n");
    uart_send_string(UART_UE,ESOSENDRAW);
    //（3）延时等待开启发送模式
	if(uecom_sendCmd("wait",2000,1))
	{
		ret_val = 1;
		goto uecom_send_exit;      //开启发送模式失败
	}
    //（4）开始发送数据
    uart_sendN(UART_UE ,length,data);
    //（5）判断数据是否发送成功
    if(uecom_sendCmd("SEND",2000,1))
    {
    	ret_val = 2;
    	goto uecom_send_exit;         //数据发送失败
    }
    //至此数据发送成功
    ret_val = 0;
    uecom_send_exit:
    return ret_val;
}


//====================================================================
//函数名称：uecom_interrupt
//函数返回：无
//参数说明：ch：串口中断接收到的数据；length：接收到的网络数据长度；
//                   recvData：存储接收到的网络数据
//功能概要：本函数需要放在串口中断中。并需要传入串口中断接收到的数据。
//  本构件的所有功能实现均依赖该api。本api实现的功能：
//  （1）接收网络发送来的数据;（2）构件内部使用本api与模块进行数据交互
//修改日期：【20180718】,LXD
//=====================================================================
void uecom_interrupt(uint_8 ch,uint_16 *length,uint_8 recvData[])
{
	//保存数据到缓冲区中
	if(AT_haveCommand)
	{
		AT_reBuf[AT_reLength] = ch;    //存储接收到的数据到缓冲区AT_reBuf中
		AT_reLength = (AT_reLength+1)%AT_reBuf_length;//接收到的数据下标递增
		AT_reBuf[AT_reLength] = 0;
	}
//	else
	uecom_recv(ch,length,recvData); //处理来自服务器的数据
	if(locationFlag==0)
	{
		uecom_recvLocation(ch,locationData);//处理定位信息
	}
}


//====================================================================
//函数名称：uecom_transparentInterrupt
//函数返回：无
//参数说明：ch：串口中断接收到的数据；length：接收到的网络数据长度；
//                   recvData：存储接收到的网络数据
//功能概要：透传的中断处理函数，本函数需要放在串口中断中。并需要传入串口中断接收到的数据。
//  本构件的所有功能实现均依赖该api。本api实现的功能：
//  （1）接收网络发送来的数据;（2）构件内部使用本api与模块进行数据交互
//修改日期：【20180922】,LXD
//=====================================================================
void uecom_transparentInterrupt(uint_8 ch,uint_16 *length,
		uint_8 recvData[])
{
	//保存数据到缓冲区中
	if(AT_haveCommand)
	{
		AT_reBuf[AT_reLength] = ch;    //存储接收到的数据到缓冲区AT_reBuf中
		AT_reLength = (AT_reLength+1)%AT_reBuf_length;//接收到的数据下标递增
		AT_reBuf[AT_reLength] = 0;
	}
	uecom_transparentRecv(ch,length,recvData); //处理来自服务器的数据
	if(locationFlag==0)
	{
		uecom_recvLocation(ch,locationData);//处理定位信息
	}
}
//=====================================================================
//函数名称：uecom_ gnssSwitch
//函数返回：0：操作GNSS成功；1：操作GNSS失败
//参数说明：state：设置GNSS的开关状态。1：热启动；2：温启动；3：冷启动；
//          建议默认使用冷启动。
//功能概要：设置GNSS的状态。开启或关闭GNSS，并设定开启方式。
//修改日期：【20180902】；
//=====================================================================
uint_8 uecom_gnssSwitch (uint_8 state)
{
	//（1）定义使用到的变量
	uint_8  ret;
	ret = 1;    //默认返回失败
	//（2）根据传入值操作GNSS
	switch(state)
	{
	case 1:
        if(uecom_sendCmd(ZGMODE_1,1000,2))//设置GPS定位模式（AGPS模式）
        	goto uecom_gnssSwitch_exit;
    	if(uecom_sendCmd(ZGNMEA_2,1000,2))//设置GPS数据NMEA上报格式模式（RMC格式）
    		goto uecom_gnssSwitch_exit;
		if(uecom_sendCmd(ZGRUN_2,1000,2))//开启循环获取定位信息
			goto uecom_gnssSwitch_exit;
        break;
	case 2:
		if(uecom_sendCmd(ZGMODE_1,1000,2))//设置GPS定位模式（AGPS模式）
			goto uecom_gnssSwitch_exit;
		if(uecom_sendCmd(ZGNMEA_2,1000,2))//设置GPS数据NMEA上报格式模式（RMC格式）
			goto uecom_gnssSwitch_exit;
		if(uecom_sendCmd(ZGRUN_2,1000,2))//开启循环获取定位信息
			goto uecom_gnssSwitch_exit;
		break;
	case 3:
		if(uecom_sendCmd(ZGMODE_1,1000,2))//设置GPS定位模式（AGPS模式）
			goto uecom_gnssSwitch_exit;
		if(uecom_sendCmd(ZGNMEA_2,1000,2))//设置GPS数据NMEA上报格式模式（RMC格式）
			goto uecom_gnssSwitch_exit;
		if(uecom_sendCmd(ZGRUN_2,1000,2))//开启循环获取定位信息
			goto uecom_gnssSwitch_exit;
		break;
	default:
		break;
	}
	//至此，本函数运行完成
	ret = 0;
	uecom_gnssSwitch_exit:
	return ret;
}


//====================================================================
//函数名称：uecom_ gnssGetInfo
//函数返回：0：获取定位信息成功；1：没有获得定位信息
//参数说明：data：存储获得的GNSS相关信息。采用结构体的方式，共包含4个成员：
//       time（15个字节的uint_8数组）,例如:“20180706155132”表示2018年7月6日15:51:32
//       latitude（double类型），纬度信息；longitude（double），经度信息；
//       speed（double类型），速度，单位为：米每秒。
//       attitude（double类型），海拔高度，单位为：米
//功能概要：获得与GNSS定位相关的信息。
//修改日期：【20180902】
//=====================================================================
uint_8 uecom_gnssGetInfo (UecomGnssInfo *data)   // UecomGnssInfo为存储gnss信息的结构体数据
{
	//（1）定义本函数使用的变量
	uint_8 *p,i,j,ret,index;
	double a,b,c,d;
	//（2）判断是否获取到有效的定位数据
	ret = 1;        //设置默认返回1
	if(locationFlag==0)
		goto uecom_gnssGetInfo_exit;
	//（3）从通信模组返回数据中解析出UTC时间
	//（3.1）提取日期
	p = strstr(locationData,",");   //获得字符','第一次所在的位置
	j=0;                        //用来标记逗号
	while(j<9)                  //定位到第9个逗号
	{
		if(*p==',')
			j++;
		p++;
	}
	data->time[0]='2';//填充数字
	data->time[1]='0';
	data->time[6]=*p;          //提取日期
	data->time[7]=*(++p);
	data->time[4]=*(++p);
	data->time[5]=*(++p);
	data->time[2]=*(++p);
	data->time[3]=*(++p);
	//（4.2）提取时间
	p = strstr(locationData,",");   //获得字符','第一次所在的位置
	i=8;                        //用来标记读到的数据下标
	while(i<14)                 //提取时间
	{
		p++;
		data->time[i]=*p;
		i++;
	}
	data->time[i]=0;                   //添加空操作符组成字符串
	//至此，获取UTC时间成功
    //（4）提取位置信息
	//（4.1）提取出维度信息
	p = strstr(locationData,",");   //获得字符','第一次所在的位置
	j=0;                        //用来标记逗号
	while(j<3)                  //定位到第3个逗号
	{
		if(*p==',')
			j++;
		p++;
	}
	a=((*p)-'0')*10.0+((*(++p))-'0');
	b = ((*(++p))-'0')*10.0 + ((*(++p))-'0');
	++p;
	++p; //跳过'.'
	c = 0.1;
	b = b + ((*p) - '0')*c;
	while((*(++p))!=',')
	{
		c *= 0.1;
		b = b + ((*p) - '0')*c;
	}
	++p;    //跳过','
	if((*p) == 'N')
		d = 1;
	else
		d = -1;
	data->latitude = (a + b/60.0)*d;
	//（4.2）提取出经度信息
	++p;
	++p;   //跳过逗号，跳到经度信息位置
	a=((*p)-'0')*100.0+((*(++p))-'0')*10.0+((*(++p))-'0');
	b = ((*(++p))-'0')*10.0 + ((*(++p))-'0');
	++p;
	++p;   //跳过'.'
	c = 0.1;
	b = b + ((*p) - '0')*c;
	while((*(++p))!=',')
	{
		c *= 0.1;
		b = b + ((*p) - '0')*c;
	}
	++p;    //跳过','
	if((*p) == 'E')
		d = 1;
	else
		d = -1;
	data->longitude = (a + b/60.0)*d;
	//（5）从通信模组返回数据中解析出速度信息，并存入传入的结构体
	if(strstr(locationData,"E,") != NULL)
		 p = strstr(locationData,"E,");
	else
		 p = strstr(locationData,"W,");
	p++;
	p++;//跳到对地速度处
	a = 0;
	while((*p)!='.')
	{
		a = a*10 + (*p - '0');
		p++;
	}
	p++;//跳过点
	b = 0.1;
	a = a + (*p - '0')*b;
	p++;
	while((*p)!=',')
	{
		b *= 0.1;
		a = a + (*p - '0')*b;
		p++;
	}
	data->speed = a*0.5144444;
	//（6）从通信模组返回数据中解析出海拔信息，并存入传入的结构体
	data->attitude = 0;//无海拔信息
	//至此，数据解析完成
	locationFlag=0;    //清空接收到一行有效定位信息标志
    ret = 0;
    uecom_gnssGetInfo_exit:
    return ret;
}




//====================================================================
//函数名称：uecom_baseInfo
//函数返回：0：获取基站信息成功；1：获取信号强度失败；
//参数说明：retData：存储返回的信息,最少分配20个字节。
//          信息组成：信号强度(1个字节)+基站号（19个字节）
//功能概要：获取与基站相关的信息：信号强度和基站号
//修改日期：【20180815】,WB
//=====================================================================
uint_8 uecom_baseInfo (uint_8 retData [20])
{
	//（1）变量声明和赋初值
	uint_8 i,j,*p,ret;
	//（2）获取信号强度，最多获取三次，间隔1秒
	ret = 1;           //设置错误返回为1
	for(j=0;j<3;j++)
	{
		//（2.1）向模组发送获取信号强度的指令
		if( uecom_sendCmd((uint_8 *)CSQ,500,3))
			goto uecom_baseInfo_exit;
		//（2.2）从接收到的数据中解析出信号强度，转为uint_8格式，并赋给*signalPower
		i = strstr(AT_reBuf,"+CSQ: ")-(char*)AT_reBuf;
		i+=5;
		retData[0]= 0;
		while(AT_reBuf[i]!=','&&i<AT_reLength)
		{
			if(AT_reBuf[i]>='0' && AT_reBuf[i]<='9')
				retData[0] = retData[0]*10 + (AT_reBuf[i]-'0');
			i++;
		}
		if(retData[0]!=0)
	    {
			retData[0] = (uint_16)100*(retData[0])/31;
			break;
		}
		Delay_ms(1000);
	}
	if(j==3)goto uecom_baseInfo_exit;  //若3次均失败，则认为获取信号强度失败
    //（3）获得基站号
	ret = 2;           //设置错误返回为2
	//（3.1）向模组发送获取基站号的指令
	if(uecom_sendCmd(MENGINFO,500,3))
		goto uecom_baseInfo_exit;
	//（3.2）从接收到的数据中解析出基站号，并存入retData数组中
	p = strstr(AT_reBuf,"*MENGINFOSC:");   //获得字符':'所在的位置，其后为所需的数据。
	j = 1;
	i = 0;
	strncpy(retData+1,"460,11,",7);
	j+=7;
	while(i<3)//跳过到第三个逗号
	{
		if(*p == ',')
			i++;
		p++;
	}
	++p;//跳到cell ID处
	while((*p)!='\"')//提取cell ID
	{
		retData[j++] = *p;
		p++;
	}
	retData[j++] =',';
	while(i<9)//跳过到第九个逗号
	{
		if(*p == ',')
			i++;
		p++;
	}
	++p;//跳到TAC处
	while((*p)!='\"')//提取TAC
	{
		retData[j++] = *p;
		p++;
	}
	if(j<=0)  //未能成功解析到数据
		goto uecom_baseInfo_exit;   //退出函数
	//最后一个逗号删除，并设置为字符串结束符
	retData[j]=0;
//	retData[1]=0;
    //（4）运行至此，说明执行成功
    ret = 0;          //返回值设为0
    uecom_baseInfo_exit:
    return ret;
}

//====================================================================
//函数名称：uecom_ modelInfo
//函数返回：0：获取模组信息成功；1：获取模组信息失败
//参数说明：retData：存储返回的信息,最少分配40个字节。
//          信息组成：IMEI (20个字节)+IMSI（20个字节）
//功能概要：获得需要与模块相关的信息，包括：IMEI号，IMSI号
//修改日期：【20180718】,LXD
//=====================================================================
uint_8 uecom_modelInfo (uint_8 retData[40])
{
	int i;
	//（1）从本构件的全局变量中取出IMEI并赋值
	for(i=0;i<15;i++)
		retData[i]=IMEI[i];
	retData[i] = 0;    //结束符
	//（2）从本构件的全局变量中取出IMSI并赋值
	for(i=20;i<35;i++)
		retData[i]=IMSI[i-20];
	retData[i] = 0;   //结束符
	return 0;
}

//======================================================================
//函数名称：uecom_typeGet
//函数返回：无
//参数说明：type:金葫芦型号
//功能概要：获取金葫芦型号
//======================================================================
uint_8 uecom_typeGet(uint_8 *type)
{
	memcpy(type,Type,sizeof(Type));
}

//======================================================================
//函数名称：uecom_biosVerGet
//函数返回：无
//参数说明：biosVer:金葫芦版本（通信模组型号）
//功能概要：获取金葫芦版本
//======================================================================
void uecom_version(uint_8 *version)
{
	memcpy(version,uecomVersion,sizeof(uecomVersion));
}

//====================================================================
//函数名称：http_get
//函数返回：0：获得get请求成功；1：初始化http失败；2：传递url参数失败；3：设置网络失败；
//                4：开启网络失败；5：建立连接失败；6：发送请求失败；7：获得返回失败；
//参数说明：ip:目标服务器地址；port :目标地址；url:get请求的内容。result:get请求返回的结果，
//                数组长度由预计返回的长度（用户应已知返回内容）*1.5来决定。
//功能概要：发起http的get请求，并将返回结果存储在result中
//=====================================================================
uint_8 uecom_httpGet (uint_8 ip[],uint_8 port[],uint_8 url[],
		uint_8 result[])
{
	return 1;
}



//------以下为本构件调用的内部函数--------------------------------------------
//====================================================================
//函数名称：uecom_sendCmd
//函数返回：0：指令发送成功;1：指令发送失败
//参数说明：cmd：需要发送的AT指令的首地址或者"wait"或者"SEND";
//        个字节;maxDelayMs:每次命令最多等待时间;maxTimes:最多发送命令次数。
//注："wait"判断是否可以发送数据（即出现‘CONNECT’）；"SEND"用于判断发送数据是否成功；
//功能概要：发送AT指令并获取返回信息。
//修改日期：【2018-06-30】,LXD
//====================================================================
uint_8 uecom_sendCmd(uint_8 *cmd,uint_16 maxDelayMs,uint_16 maxTimes)
{
	//中断方式实现
    //（1）变量声明和赋初值
	uint_8 ret;
	uint_16 i,j,count;
	uint_8 wait[30]="OK";       //存放发送AT指令后所含有的字符串，默认为"OK"
	AT_reLength = 0;
	AT_reBuf[0]=0;//清空字符串（当在接收中断里再接收一个字节时，在其后的位置赋值0）
	if(strcmp(cmd,"wait")==0)//用于判断发送完开启发送数据指令“AT+ESOSENDRAW”后，
		strcpy(wait,"CONNECT"); //是否收到CONNECT，从而去发送数据
	AT_haveCommand=1;  //置位AT命令标志
//	AT_reLength = 0;
//	AT_reBuf[0]=0;//清空字符串（当在接收中断里再接收一个字节时，在其后的位置赋值0）
	Delay_ms(10);//防止串口AT命令发送过于频繁
	j = maxDelayMs/100;
	uart_enable_re_int(UART_UE);//开启中断
	for(count=0;count<maxTimes;count++)
	{
		//如果是AT指令
		if(strcmp(cmd,"SEND") && strcmp(cmd,"wait"))
		{
			AT_reLength = 0;
			AT_reBuf[0]=0;//清空字符串（当在接收中断里再接收一个字节时，在其后的位置赋值0）
//			uart_send_string(UARTA,cmd); //通过串口发送AT指令
			uart_send_string(UART_UE,cmd); //通过串口发送AT指令
		}
		//等待maxDelayMs毫秒
		for(i=0; i<j;i++)
		{
	        Delay_ms(100);
	        if(strstr(AT_reBuf,wait))
	        {
	        	ret = 0;
	        	goto uecom_sendCmd_exit;
	        }
	        if(strstr(AT_reBuf,ERROR))
	        {
	        	Delay_ms(200);//缓冲100毫秒
	        	break;
	        }
		}
		//至此，说明未接收到正确返回。
    	j*=2;//若失败，下一次发送指令时，等待时间加倍
	}
	ret = 1;
	uecom_sendCmd_exit:
	AT_haveCommand=0;
	return ret;
}
//==================================================================
//函数名称：uecom_internal_getIMEI
//函数返回： 0：获取IMEI成功；1：获取IMEI号失败；2：解析IMEI号失败
//参数说明：dest：存放返回的IMEI号，15位
//功能概要：获取设备IMEI号
//内部调用：uecom_sendCmd
//修改日期：【2018-06-30】,LXD
//==================================================================
uint_8 uecom_internal_getIMEI(uint_8 *dest)
{
    //（1）变量声明和赋初值
    uint_8 ret_val;
    uint_8 *p,*start,*end;
    //（2）获取IMEI号
    dest[15] = 0;        //字符串的结尾符
    if(uecom_sendCmd((uint_8 *)GSN,200,4))
    {
		uecom_sendCmd((uint_8 *)AT,100,4);
		if(uecom_sendCmd((uint_8 *)GSN,200,5))
		{
			ret_val = 1;
			goto uecom_internal_getIMEI_exit;      //获取IMEI号失败
		}
    }

    //strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。
    //如果是，则该函数返回str2在str1中首次出现的地址；否则，返回NULL。

    //（3）将IMEI号存储在dest数组中
    p = strstr(AT_reBuf,"OK");
    while(p>=AT_reBuf&&(*p<'0'||*p>'9'))p--;    //查找到最后一个数字所在的位置
    end=p;
    while(p>=AT_reBuf && *p>='0' && *p<='9')p--;//查找到第一个数字所在的位置
    start=p+1;
    if(start>end)
    {
        ret_val = 2;
        goto uecom_internal_getIMEI_exit;      //返回的数据中不含IMEI号
    }

    //void *memcpy(void *dest, const void *src, size_t n);
    //从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

    memcpy(dest,start,end-start+1);//拷贝读取到的数据
    dest[end-start+1]=0;//结束符
    ret_val = 0;
    //退出处
uecom_internal_getIMEI_exit:
    return ret_val;
}

//==================================================================
//函数名称：uecom_internal_getIMSI
//函数返回：0：获取IMSI成功；1：获取IMSI失败
//参数说明：dest：存放返回的IMSI号，15位
//功能概要：获取设备IMSI号
//内部调用：uecom_sendCmd
//修改日期：【2018-06-30】,LXD
//==================================================================
uint_8 uecom_internal_getIMSI(uint_8 *dest)
{
    //（1）变量声明和赋初值
    uint_8 ret_val;
    uint_8 i,k;
    uint_8 *p,*start,*end;
    dest[14] = 0;
    //（2）获取IMSI号
	if(uecom_sendCmd((uint_8 *)CIMI,6000,2))
	{
		uecom_sendCmd((uint_8 *)AT,100,4);
		if(uecom_sendCmd((uint_8 *)CIMI,6000,1))
		{
			ret_val = 1;
			goto uecom_internal_getIMSI_exit;     //获取IMSI号失败
		}
	}

	//strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。
	//如果是，则该函数返回str2在str1中首次出现的地址；否则，返回NULL。

    //（3）将IMSI号存储在dest数组中
    p = strstr(AT_reBuf,"OK");
    while(p>=AT_reBuf&&(*p<'0'||*p>'9'))p--;    //查找到最后一个数字所在的位置
    end=p;
    while(p>=AT_reBuf && *p>='0' && *p<='9')p--;//查找到第一个数字所在的位置
    start=p+1;
    if(start>end)
    {
        ret_val = 2;
        goto uecom_internal_getIMSI_exit;       //返回的数据中不含IMSI号
    }

    //void *memcpy(void *dest, const void *src, size_t n);
    //从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中

    memcpy(dest,start,end-start+1);             //拷贝读取到的数据
    dest[end-start+1]=0;                        //结束符
    ret_val = 0;
    //退出处
uecom_internal_getIMSI_exit:
    return ret_val;
}

//====================================================================
//函数名称：uint_to_str
//函数返回：无
//参数说明：ch:待转换的正整数        *str：存放转换后的字符串
//功能概要：将无符号整数转换为字符串
//====================================================================
void uint_to_str(uint_32 ch,char *str)
{
    int i,j,sign;
    char s[10];
    if((sign=ch)<0)//记录符号
        ch=-ch;//使n成为正数
    i=0;
    do{
           s[i++]=ch%10+'0';//取下一个数字
    }
    while ((ch/=10)>0);//删除该数字
    if(sign<0)
      s[i++]='-';
    for(j=i-1;j>=0;j--)//生成的数字是逆序的，所以要逆序输出
           str[i-1-j]=s[j];
    str[i]=0;
}

//====================================================================
//函数名称：frameEncode
//功能概要：实现待发送数据的组帧,将待发送数据加上帧头、帧长、帧尾以及校验信息
//       frame=帧头+IMSI+data长度+data+CRC校验码+帧尾
//参数说明：imsi：发送数据的设备的IMSI号
//       data:待组帧的数据头指针
//       dlen:待组帧的数据长度
//       frame:组帧之后的待发送数据帧
//       framelen:组帧后的数据长度
//函数返回：无
//====================================================================
void frameEncode(uint_8 *imsi,uint_8 *data,uint_16 dlen,
                 uint_8 *frame,uint_16 *framelen)
{
    uint_16 crc,len;

    //组帧操作
    //帧头
    frame[0] = frameHead[0];
    frame[1] = frameHead[1];
    len = 2;
    //IMSI号
    strncpy(frame+2,imsi,15);
    len += 15;
    //data长度
    frame[len++] = dlen>>8;
    frame[len++] = dlen;
    //data
    memcpy(frame+19,data,dlen);
    len += dlen;
    //CRC校验码
    crc = crc16(frame+2,dlen+17);//对IMSI号、data长度和data进行CRC校验
    frame[len++] = crc>>8;
    frame[len++] = crc;
    //帧尾
    frame[len++] = frameTail[0];
    frame[len++] = frameTail[1];
    frame[len] = 0;
    *framelen = len;
}

//====================================================================
//函数名称：uecom_recv
//函数返回：0：接收到了通过TCP/UDP发来的数据；1：正在解帧；2接收数据错误
//参数说明：ch：串口接收到的数据(1字节)，*dataLen:存储接收到的数据长度，
//      *data:将接收到的数据存放到该数组中;
//功能概要：对接收到来自服务器的数据进行解帧，并将有效数据存入data数组之中，由串口的uart中断调用。
//修改日期：【2018-06-30】,LXD
//====================================================================
uint_8 uecom_recv(uint_8 ch,uint_16 *dataLen, uint_8 *data)
{
    static uint_16 index1 = 0;   //帧索引
    static uint_16 length = 0;  //数据长度
    uint_8 ret_val;
    uint_16 i;
    uint_8 CRC[2];
    uint_16 crc;
    //如果未遇到帧头或者未收到帧头后面的数据(即不是数据帧)
    if((index1 == 0 && ch != frameHead[0]) ||
      (index1 == 1 && ch != frameHead[1]))
    {
    	index1 = 0;
        length = 0;
        ret_val = 2;     //接收数据错误
        goto uecom_recv_exit;
    }
    //至此说明是来自服务器的数据
    data[index1++] = ch;  //存储帧数据
    if(index1 == 19)      //读取data长度
    {
        length = ((uint_16)data[17]<<8) + data[18];
    }
    //接收到的数据达到一帧长度。length+23为整个帧长度
    if(length != 0 && index1 >= length+23)
    {
        //CRC校验
        crc = crc16(data+2,length+17);
        CRC[0] = (crc>>8)&0xff;
        CRC[1] = crc&0xff;
        if(data[index1-2]!=frameTail[0] ||
        		data[index1-1]!= frameTail[1]  //未遇到帧尾
            || CRC[0] != data[length+19] ||
			CRC[1] != data[length+20])        //CRC检验错误
        {
        	index1 = 0;
            length = 0;
            ret_val = 2;         //接收数据错误
            goto uecom_recv_exit;
        }
        for(i=0;i<length;i++)
        {
            data[i] = data[i+19];//19为有效字节前的数据长度
        }
        *dataLen = length;
        index1 = 0;
        length = 0;
        ret_val = 0;             //接收到了通过TCP/UDP发来的数据
        goto uecom_recv_exit;
    }
    *dataLen = 0;
    ret_val = 1;//正在解帧
uecom_recv_exit:
    return ret_val;
}

//=====================================================================
//函数名称：uecom_transparentRecv
//函数返回：0：接收到了通过TCP/UDP发来的数据；1：未接收到数据
//参数说明：ch:串口接收到的数据
//                   *dataLen:存储接收到的数据长度
//                   *data:将接收到的数据存放到该数组串中;
//功能概要：透传接收函数，将接收到的数据存入data数组之中，由串口的uart中断调用。
//修改日期：【20180922】,LXD
//=====================================================================
uint_8 uecom_transparentRecv(uint_8 ch,uint_16 *dataLen, uint_8 *data)
{
	static uint_8 flag = 0;
	static uint_16 recvLength = 0;
	static uint_16 index = 0;
	static uint_16 length = 0;
	uint_8 ret_val = 1;
	uint_16 i;
	uint_8 CRC[2];
	uint_16 crc;
	switch(flag)
	{
	case 0:    if(ch=='+')flag=1; else flag = 0;break;
	case 1:    if(ch=='E')flag=2; else flag = 0;break;
	case 2:    if(ch=='S')flag=3; else flag = 0;break;
	case 3:    if(ch=='O')flag=4; else flag = 0;break;
	case 4:    if(ch=='N')flag=5; else flag = 0;break;
	case 5:    if(ch=='M')flag=6; else flag = 0;break;
	case 6:    if(ch=='I')flag=7; else flag = 0;break;
	case 7:    if(ch=='=')flag=8; else flag = 0;break;
	case 8:    if(ch=='0')flag=9; else flag = 0;break;
	case 9:    if(ch==',')flag=10; else flag = 0;recvLength = 0;break;
	case 10:   if(ch==','){flag=11;index=0;}else recvLength=recvLength*10+ch-'0';break;
	case 11:
	//无帧格式
		if(index<recvLength)
		{
			data[index++]=ch;
		}
		else
		{
			ret_val = 0;
			*dataLen = recvLength;
			flag = 0;
		}
		break;
	}
uecom_recv_exit:
    return ret_val;
}

//====================================================================
//函数名称：uecom_recvLocation
//函数返回：0：接收到了定位信息；1：接收数据错误
//参数说明：ch：串口接收到的数据(1字节); *data:将接收到的数据存放到该数组中;
//功能概要：将接收到的一行定位信息存入data数组之中，由串口的uart中断调用。
//修改日期：【2018-07-17】,LXD
//====================================================================
uint_8 uecom_recvLocation(uint_8 ch, uint_8 *data)
{
	static uint_16 index2 = 0;   //数据索引
    uint_8 ret_val,*p,j;
    //如果接收的前三个字节不是$GN,则认为不是定位信息
    if((index2 == 0 && ch != '$')
    	||(index2 == 1 && ch != 'G')
		||(index2 == 2 && ch != 'N')
		||(index2 == 3 && ch != 'R')
		||(index2 == 4 && ch != 'M')
		||(index2 == 5 && ch != 'C')
		)
    {
    	index2 = 0;
        ret_val = 1;     //接收数据错误
        goto uecom_recv_exit;
    }
    //至此说明是定位信息
    data[index2++] = ch;      //存储接收到定位信息
    if(ch=='\n') //如果接收到一行有效定位信息
    {
    	if(data[17]=='A')
    	{
    		p = strstr(data,",");   //获得字符','第一次所在的位置
    		j=0;                    //用来标记逗号
    		while(j<9)              //定位到第9个逗号
    		{
    			if(*p==',')
    				j++;
    			p++;
    		}
    		if((*p)!=',')        //如果接收到日期（第九个逗号后是日期）
    		{
    			locationFlag=1;//置位接收到一行有效定位信息标志
    			index2 = 0;
    			ret_val = 0;     //接收到一行定位信息
    			goto uecom_recv_exit;
    		}
    	}
		index2 = 0;
		ret_val = 1;
    }
uecom_recv_exit:
        return ret_val;
}
