/********************************************************************
版权所有：	
文件版本： V1.00
文件名称： IEC101.c
生成日期： 2010年04月26日
作    者：hhw
使用范围：

功    能： IEC101规约处理 
更新信息：
更新日志1
修改者:
修改日期:
修改内容:
修改原因:
*********************************************************************/
#include    "head.h"  

u32 IEC101_SendEvent(IEC101_DEF *pt,int is_send_cos);
void IEC101_Info_Printf_One(IEC101_DEF *p);

extern int gprs_vs_write(const unsigned char * buf, int count);

GPRS_INF_DEF tGprsInf;


static void IEC101_State(IEC101_DEF *pt101,int state)
{

	if(pt101->st101 != state)
	{
		char buf[64];
		char log_info[128];
		
		rt_printf_time("IEC101(%d) state: %d => %d!\r\n", pt101->chnl,pt101->st101,state);
		pt101->st101 = state;

		if(state == IEC101_ST_DATA)
		{
			sprintf(buf,"101主站连接(%s)!\r\n",get_comm_name(pt101->chnl + 1));
			log_str_time(LOG_OPERATE,buf,0,1);	

			sprintf(log_info,"IEC101(%d):101 main station connection",pt101->chnl+1);
			load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,1);

			if(log_flag.com_err[pt101->chnl] == true)
			{
				memset(log_info,0,sizeof(log_info));
				sprintf(log_info,"IEC101(%d):Communication process anomaly return",pt101->chnl+1);
				load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,0);
				log_flag.com_err[pt101->chnl] = false;
			}
			
			// SOE发送缓冲区复位，发送指针回到ACK的位置
			if(pRunSet->bTT_SoeResumeComm)
			{		
			#ifdef SOE_RELINK_DISCOS
				if(!g_soe_queue.bSoeRelink[pt101->chnl])
				{			
					//g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_send[pt101->chnl].n;		
					g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_eep.n;		
				}		
				g_soe_queue.bSoeRelink[pt101->chnl] = true;
			#endif
				//pt101->bSoeRelink = true;
				g_soe_queue.tail_send[pt101->chnl].n=g_soe_queue.tail_ack[pt101->chnl].n;	
			}

		}
		else if(state == IEC101_ST_NONE)
		{
			sprintf(buf,"101主站断开(%s)!\r\n",get_comm_name(pt101->chnl + 1));
			log_str_time(LOG_OPERATE,buf,0,0);	

			sprintf(log_info,"IEC101(%d):101 main station disconnection",pt101->chnl + 1);
			load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,0);

		}
	}

	return;
}


static void IEC101_ResetTxBuf(IEC101_DEF *pt101)
{
	pt101->btx_buf_m = false;
	pt101->btx_buf_s = false;
	memset(&pt101->tx_buf,0,sizeof(pt101->tx_buf));
}

static void IEC101_Reset(IEC101_DEF *pt101)
{
	// 通道属性、连接计数不允许复位
	
	// 规约状态复位
	g_comm_link_status &= ~(1<<pt101->chnl);
	IEC101_State(pt101,IEC101_ST_NONE);
	pt101->bSendChange=false;

	// 计时
	pt101->us0_sec = ustimer_get_origin();
	pt101->secs = 0;
	pt101->us0_sendpiece=ustimer_get_origin();		

	// 对时
	pt101->us0_frame = 0;
	pt101->ms_delay = 0;

	// 1级、2级数据发送
	pt101->bFirstFCV = 1;
	pt101->tb_index = -1;
	pt101->FCBSave=0;
	
	// 遥测发送处理
	pt101->bycsend = FALSE;
	pt101->ycno=0;
	pt101->us0_yc = ustimer_get_origin();
	memset(pt101->yc_save,0,sizeof(pt101->yc_save));
	
	// 其它状态复位
	pt101->bRmtSM2=false;
	pt101->dValidTime=0;
	pt101->bMSend=false;
	pt101->bDIR=true;
	pt101->bResetProcess = false;
	pt101->tm.us0_callall=dTCounter;		//  不能用微妙定时器，最多支持13分钟
	pt101->tm.bAppEcho=false;
	//pt101->bSoeRelink = false;
	pt101->bTimeSyn = false;

#if 0
	// SOE发送缓冲区复位，发送指针回到ACK的位置
	if(pRunSet->bTT_SoeResumeComm)
	{		
	#ifdef SOE_RELINK_DISCOS
		if(!g_soe_queue.bSoeRelink[pt101->chnl])
		{			
			//g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_send[pt101->chnl].n;		
			g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_eep.n;		
		}		
		g_soe_queue.bSoeRelink[pt101->chnl] = true;
	#endif
		//pt101->bSoeRelink = true;
		g_soe_queue.tail_send[pt101->chnl].n=g_soe_queue.tail_ack[pt101->chnl].n;	
	}
#endif

	// 遥控信息复位
	ResetRctrl(MASTER_101+pt101->chnl);
	
	// 发送缓冲区复位
	IEC101_ResetTxBuf(pt101);
	
	RS_Recv_Enable(pt101->chnl);

	#ifdef	RCD_STRAN_S
		rt_printf_time("-> Link Reset, Clear File Cache\r\n");
		iec_freefile(&pt101->tf);
		pt101->tf.bTransing=false;
		pt101->tf.bdatTraned=false;
	#endif
			
}


void IEC101_Init(IEC101_DEF *pt,u8 chnl)
{
	// 接收帧间隔为重发间隔时间的一半
	g_tRsComm[chnl].us_recv_timeout = pRunSet->dT101Resend*USTIMER_SEC/2;
	
	memset(pt,0,sizeof(IEC101_DEF));
	pt->type=IEC101_CHN;
	pt->chnl=chnl;
	pt->bMaintainComm=false;	
	if(tRunPara.tUartPara[chnl].wProtocol==PROTOCOL_MAINTAIN)
	{
		pt->bMaintainComm=true;
	}
	// 初始化接收buf结构
	pt->rx_buf.chn = pt->chnl;
	pt->rx_buf.buf = pt->recvbuf;

	rt_fifo_init(&pt->recv_fifo,pt->recv_fifo_buf,IEC101_FIFO_SIZE);
	
#ifdef RCD_STRAN_S	
	queue_init(&pt->rcd_qt.queue,NEWRCD_COUNT,&pt->rcd_qt.data,NEWRCD_SIZE);
#endif

	IEC101_Reset(pt);
}


/**************************************************************************
函数名称：IEC101_Send
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：启动485口的数据发送
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


void IEC101_SSend(IEC101_DEF *pt)
{
	u8 *sb = g_tRsComm[pt->chnl].sendbuf;

	// 如果链路层没有数据，直接返回
	if(pt->btx_buf_s == false)
	{
		return;

	}

	// 有数据，复制到真正的发送BUF并清标志
	memcpy(sb,pt->tx_buf_s,pt->tx_buf_s[0]+1);
	pt->btx_buf_s = false;
	
	// 置发送标志
	RS_Send_Enable(pt->chnl);
	g_tRsComm[pt->chnl].nSendCnt=1;   // 发送缓冲区的第一个字节为发送长度
	pt->bSend=true;
	if(!g_bCommLed)g_bCommLed=true;


	if(UART_CHANNEL[pt->chnl]==CFG_UART_GPRS_IN )
	{
		//gprs_vs_write(&sb[1], sb[0]);	
		pt->bSend=false;
		s_stat_tx((int)pt->chnl, sb[0]);
	}

#ifdef FUN_FUXI_ESAM
	if(UART_CHANNEL[pt->chnl]==CFG_UART_CHIP_S1 )
	{
		write_chip_s1(&sb[1], sb[0]);	
		pt->bSend=false;
	}		
#endif

	
	if(g_print_101 && (g_print_port & (1<<pt->chnl)))
	{
		rt_printf("TX_S101_%d:",pt->chnl);
		print_msg("",&sb[1],sb[0]);
	}
	//统计发送帧数
	s_stat_tx_frame((int)pt->chnl);
}

/**************************************************************************
函数名称：IEC101_MSend
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：平衡101，作为主站发送
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


static void IEC101_MSend(IEC101_DEF *pt)
{
	u8 *sb = g_tRsComm[pt->chnl].sendbuf;
	
	// 如果链路层没有数据，直接返回
	if(pt->btx_buf_m == false)
	{
		return;

	}

	// 有数据，复制到真正的发送BUF并清标志
	memcpy(sb,pt->tx_buf_m,pt->tx_buf_m[0]+1);
	pt->btx_buf_m = false;

	// 置发送标志
	RS_Send_Enable(pt->chnl);
	g_tRsComm[pt->chnl].nSendCnt=1;   
	pt->bSend=true;
	if(!g_bCommLed)g_bCommLed=true;	

	
	if(UART_CHANNEL[pt->chnl]==CFG_UART_GPRS_IN )
	{
		//gprs_vs_write(&sb[1], sb[0]);	
		pt->bSend=false;
		s_stat_tx((int)pt->chnl, sb[0]);
	}	

#ifdef FUN_FUXI_ESAM
	if(UART_CHANNEL[pt->chnl]==CFG_UART_CHIP_S1 )
	{
		write_chip_s1(&sb[1], sb[0]);	
		pt->bSend=false;
	}		
#endif
	
	if(g_print_101 && (g_print_port & (1<<pt->chnl)))
	{
		rt_printf("TX_M101_%d:",pt->chnl);
		print_msg("",&sb[1],sb[0]);
	}
	
	//统计发送帧数
	s_stat_tx_frame((int)pt->chnl);
}

static void IEC101_ext_send(IEC101_DEF *pt, u8 *buf,u16 len) 
{
#ifdef FUNC_ENCRY_IN_ONE_SERIAL
	if((tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) && pt->chnl==1)
	{//投入纬德规约时，串口通道1/3/4的待发数据统一由串口通道0发送处理
		u8 *sb = g_tRsComm[0].extsendbuf_1;
		memcpy(sb,buf,len);
		g_tRsComm[0].extsendcnt_1=0;
		g_tRsComm[0].extsendlen_1=len;
		g_tRsComm[0].bextsend_1=true;
	}
	else if((tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) && pt->chnl==3)
	{//投入纬德规约时，串口通道1/3/4的待发数据统一由串口通道0发送处理
		u8 *sb = g_tRsComm[0].extsendbuf_2;
		memcpy(sb,buf,len);
		g_tRsComm[0].extsendcnt_2=0;
		g_tRsComm[0].extsendlen_2=len;
		g_tRsComm[0].bextsend_2=true;
	}
	else if((tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) && pt->chnl==4)
	{//投入纬德规约时，串口通道1/3/4的待发数据统一由串口通道0发送处理
		u8 *sb = g_tRsComm[0].extsendbuf_3;
		memcpy(sb,buf,len);
		g_tRsComm[0].extsendcnt_3=0;
		g_tRsComm[0].extsendlen_3=len;
		g_tRsComm[0].bextsend_3=true;
	}
	else
#endif
	{
		u8 *sb = g_tRsComm[pt->chnl].extsendbuf;
		memcpy(sb,buf,len);
		g_tRsComm[pt->chnl].extsendcnt=0;
		g_tRsComm[pt->chnl].extsendlen=len;
		g_tRsComm[pt->chnl].bextsend=true;
	}
}

static void IEC101_Send_Polling(IEC101_DEF *pt)
{
#ifdef FUNC_ENCRY_IN_ONE_SERIAL
	int i=0,j=0,crc_sum=0;
#endif
	u8 *buf;

#ifdef FUNC_ENCRY_IN_ONE_SERIAL
	memset(&g_tRsComm[pt->chnl].enc_sendbuf,0,sizeof(&g_tRsComm[pt->chnl].enc_sendbuf));
#endif

	// 如果串口忙，不能发送数据
	if(g_tRsComm[pt->chnl].bextsend
	#ifdef FUNC_ENCRY_IN_ONE_SERIAL
		|| g_tRsComm[0].bextsend_1 || g_tRsComm[0].bextsend_2 || g_tRsComm[0].bextsend_3
	#endif
	)
	{
		pt->us0_sendpiece=ustimer_get_origin();
		return;
	}
	if(pt->bSend)//  有数据要发送了
	{
		int len=g_tRsComm[pt->chnl].sendbuf[0];
		
	#ifdef FUNC_ENCRY_IN_ONE_SERIAL
		if(tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC)
		{//纬德规约内容组包
			g_tRsComm[pt->chnl].enc_sendbuf[i++] = 0xAA;
			g_tRsComm[pt->chnl].enc_sendbuf[i++] = (u8)(len);		//数据报文长度
			g_tRsComm[pt->chnl].enc_sendbuf[i++] = (u8)((len)>>8);
			g_tRsComm[pt->chnl].enc_sendbuf[i++] = pt->enc_tunnel;
			memcpy(&g_tRsComm[pt->chnl].enc_sendbuf[i],&g_tRsComm[pt->chnl].sendbuf[1],len);
			i = i+len;
			
			for(j=1;j<i;j++)
			{

				crc_sum = crc_sum + g_tRsComm[pt->chnl].enc_sendbuf[j];
			}
			
			g_tRsComm[pt->chnl].enc_sendbuf[i++] = (u8)crc_sum;
			g_tRsComm[pt->chnl].enc_sendbuf[i++] = 0x55;
			buf = &g_tRsComm[pt->chnl].enc_sendbuf[0];
			len = i;
		}
		else
	#endif
		{
			buf = &g_tRsComm[pt->chnl].sendbuf[1];
		}
		
		if(pRunSet->bTT_ESAM&&(!pt->bMaintainComm))
		{
			u8 ti,cot,se;
			u8 *out;
			iec_GetMsgInfo(buf,len,&ti,&cot,&se,true);
			len =  sec_encrpt_iec(ti,cot,se,buf,len,&out);
			if(len > 0)
			{
				IEC101_ext_send(pt,out,len);
			}
		}
		else
		{
			IEC101_ext_send(pt,buf,len);
		}
		pt->bSend=false;
		return;
	}

	if(ustimer_get_duration(pt->us0_sendpiece) < USTIMER_MS*5)    // 发送帧之间间隔5ms
	{
		return;
	}
	// 如果有从动站数据，发送
	if(pt->btx_buf_s)
	{
		IEC101_SSend(pt);
		pt->us0_sendpiece=ustimer_get_origin();
	}

	// 如果有启动站数据，发送
	else if(pt->btx_buf_m)
	{
		IEC101_MSend(pt);
		pt->us0_sendpiece=ustimer_get_origin();
	}
	else if(pt->bEchosecmd5)
	{
		u8 md5buf[24];
		u16 len;
		len=sec_md5_echo(md5buf);
		IEC101_ext_send(pt,md5buf,len);
		pt->bEchosecmd5=false;
	}
}

/**************************************************************************
函数名称：IEC101_GetOneBuf
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：将数据添加到一级数据缓冲区
输入参数：pt 各通道101结构定义首地址，ps源数据地址
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static int IEC101_BufFreeNum(iec_tbuf_t *pt)
{
	return (pt->tb_tail_ack - pt->tb_head - 1)&(IEC_TBUF_NUM-1);
}

static u16 IEC101_GetOneBuf(iec_tbuf_t *pt)
{
	u16 head;
	
	
	head = pt->tb_head;
	pt->tb_head++;

	// 系统设计时，保证缓冲不会满，以下情况不应出现
	if(pt->tb_head==pt->tb_tail_ack || pt->tb_head==pt->tb_tail_send) 
	{
		rt_printf_time("IEC101: IEC101_GetOneBuf error:head=%d,send=%d,ack=%d.\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack);
		
	}

	return head;
}

/**************************************************************************
函数名称：IEC101_FrameEcho
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：应答短帧报文
输入参数：pt 各通道101结构定义首地址，ZoneByte短政控制域
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static  void IEC101_FrameEcho(IEC101_DEF *pt,unsigned char ZoneByte)
{
	u8 *pd=pt->tx_buf_s;

	// 如果此时正在发送数据，说明逻辑不对
	if(pt->btx_buf_s)
	{
		rt_printf("IEC101_FrameEcho(%d) err:链路层BUF已有数据\r\n",pt->chnl);
	}

	// 更新ACD或DIR位
	if(tRunPara.tUartPara[pt->chnl].b101PH) 
	{
		// 平衡101，DIR置1
		if(!pt->bDIR)
		{
			IEC101_SETDIR(ZoneByte);
		}
	}
	else
	{
		// 目前所有数据都当成一级数据，等新标准正式颁布后再分一、二级数据。
		// 非平衡101， 如果还有一级数据，设置ACD位
		if((pt->tx_buf[0].tb_head != pt->tx_buf[0].tb_tail_send)
			|| (pt->tx_buf[1].tb_head != pt->tx_buf[1].tb_tail_send)
			|| (pt->tx_buf[2].tb_head != pt->tx_buf[2].tb_tail_send))
		{
			IEC101_SETACD(ZoneByte);
		}
	}

	// 形成报文
	if(!tRunPara.b101Addr2Byte)
	{
		pd[0]=5;
		pd[1]=0x10;
		pd[2]=ZoneByte;
		pd[3]=(u8)tRunPara.byAddr;
		pd[4]=pd[2]+pd[3];
		pd[5]=0x16;
	}
	else
	{
		pd[0]=6;
		pd[1]=0x10;
		pd[2]=ZoneByte;
		pd[3]=(u8)tRunPara.byAddr;
		pd[4]=(u8)(tRunPara.byAddr>>8);
		pd[5]=pd[2]+pd[3]+pd[4];
		pd[6]=0x16;

	}
}


/**************************************************************************
函数名称：IEC101_GetClassOne
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：从一级数据缓冲区里读取数据
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void IEC101_UpdateCOS(u8 *pd)
{
	u8 sq,num,i,ti;
	u16 cp;
	u8 addr=0;	//传送原因双字节
	u8 SIQ;
	if(tRunPara.b101Cot2Byte)  addr++;		
	if(tRunPara.b101App2Byte)  addr++;

	ti=pd[0];
	sq = pd[1]&0x80;
	num = pd[1]&0x7f;
	cp = pd[4+addr] | (pd[5+addr]<<8);
	pd +=(6+addr);
	for(i=0;i<num;i++)
	{
		SIQ = tbl_get_yx(tbl_cp2index(cp),(ti==3?1:0));
        #if !defined GD_AREA_SHANTOU && !defined GD_AREA_FOS_2020 && !defined FUNC_JX_YX_BEFORE
		if(g_run_stu.bjx)
			SIQ|=0x80;	
        #endif
		*pd++ = SIQ;		
		if(sq == 0)
		{
			cp = pd[0] | (pd[1]<<8); 
			pd += 2;
		}
		else
		{
			cp++;
		}
	}
}

//101可变帧长中信息体相对于帧头的地址。
u32 IEC101_InfoAddr(void)
{
	u32 addr;

	// 10 = 68 L L 68 CTRL LADDR TYPE VSQ COT PADDR
	addr = 10;
	if(tRunPara.b101Addr2Byte) 	addr++;
	if(tRunPara.b101App2Byte) 	addr++;
	if(tRunPara.b101Cot2Byte) 	addr++;

	return addr;
}
static bool IEC101_SendOneBuf(IEC101_DEF *pt101)
{
	u16	i,len,frm_attr,cnt;	
	u8 *pd,cs,tb_send;
	iec_tbuf_t *pt;	

	// 还有上帧数据没有发送完，直接返回。逻辑上不应该有这种现象出现
	if(tRunPara.tUartPara[pt101->chnl].b101PH ? pt101->btx_buf_m : pt101->btx_buf_s)
	{
		RT_PRINTF_POSITION();
		return false;
	}
			
	// 运行到这，如果有数据就无条件发送
	for(i=0;i<3;i++)
	{
		pt = &pt101->tx_buf[i];
		if(pt->tb_head == pt->tb_tail_send)  
		{
			continue;
		}
	 	
		// 更新发送尾指针,保留老指针备用。
		// 由于系统设计时保证发送BUF队列不会溢出，所以指针先加也是没有问题的。
		tb_send = pt->tb_tail_send;
		pt->tb_tail_send++;
		

		//拷贝数据并设置控制域
		len = pt->tb_data[tb_send][0];
		if(tRunPara.tUartPara[pt101->chnl].b101PH)
		{
			u8 zone; 

			// 获取数据
			pd = pt101->tx_buf_m;
			memcpy(pd,pt->tb_data[tb_send],len + 1);  

			// 平衡101，将控制域修改，原来一级数据缓冲区中为8，现在需要变为3
			zone=IEC101_CALL_DATA;

			// 合纵FTU现场，测试长帧，功能码需回2，不能是3   (原来为3，不合理)
			if(pd[7+tRunPara.b101Addr2Byte]==104) //长帧测试帧	
			{	 
				zone=IEC101_CALL_TESTLINK;  
				pt101->bTestFrameL = true;
			}

			
			//梅州FTU，101主站总召前，通讯续传不回确认帧
			if((pd[7+tRunPara.b101Addr2Byte]==0x1e)&&(pt101->bSendChange == 0)) //SOE
			{
				pt101->bSoeResume = true;
			}

			
			IEC101_SETFCV(zone);   //FCV位有效
			IEC101_SETPRM(zone);	
			if(!pt101->bDIR)IEC101_SETDIR(zone);				

			if(pt101->bMFCB)
			{
				IEC101_SETFCB(zone);  
			}
			else
			{
				IEC101_CLRFCB(zone);  
			}	
			pd[5]=zone;
		}
		else
		{
			// 获取数据
			pd = pt101->tx_buf_s;
			memcpy(pd,pt->tb_data[tb_send],len + 1); 
			
			// 目前所有数据都当成一级数据，等新标准正式颁布后再分一、二级数据。
			// 非平衡101，如果还有一级数据，设置ACD位
			if((pt101->tx_buf[0].tb_head != pt101->tx_buf[0].tb_tail_send)
				|| (pt101->tx_buf[1].tb_head != pt101->tx_buf[1].tb_tail_send)
				|| (pt101->tx_buf[2].tb_head != pt101->tx_buf[2].tb_tail_send))
			{
			IEC101_SETACD(pd[5]);
		}
		}
		
		// 更新低优先级的COS
		frm_attr = pt->tb_info[tb_send].frm_attr;
		if((frm_attr&(FRAME_I_PRIOR_LOW|FRAME_I_COS)) == (FRAME_I_PRIOR_LOW|FRAME_I_COS))
		{
			IEC101_UpdateCOS(pd + 7 + tRunPara.b101Addr2Byte);
		}

		// 如果是延时获取帧，校验和计算前计算延时
		if(frm_attr & FRAME_I_106)
		{
			u16 ms;
			u8 * info_addr;

			info_addr = pd + 1 + IEC101_InfoAddr();
			ms = info_addr[2] + (info_addr[3] << 8);
			ms += ustimer_get_duration(pt101->us0_frame)/USTIMER_MS;
			info_addr[0] = 0;
			info_addr[1] = 0;
			info_addr[2] = ms;
			info_addr[3] = ms>>8;
		}

		// 计算校验和
		cs=0;
		len = pd[2];
		pd=&pd[5];
		for(cnt=0;cnt<len;cnt++)  //标示拷贝
		{			
			cs+=*pd++;
		}
		*pd++=cs;
		
		// 保留发送的索引，供确认的时候用
		pt101->tb_index=i;
		
		// 设置安全报文发送标志
		if(frm_attr & FRAME_I_ENCRYPT)
		{
			pt101->bMSend_encrypt = true;
		}
		
		return true;
	}

	return false;
}


 static  void IEC101_AckOneBuf(IEC101_DEF *pt101)  //调整一级数据缓冲区，已发送数据清除
{
	iec_tbuf_t *pt;	
	char log_info[128]={0};

	// 如果最后发送的遥测帧，确认遥测帧
	if (pt101->bycsend)
	{
		pt101->bycsend = FALSE;
		return;
	}

	// 如果没有发送报文，不需要确认
	if(pt101->tb_index < 0)
	{
		return;
	}

	// 如果没有需要确认的帧，返回，但这不是正常流程
	pt = &pt101->tx_buf[pt101->tb_index];
	pt101->tb_index = -1;
	if(pt->tb_tail_ack == pt->tb_tail_send)
	{
		
		rt_printf_time("IEC101_AckOneBuf error!\r\n");
		IEC101_Info_Printf_One(pt101);
		sprintf(log_info,"IEC101(%d):IEC101_AckOneBuf error!",pt101->chnl+1);
		load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
		log_flag.com_err[pt101->chnl]  = true;
		
		return;
	}
	
	// 如果是SOE报文，需确认SOE缓冲池中SOE.
	if(pt->tb_info[pt->tb_tail_ack].frm_attr&FRAME_I_SOE)
	{
		soe_ack(pt101->chnl,pt->tb_info[pt->tb_tail_ack].soe_tail_send);
	}

	pt->tb_tail_ack++;
}



/**************************************************************************
函数名称： IEC101_Asdu_Yc
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：通用分类数据的链路直接响应
输入参数：pt 缓冲区地址，pdat报文数据，len报文长度
                           vsq,inf,cot ,rii,IEC101规约要求的内容
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static void IEC101_Asdu_Yc(IEC101_DEF *pt,u8 *pdat,u8 len,u8 vsq, u8 cot)
{
	u8 *pd,*pdata;
	u8 cnt,checksum,sum;
	u8 AddByte=0;
	sum=0;

	if(tRunPara.tUartPara[pt->chnl].b101PH)
	{
		pdata=pt->tx_buf_m;	
	}
	else
	{
		pdata=pt->tx_buf_s;	
	}

	if(tRunPara.b101Addr2Byte) AddByte=1;

	pd=&pdata[7+AddByte];

	if(tRunPara.bYcBDH)  //标度化值
	{
		pd[sum++]=11;						//类型标示
	}
	else if(tRunPara.bYcFloat)  //浮点值
	{
		pd[sum++]=13;						//类型标示浮点数
	}	
	else
	{
		pd[sum++]=9;						//类型标示
	}

	
	pd[sum++]=vsq;						//VSQ
	pd[sum++]=cot;						//传送原因
	if(tRunPara.b101Cot2Byte)  pd[sum++]=0;						//传送原因		
	pd[sum++]=(u8)tRunPara.byAddr;	//应用服务单元地址		
	if(tRunPara.b101App2Byte)  pd[sum++]=(u8)(tRunPara.byAddr>>8);	//应用服务单元地址		
	
	for(cnt=0;cnt<len;cnt++)  //标示拷贝
	{	
		pd[sum++]=*pdat++;			
	}			
	pdata[0]=(sum+2+6+AddByte);  //报文长度 +2=控制域、地址 +6=报文头（4字节）校验和、结束符	
	pdata[1]=0x68;  	//报文头
	pdata[2]=sum+2+AddByte;   //数据长度
	pdata[3]=sum+2+AddByte;
	pdata[4]=0x68;		
	if(tRunPara.tUartPara[pt->chnl].b101PH)
	{
		pdata[5]=IEC101_CALL_DATA; 
		IEC101_SETFCV(pdata[5]);   //FCV位有效
		IEC101_SETPRM(pdata[5]);	
		if(!pt->bDIR)IEC101_SETDIR(pdata[5]);				
		
		if(pt->bMFCB)
		{
			IEC101_SETFCB(pdata[5]);  
		}
		else
		{
			IEC101_CLRFCB(pdata[5]);  
		}
	}
	else
	{
		pdata[5]=IEC101_ECHO_DATA;			//类型标示
	}
	pdata[6]=(u8)tRunPara.byAddr;	//链路地址
	if(tRunPara.b101Addr2Byte)  pdata[7]=(u8)(tRunPara.byAddr>>8);	//链路地址
	
	checksum=0;
	pd=&pdata[5];
	for(cnt=0;cnt<pdata[2];cnt++)  //标示拷贝
	{			
		checksum+=*pd++;
	}
	*pd++=checksum;
	*pd++=0x16;			


}
/**************************************************************************
函数名称：IEC101_GetYc
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：响应遥测帧
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static bool IEC101_GetYc(IEC101_DEF *pt)
{
	bool bContinue=false;     //是否保存当前判断的遥测位置
	u8 *pd;
	u8 vsq;
	int i,num;
	WORD cp=0;
	int ycvalue;	
	union{

		float ff;
		u8 tt[4];
	}ff;

	// 主动上送遥测条件:
	// 1、总查询结束后可送
	// 2、没有在处理加密遥控报文
	// 3、不是复位进程状态
	// 4、主动上送遥测投入
	if((!pt->bSendChange) || pt->bRmtSM2 || pt->bResetProcess || (!tRunPara.bAutoSendYc))
	{
		return false;
	}

	 //平衡101，主动发送不能小于发送间隔时间
	if(tRunPara.tUartPara[pt->chnl].b101PH)
	{
		if((ustimer_get_duration(pt->us0_yc) < (DWORD)rt_round(tRunPara.fYcTime*USTIMER_SEC))|| byc_filter)//
		{
			return false;
		}
	}	

#if defined IEC_JXYB_DEAL || defined NO_SEND_YC
	if(g_run_stu.bjx)
		return false;
#endif


	// 得到一遥测帧
	pd=g_arrIECBuf;
	vsq=0;
	num=0;
	for(i=pt->ycno;i<g_table_head->ac_num;i++)
	{
		u8  QDS=0;	//品质描述， 默认为零
		u8 no=g_ac_table[i].indexno;
		long lmeaval;
		bool bsend=false;
		if(g_ac_table[i].dead_value==0||g_ac_table[i].normal==0) continue;  //死区或额定为零，不主动上送
		if(no>0)
		{	
			DWORD dwMax=0;
			DWORD dwDeadVal=0;
			float rate;
			float f_65536=65536.0;
			u8 link_qds=0;
			no-=1;
			rate=(float)g_ac_table[i].rate/65536.0;
			//dwMax=(DWORD)g_ac_table[i].normal;			
			dwMax = (tRunPara.bDeadType==true) ? (DWORD)g_ac_table[i].normal:_AbsL(pt->yc_save[i]);
			if(dwMax==0) //防止额定或者前一次保存值为0
				dwMax=1;
			dwDeadVal=_Mul_Div_U(g_ac_table[i].dead_value,dwMax,6553600);		
			if(dwDeadVal==0)dwDeadVal=1;
			if(dwMax==0)f_65536=1.0;
			lmeaval=GetRmtMeaVal(g_ac_table[i].owner,no,&link_qds);
			ff.ff=(float)lmeaval/f_65536*rate; //转为浮点数

		#ifdef VOLT_ADAPTIVE_FACTOR
			if(pRunSet->bTT_AdaptiveFactor)
			{
				if(0 == g_ac_table[i].owner)
				{
					if((no <= PUB_AC_UCA1) && (no >= PUB_AC_UA1))
					{
						ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt1_two;
					}
					else if((no <= PUB_AC_UCA2) && (no >= PUB_AC_UA2))
					{
						ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt2_two;
					}
				}
			}
		#endif
			
			ycvalue=(int)rt_round(ff.ff);

			

			// 溢出处理
			if(tRunPara.bYcFloat)
			{
				if(_AbsL(lmeaval) > (dwMax*10))  // 大于额定值的10倍*65536
				{
					if(dwMax>0)QDS|=0x01;		//遥测溢出
					
				}
			}
			else
			{
				if (ycvalue >= 0)
				{
					if (ycvalue > 32767)
					{
						ycvalue = 32767;
						QDS|=0x01;		//遥测溢出
					}
				}
				else
				{
					if (ycvalue < (-32768))
					{
						ycvalue = -32768;
						QDS|=0x01;		//遥测溢出
					}
				}
			}

			if(((no==PUB_AC_YC9) || (no==PUB_AC_YC10) || (no==PUB_AC_YC11) || (no==PUB_AC_YC12)))
			{
				bsend = true;
			}
			// 如果大于死区值，上送
			#ifndef IEC_JXYB_DEAL
			if(bsend || ((i<MAX_YC_NUMBER)&&(_AbsL(lmeaval-pt->yc_save[i])>=dwDeadVal)))
			#else
			if(bsend || ((i<MAX_YC_NUMBER)&&(_AbsL(lmeaval-pt->yc_save[i])>=dwDeadVal)&& (link_qds!=0x80)))
			#endif
			{
				if(g_run_stu.bToolRmtTest||g_run_stu.bHmiRmtTest) //在测试模式下，置无效状态
				{
					if(pRunSet->bTT_RmtTest)QDS|=0x80;
				}
			
				cp=g_ac_table[i].cp;
				
				if(i<MAX_YC_NUMBER)	pt->yc_save[i]=lmeaval;
				pd[num++]=(u8)cp;		//信息体地址L
				pd[num++]=(u8)(cp>>8);	//信息体地址H

				if(tRunPara.bYcFloat)  //浮点值上送
				{
					num += CopySwap(&pd[num],ff.tt,sizeof(ff.tt),true);	
				}
				else
				{				
					pd[num++]=(u8)(ycvalue);				 //无符号整数
					pd[num++]=(u8)(ycvalue>>8);				 //无符号整数
				}
                #if  !defined GD_AREA_SHANTOU //&& !defined GD_AREA_FOS_2020	
				if(g_run_stu.bjx)
				{
					QDS|=0x80;//带品质描述，有效 <1>：=无效
				}
                #endif

				#ifdef IEC_QDS_HAVE
				QDS|=link_qds;
				#endif
				
				pd[num++]=QDS;
				vsq++;

			}

			if(num>MAX_101_FRAME_LENTH)
			{
				bContinue=true;
				break;
			}
			
		}
	}

	
	
	// 更新下一帧的遥测序号
	pt->ycno = bContinue ? (i+1) : 0;

	// 发送遥测帧
	if(num>0)
	{
		
		IEC101_Asdu_Yc(pt,g_arrIECBuf,num,vsq,3);
		pt->us0_yc = ustimer_get_origin();
		
		return true;
	}
	return false;
}

static void IEC101_LinkInf(IEC101_DEF *pt)
{
	//68 bc bc 68 28 01 f0 08   14    01       01 40 00 00 00 00 00 00 00 00 00 00 00 00 00   遥测帧
	//                               vsq cot appaddr

	u8 *pd,*pdata;
	u8 checksum,sum;
	int i;
	WORD cp;
	u8 num;	
	WORD count=0;
	
	pdata=pt->tx_buf_s;	
	
	sum=0;	
	pd=&pdata[5];
	pd[sum++]=IEC101_ECHO_DATA;			//类型标示
	pd[sum++]=(u8)tRunPara.byAddr;	//链路地址
	pd[sum++]=250;  //类型标示
	pd[sum++]=8;	//长度
	pd[sum++]=14;  //传送原因
	pd[sum++]=1;	//KOD
	pd[sum++]=0;      //统计遥测遥信个数

	cp=g_di_table[0].cp;
	num=0;
	for(i=0;i<g_table_head->di_num;i++)  // 遥信个数统计 
	{
		if(g_di_table[i].cp==(cp+num))
		{
			num++;
		}
		else
		{
			pd[sum++]=1;	//遥信类型
			pd[sum++]=num;
			pd[sum++]=(u8)cp;	//遥信个数
			pd[sum++]=(u8)(cp>>8);	//遥信个数		
			count++;
			cp=g_di_table[i].cp;
			num=1;
		}
	}
	if(num)
	{
		pd[sum++]=1;	//遥信类型
		pd[sum++]=num;
		pd[sum++]=(u8)cp;	//遥信点号
		pd[sum++]=(u8)(cp>>8);	//遥信点号		
		count++;

	}
	cp=g_ac_table[0].cp;
	num=0;
	for(i=0;i<g_table_head->ac_num;i++)  // 遥测个数统计 
	{
		if(g_ac_table[i].cp==(cp+num))
		{
			num++;
		}
		else
		{
			pd[sum++]=2;	//遥测类型
			pd[sum++]=num;
			pd[sum++]=(u8)cp;	//遥测个数
			pd[sum++]=(u8)(cp>>8);	//遥测个数		
			count++;
			cp=g_ac_table[i].cp;
			num=1;
		}
	}
	if(num)
	{
		pd[sum++]=2;	//遥测类型
		pd[sum++]=num;
		pd[sum++]=(u8)cp;	//遥测个数
		pd[sum++]=(u8)(cp>>8);	//遥测个数	
		count++;
	
	}

	pdata[0]=sum+6;  //报文长度 +2=控制域、地址 +6=报文头（4字节）校验和、结束符	
	pdata[1]=0x68;  	//报文头
	pdata[2]=sum;   //数据长度
	pdata[3]=sum;
	pdata[4]=0x68;		
	pdata[11]=count;
	checksum=0;
	pd=&pdata[5];
	for(i=0;i<sum;i++)  //标示拷贝
	{			
		checksum+=*pd++;
	}
	*pd++=checksum;
	*pd++=0x16;	

}


/**************************************************************************
函数名称：IEC101_Asdu_Add
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：将通用分类数据添加到一级数据缓冲区中
输入参数：pt 缓冲区地址，pdat报文数据，len报文长度
                           vsq,inf,cot ,rii,IEC101规约要求的内容
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static void IEC101_Asdu_Add_sts(IEC101_DEF *pt,u8 *pdat,u8 len,u8 vsq, u8 cot,u8 type,u16 attr,u16 soe_tail_send)
{
	u8 *pd;
	u8 cnt,L;
	
	u16 head;
	iec_tbuf_t	*ptb; 
	
	// 获取BUF
	ptb=&pt->tx_buf[1];
	if(attr&FRAME_I_PRIOR_HIGH)ptb=&pt->tx_buf[0];
	else if(attr&FRAME_I_PRIOR_LOW)ptb=&pt->tx_buf[2];
	
	head  = IEC101_GetOneBuf(ptb);	
	
	// 保存数据
	ptb->tb_info[head].frm_attr = attr;
	ptb->tb_info[head].soe_tail_send = g_soe_queue.tail_send[pt->chnl].n;

	L = IEC101_InfoAddr()  + len - 4;
	
	// 报文生成
	pd=ptb->tb_data[head];
	*pd++=L+6;  						//报文长度 +2=控制域、地址 +6=报文头（4字节）校验和、结束符	
	*pd++=0x68;  						//报文头
	*pd++=L;   							//数据长度
	*pd++=L;
	*pd++=0x68;		
	*pd++=IEC101_ECHO_DATA;				//非平衡功能码，平衡通道发送时再修改
	*pd++=(u8)tRunPara.byAddr;			//链路地址1
	if(tRunPara.b101Addr2Byte)
	{
		*pd++=(u8)(tRunPara.byAddr>>8);	//链路地址2
	}
	*pd++=type;							//类型标示
	*pd++=vsq;							//VSQ
	*pd++=cot;							//传送原因1
	if(tRunPara.b101Cot2Byte)
	{
		*pd++=0;						//传送原因2	
	}
	*pd++=(u8)tRunPara.byAddr;		//应用服务单元地址1	
	if(tRunPara.b101App2Byte)
	{
		*pd++=(u8)(tRunPara.byAddr>>8);	//应用服务单元地址2	
	}
	for(cnt=0;cnt<len;cnt++)  
	{	
		*pd++=*pdat++;					//信息体
	}			
	*pd++=0; 							// 校验和，推迟计算
	*pd++=0x16;							// 结束字符

	return;
}

void IEC101_Asdu_Add(IEC101_DEF *pt,u8 *pdat,u8 len,u8 vsq, u8 cot,u8 type,u16 attr)
{
	IEC101_Asdu_Add_sts(pt,pdat,len,vsq,cot,type,attr,0);
}

/**************************************************************************
函数名称：IEC101_TimeSet
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：时间同步，并应答
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void IEC101_TimeSetEcho(IEC101_DEF *pt101,u8 *ps,u8 scot)			//确认帧
{
	u8 *pd;
	int num;
	int check_time=false;
	u8 cot;
	TIME ct;
	struct timespec ts;
	u8 week;

	 //对时
	if(scot == IEC_COT_ACT)
	{
		ct.ms=ps[0]+((WORD)ps[1]<<8);
		ct.min=(ps[2]&0x3F);
		ct.hour=(ps[3]&0x1F);
		ct.day=(ps[4]&0x1F);
		ct.month=(ps[5]&0x0F);
		ct.year=(ps[6]&0x7F);

	// 检查时间，如果合法，重新设置系统时间
		check_time = check_rtc(&ct);
		if(check_time)
		{
		u32 ms;

		// 计算延时，传输延时T2和发送该报文的传输耗时T3
		ms = ustimer_get_duration(pt101->us0_frame)/USTIMER_MS;
//		rt_printf("时间设置:T2=%dms,T3=%dms.\r\n",pt101->ms_delay,ms);
		ms += pt101->ms_delay;

		// 修正时间
		ct.ms += ms;
		if(ct.ms>=60000)
		{
			ct.ms-=60000;
			sys_time_change(&ct);
		}

		// 设置时间
			sys_time_set_rmt(&ct);
		}
	}

	clk_time_get(&ts);
	timespec_to_rtc(ts,&ct,1);	
	// 得到星期
	rtc_to_timespec(&ct, &ts);
	week = WEEK_DAY(ts.tv_sec);	
	//组织报文，以最新时间返回
	pd=g_arrIECBuf;	
	num=0;

#if defined GD_AREA_ZHONGSHAN || defined GD_AREA_ZHONGSHAN_2020
	pd[num++]=0;				//信息序号=0时钟同步		
	pd[num++]=0;				//信息序号=0时钟同步
	if(scot==IEC_COT_ACT)
	{
		pd[num++]=ps[0];				//MsL
		pd[num++]=ps[1];				//MsH
		pd[num++]=ps[2];				//Min
		pd[num++]=ps[3];				//hour
		pd[num++]=ps[4];	//Day
		pd[num++]=ps[5];				//month
		pd[num++]=ps[6];				//year
	}
	else
	{
		pd[num++]=(u8)ct.ms;	  //MsL
		pd[num++]=(u8)(ct.ms>>8);  //MsH
		pd[num++]=ct.min;    //Min
		pd[num++]=ct.hour;	//hour
		pd[num++]=ct.day;    //Day
		pd[num++]=ct.month;	 //month
		pd[num++]=ct.year;	//year
	}
#else
	pd[num++]=0;				//信息序号=0时钟同步		
	pd[num++]=0;				//信息序号=0时钟同步
	pd[num++]=(u8)ct.ms;				//MsL
	pd[num++]=(u8)(ct.ms>>8);				//MsH
	pd[num++]=ct.min;				//Min
	pd[num++]=ct.hour;				//hour
	pd[num++]=((week<<5) | ct.day);	//Day
	pd[num++]=ct.month;				//month
	pd[num++]=ct.year;				//year

#endif

	if(scot == IEC_COT_REQ)
	{
		cot=0x05;
	}
	else
	{
		cot = check_time ? 0x07:0x47;	//对时内容非法，即将传送原因的P/N位置为1
	}
	IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,cot,103,FRAME_I);//
	
}

static void IEC101_TestFrame(IEC101_DEF *pt101,u8 *ps)			//测试确认帧
{
	u8 *pd;
	int num;
	
	//组织报文
	pd=g_arrIECBuf;	
	num=0;
	pd[num++]=0;				//信息体地址	
	pd[num++]=0;				//
	pd[num++]=ps[0];//pd[num++]=0xAA;				//测试码
	pd[num++]=ps[1];//pd[num++]=0x55;				//测试码
	IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,7,104,FRAME_I|FRAME_I_PRIOR_LOW);//
}
/**************************************************************************
函数名称：IEC101_InitOK
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：子站初始化完毕
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static void IEC101_InitOK(IEC101_DEF *pt101)
{	
	u8 *pd;
	int num;
	u8 cot;
	
	//组织报文
	pd=g_arrIECBuf;	
	num=0;
	pd[num++] = 0;
	pd[num++] = 0;
	if(pt101->bInitCot)
	{
		pd[num++]=2; 	//远方复位
	}
	else
	{
		pd[num++]=0; 		//当地电源合上
	}
	pt101->bInitCot=true;
	
	cot = pRunSet->bTT_101Cot03 ? 3 : 4;
	IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,cot,70,FRAME_I);//70 初始化结束帧
}

// 复位进程
static void IEC101_ResetProcess(IEC101_DEF *pt101,u8 qrp)
{	
	u8 *pd;
	int num;
	
	//组织报文
	pd=g_arrIECBuf;	
	num=0;
	pd[num++] = 0;
	pd[num++] = 0;
	pd[num++] = qrp; 	
	
	IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,7,105,FRAME_I);
}



/**************************************************************************
函数名称：IEC101_CallAll
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：通用范围总查询响应
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static void IEC101_CallAll(IEC101_DEF *pt,u8 QOI)		//确认帧
{
	u8 *pd,*pdata;
	u8 vsq;//通用分类数据集数目
	u16 cp=0,attr;
	int i,num,begin,end;	
	
	pdata=g_arrIECBuf;	
	pt->ycno=0;
	attr = pt->bSendChange ? FRAME_I_PRIOR_LOW : FRAME_I;//|		FRAME_I_PRIOR_HIGH
	
	//总召唤确认帧
	num=0;
	pd=pdata;
	pd[num++] = 0;
	pd[num++] = 0;
	pd[num++] = QOI;
	IEC101_Asdu_Add(pt,pdata,num,1,7,100, FRAME_I);
	
	// 遥测帧
	if((QOI==IEC_QOI_CALL_ALL_GROUP)|((QOI >= IEC_QOI_CALL_YC_BEGIN && QOI <= IEC_QOI_CALL_YC_END)))
	{   
		int ycvalue;
		u8 ti;
		union{

			float ff;
			u8 tt[4];
		}ff;	
		long lmeaval;

		// 类型标识
		if(tRunPara.bYcBDH)  
		{
			ti=11;	// 标度化值			
		}
		else if(tRunPara.bYcFloat)  
		{
			ti=13;	// 浮点值			
		}
		else
		{	
			ti=9;	// 归一化值		
		}	
		
		// 确认起止点号
		if(QOI == IEC_QOI_CALL_ALL_GROUP || pRunSet->dGroupYcSize == 0)
		{
			begin = 0;
			end = g_table_head->ac_num;
		}
		else if(pRunSet->dGroupYcSize)
		{
			begin = (QOI - IEC_QOI_CALL_YC_BEGIN) * pRunSet->dGroupYcSize;
			end = begin + pRunSet->dGroupYcSize;
			if(end > g_table_head->ac_num)
			{
				end = g_table_head->ac_num;
			}
		}

		// 得到第一点的通讯点号，必须检查g_ac_table，否则有可能系统崩溃
		if(g_ac_table)
		{
			cp=g_ac_table[begin].cp;
		}

		// 初始数据
		pd=pdata;
		vsq=0;
		num=0;
		
		// 如果点号连续，保存第一点点号
		if(pRunSet->bDIContinue)
		{
			pd[num++]=(u8)cp;
			pd[num++]=(u8)(cp>>8);
		}

		// 发送begin到end之间的所有遥测值
		for(i=begin;i<end;i++)
		{
			u8  QDS=0;	//品质描述， 默认为零
			u8 link_qds=0;
			u8 no=g_ac_table[i].indexno;	

			// 得到遥测值
			if(no>0)
			{		
				DWORD dwMax=0;
				float rate;
				
				float f_65536=65536.0;					
				no-=1;
				rate=(float)g_ac_table[i].rate/65536.0;
				//dwMax=(DWORD)g_ac_table[i].normal;
				dwMax = (tRunPara.bDeadType==true) ? (DWORD)g_ac_table[i].normal:_AbsL(pt->yc_save[i]);
				if(dwMax==0) //防止额定或者前一次保存值为0
					dwMax=1;
				lmeaval=GetRmtMeaVal(g_ac_table[i].owner,no,&link_qds);
				if(dwMax==0)f_65536=1.0;
				ff.ff=((float)lmeaval)/f_65536*rate; //转为浮点数
				
			#ifdef VOLT_ADAPTIVE_FACTOR
				if(pRunSet->bTT_AdaptiveFactor)
				{
					if(0 == g_ac_table[i].owner)
					{
						if((no <= PUB_AC_UCA1) && (no >= PUB_AC_UA1))
						{
							ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt1_two;
						}
						else if((no <= PUB_AC_UCA2) && (no >= PUB_AC_UA2))
						{
							ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt2_two;
						}
					}
				}
			#endif
				
				ycvalue=(int)rt_round(ff.ff);
				if(tRunPara.bYcFloat)
				{		
					if(_AbsL(lmeaval) > (dwMax*10))  // 大于额定值的10倍*65536
					{
						if(dwMax>0)QDS|=0x01;		//遥测溢出
					}
				}
				else
				{
					if (ycvalue >= 0)
					{
						if (ycvalue > 32767)
						{
							ycvalue = 32767;
							QDS|=0x01;		//遥测溢出
						}
					}
					else
					{
						if (ycvalue < (-32768))
						{
							ycvalue = -32768;
							QDS|=0x01;		//遥测溢出
						}
					}
				}
			}
			else		//备用点号，数值上送0
			{
				lmeaval=0;
				ycvalue=0;
				ff.ff=0;
				if(!pRunSet->bDIContinue) continue;  // 点号不连续上送，可不送备用点
			}

			//如果在测试模式下，置无效状态
			if(g_run_stu.bToolRmtTest||g_run_stu.bHmiRmtTest) 
			{
				if(pRunSet->bTT_RmtTest)QDS|=0x80;//带品质描述，有效 <1>：=无效
			}
        #if  !defined GD_AREA_SHANTOU && !defined GD_AREA_FOS_2020 && !defined FUNC_JX_YC_SIQ00
			if(g_run_stu.bjx)
			{
				QDS|=0x80;//带品质描述，有效 <1>：=无效
			}
		#endif
			#ifdef IEC_QDS_HAVE
				QDS|=link_qds;
			#endif
			

			//保存遥测值，供主动遥测上送比较用
			if(i<MAX_YC_NUMBER)pt->yc_save[i]=lmeaval;

			// 如果点号不连续，上送每个点的点号
			if(!pRunSet->bDIContinue)
			{
				cp=g_ac_table[i].cp;
				pd[num++]=(u8)cp;
				pd[num++]=(u8)(cp>>8);
			}

			// 上送值
			if(tRunPara.bYcFloat)  
			{
				//浮点值上送
				num += CopySwap(&pd[num],ff.tt,sizeof(ff.tt),true);
			}
			else
			{	// 整型			
				pd[num++]=(u8)(ycvalue);				 
				pd[num++]=(u8)(ycvalue>>8);				

			}
			
			pd[num++]=QDS;
			vsq++;

			// 发送一帧
			if(num>MAX_101_FRAME_LENTH&&(i<g_table_head->ac_num-1))
			{
				u8 vsq1=vsq;
				if(pRunSet->bDIContinue)
				{
					vsq1|=0x80;
				}
				IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti,attr);
				num=0;					
				pd=pdata;
				if(pRunSet->bDIContinue)
				{				
					pd[num++]=(u8)(cp+vsq);
					pd[num++]=(u8)((cp+vsq)>>8);
					cp+=vsq;
				}
				vsq=0;  
			}					
		}		
		if(num>2)
		{
			u8 vsq1=vsq;
			if(pRunSet->bDIContinue)
			{
				vsq1|=0x80;
			}
			IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti,attr);
		}
	
		pt->us0_yc = ustimer_get_origin();
	}

	// 事件帧
	if((QOI == IEC_QOI_CALL_ALL_GROUP) || (QOI >= IEC_QOI_CALL_YX_BEGIN && QOI <= IEC_QOI_CALL_YX_END))	
	{   
		u8 ti;	
		u8 yxtype=0;
		
		// 确定类型标识，单点(1)或双点(3)
		ti = tRunPara.bDPI ? 3:1;

		// 确定起止点号
		if(QOI == IEC_QOI_CALL_ALL_GROUP || pRunSet->dGroupYxSize == 0)
		{
			begin = 0;
			end = g_table_head->di_num;
		}
		else
		{
			begin = (QOI - IEC_QOI_CALL_YX_BEGIN) * pRunSet->dGroupYxSize;
			end = begin + pRunSet->dGroupYxSize;
			if(end > g_table_head->di_num)
			{
				end = g_table_head->di_num;
			}
		}
		
		// 初始数据
		pd=pdata;
		vsq=0;
		num=0;
		
		// 得到第一点的通讯点号，必须检查g_di_table，否则有可能系统崩溃		
		if(g_di_table)
		{
			cp=g_di_table[begin].cp;	
			yxtype=tbl_get_dpi(begin);
		}

		// 如果点号连续，保存第一点点号
		if(pRunSet->bDIContinue)
		{
			pd[num++]=(u8)cp;
			pd[num++]=(u8)(cp>>8);
		}	
		
		// 发送begin到end之间的所有遥信值
		for(i=begin;i<end;i++)
		{
			u8 SIQ;			
			// 得到遥信值
			if(tRunPara.bSDPI) // 单双点分别上送
			{
				u8 type=tbl_get_dpi(i);
				if(yxtype!=type&&num>2)
				{
					u8 vsq2=vsq;
					u8 ti2=yxtype ? 3:1;
					yxtype=type;					
					if(pRunSet->bDIContinue)
					{
						vsq2|=0x80;
					}					
					IEC101_Asdu_Add(pt,pdata,num,(vsq2),QOI,ti2, attr| FRAME_I_COS);//cot=9总查询
					num=0;
					pd=pdata;
					if(pRunSet->bDIContinue)
					{
						pd[num++]=(u8)(cp+vsq);
						pd[num++]=(u8)((cp+vsq)>>8);	
						cp+=vsq;
					}
					vsq=0;
					yxtype=type;
				}				
				SIQ = tbl_get_yx(i,type);
				ti=type ? 3:1;
			}
			else
			{
				SIQ = tbl_get_yx(i,tRunPara.bDPI);
			}
         #if !defined GD_AREA_SHANTOU && !defined GD_AREA_FOS_2020 && !defined FUNC_JX_YX_BEFORE
			if(g_run_stu.bjx)
			{
				SIQ|=0x80;//带品质描述，有效 <1>：=无效
			}
         #endif
			
			if(g_run_stu.bToolRmtTest||g_run_stu.bHmiRmtTest)	//测试模式，遥信置为无效
			{
				if(pRunSet->bTT_RmtTest)SIQ|=0x80;
			}

			// 点号
			if(!pRunSet->bDIContinue)
			{
				cp=g_di_table[i].cp;
				pd[num++]=(u8)cp;
				pd[num++]=(u8)(cp>>8);	
			}
			
			pd[num++]=SIQ;
			vsq++;			
			
			if(((vsq>120)||(num>MAX_101_FRAME_LENTH))&&(i<g_table_head->di_num-1))  // ngd不能超过128，因个数最高位为连续标示位
			{
				u8 vsq1=vsq;
				if(pRunSet->bDIContinue)
				{
					vsq1|=0x80;
				}
				
				IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti, attr| FRAME_I_COS);//cot=9总查询
				num=0;
				pd=pdata;
				if(pRunSet->bDIContinue)
				{
					pd[num++]=(u8)(cp+vsq);
					pd[num++]=(u8)((cp+vsq)>>8);	
					cp+=vsq;
				}
				vsq=0;
			}				
		}		
		if(num>2)
		{
			u8 vsq1=vsq;
			if(pRunSet->bDIContinue)
			{
				vsq1|=0x80;
			}		
			IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti, attr| FRAME_I_COS);//cot=9总查询
		}

	}
	
	// 总召唤结束帧
	num=0;
	pd=pdata;
	pd[num++] = 0;
	pd[num++] = 0;
	pd[num++] = QOI;
	IEC101_Asdu_Add(pt,pdata,num,1,10,100, attr);
	
	// 不允许通讯续传的情况下，第一次总召先清SOE队列，保证上传事件是总召后产生的，后续总召不清SOE队列
	if((pRunSet->bTT_SoeResumeComm == 0) &&(!pt->bSendChange))
	{
		g_soe_queue.tail_send[pt->chnl].n=g_soe_queue.head.n;	
		g_soe_queue.tail_ack[pt->chnl].n = g_soe_queue.tail_send[pt->chnl].n;	
	}

#ifdef FUN_SOE_DELAY_TIME
	if(pt->bSendChange == false)
	{
		pt->DTSendChange = dTCounter;
		pt->bfirst_call = true; 
	}
#endif

	// 置总召标志
	pt->bSendChange=true;

}


static void IEC101_DDAll(IEC101_DEF *pt,u8 QCC)		//确认帧
{
	u8 *pd,*pdata;
	u8 vsq,cot;//通用分类数据集数目
	u16 cp;
	int i,num;	
	
	pdata=g_arrIECBuf;	
	num=0;	
	//总召唤确认帧
	pd=pdata;
	pd[num++] = 0;
	pd[num++] = 0;
	pd[num++] = QCC;
	IEC101_Asdu_Add(pt,pdata,num,1,7,101, FRAME_I);

	// 电度值
	if((QCC==1)||(QCC==5))
	{   
		// 初始数据
		pd=pdata;
		vsq=0;
		num=0;
		cp=g_dd_table[0].cp;
		if(QCC==5)cot=37;  // 总召
		else cot=QCC+37;  // 召某一组		
		pd[num++]=(u8)cp;
		pd[num++]=(u8)(cp>>8);	
		for(i=0;i<g_table_head->dd_num;i++)
		{	
			int sw,no;
			float rate;
			union{
				float ff;
				u8 tt[4];
			}ff;	
			sw=g_dd_table[i].owner;	
			no=g_dd_table[i].indexno;	
			if(no>0)
			{
				rate=(float)g_dd_table[i].rate/65536.0;
				ff.ff=GetRmtDdVal(sw,no-1)*rate;
			}
			else
			{
				ff.ff=0;
			}
			num += CopySwap(&pd[num],ff.tt,sizeof(ff.tt),true);
				
			pd[num++]=0;	
			vsq++;
			// 发送一帧
			if((num>MAX_101_FRAME_LENTH)||(i==g_table_head->dd_num-1))
			{
				IEC101_Asdu_Add(pt,pdata,num,(vsq|0x80),cot,206,FRAME_I_PRIOR_LOW);
				cp+=vsq;
				pd=pdata;
				num=0;					
				vsq=0;  
				pd[num++]=(u8)cp;
				pd[num++]=(u8)(cp>>8);
			}					
		}		
	}
	if(QCC==0x45)//主站扩展瞬时冻结电度命令
	{
		//rand_file_info.b_frz=true;
	}
	// 总召唤结束帧
	num=0;
	pd=pdata;
	pd[num++] = 0;
	pd[num++] = 0;
	pd[num++] = QCC;
	IEC101_Asdu_Add(pt,pdata,num,1,10,101, FRAME_I_PRIOR_LOW);
}



/**************************************************************************
函数名称：IEC101_RmtCtrl
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：遥控处理
输入参数： 点号开始21 0b 00 82 （10-13）
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC101_Echo_YK(IEC101_DEF *pt,u16 cp,u8 ti,u8 cot,u8 v)
{
	u8 *pd,*pdata;
	int num;
	pdata=g_arrIECBuf;
	pd = pdata;
	
	num=0;
	pd[num++]=(u8)cp;
	pd[num++]=(u8)(cp>>8);
	pd[num++]=v;
	IEC101_Asdu_Add(pt,pdata,num,1,cot,ti,FRAME_I|FRAME_I_PRIOR_HIGH);//cot=9总查询			
	pt->bRmtSM2 = false; //遥控执行完成，清除SM2标志，开放事件上传

}
void IEC101_Echo_PAR(IEC101_DEF *pt,u16 cp,u8 ti,u8 cot,BYTE *buf,u8 len,u8 vsq)
{
	u8 *pd,*pdata;
	int num;
	pdata=g_arrIECBuf;
	pd = pdata;
	
	num=0;
	//pd[num++]=(u8)cp;
	//pd[num++]=(u8)(cp>>8);
	memcpy(&pd[num],buf,len);
	num+=len;
	//pd[num++]=v;
	IEC101_Asdu_Add(pt,pdata,num,vsq,cot,ti,FRAME_I);//cot=9总查询			
}
int get_yk_info_cp(u8 cp)
{
	int i;	
	for(i=0;i<g_table_head->do_num;i++)
	{
 		//rt_printf("cp=%X link_ch=%x cp=%x \r\n",cp,(g_do_table[i].link_ch&0x0f),g_do_table[i].link_cp);
		if((g_do_table[i].link_ch&0x01f) == cp)
		{
			return (u8)i;
		}	
 	}
	return -1;
}

u8 get_par_link_101_to_104(BYTE *out,BYTE *in,BYTE parNum,u8 ti)
{
	int size=0,i=0,j=0;
	int no=0;

	if((ti==FOS_PAR_SET_MUL) || (ti==FOS_PAR_SET_ONE))
		size = (parNum*(sizeof(float)+2+1));	//写定值时需多一位QOS位
	else if(ti==FOS_PAR_READ_MUL || ti==FOS_PAR_READ_ONE)
		size = (parNum*(sizeof(float)+2));

	rt_printf("size=%d,parNum=%d\n\r",size,parNum);
	
	for(no=0;no<parNum;no++)
	{	
		if((ti==FOS_PAR_READ_MUL) || (ti==FOS_PAR_READ_ONE))
		{
			out[j++] = in[i++];		//地址	
			out[j++] = in[i++];		//地址
			out[j++] = 0;				
			out[j++] = in[i++];		//值
			out[j++] = in[i++];		//值		
			out[j++] = in[i++];		//值	
			out[j++] = in[i++];		//值									
		}
		else if((ti==FOS_PAR_SET_MUL) || (ti==FOS_PAR_SET_ONE))
		{
			out[j++] = in[i++];		//地址		
			out[j++] = in[i++];		//地址
			out[j++] = 0;				
			out[j++] = in[i++];		//值
			out[j++] = in[i++];		//值		
			out[j++] = in[i++];		//值	
			out[j++] = in[i++];		//值				
			out[j++] = in[i++];		//QOS 
		}
	}

	return (size+parNum);
}


u8 get_par_link_data(BYTE *out,BYTE *in,BYTE parNum,u8 ti)
{
	int size;
	if(ti == FOS_PAR_SET_MUL)//写带品质描述，长度+1
		size = (parNum*(sizeof(float)+2+1));//4
	else
		size = (parNum*(sizeof(float)+2));

	memcpy(out,in,size);//拷贝遥参点表地址=2 && 参数值=size
	return (size);
}

u8 get_par_link_104_to_104(BYTE *out,BYTE *in,BYTE parNum,u8 ti)
{
	int size;
	if(ti == FOS_PAR_SET_MUL)//写带品质描述，长度+1
		size = (parNum*(sizeof(float)+3+1));//4
	else
		size = (parNum*(sizeof(float)+3));

	memcpy(out,in,size);//拷贝遥参点表地址=2 && 参数值=size
	return (size);
}

u8 get_par_link_data_dz(BYTE *out,BYTE *in,BYTE parNum,u8 ti)
{
	int size=0,i,j=0;

	if((ti==FOS_PAR_SET_MUL) || (ti==FOS_PAR_SET_ONE))
		size = (parNum*(sizeof(float)+3+1));	//写定值时需多一位QOS位
	else if(ti==FOS_PAR_READ_MUL || ti==FOS_PAR_READ_ONE)
		size = (parNum*(sizeof(float)+3));

	rt_printf("size=%d,parNum=%d\n\r",size,parNum);
	
	for(i=0;i<size;i++)
	{	//104遥调时，因管理机与级联间仍为101通讯，故管理机转发给级联的报文应去掉1位地址位（104为3位地址位，101为2位地址位）
		if((ti==FOS_PAR_READ_MUL) || (ti==FOS_PAR_READ_ONE))
		{
			if(2 == i%7)	//读定值时3位地址位+4位数据位共7位
				continue;	//跳过地址位第3位
			else
			{
				out[j++]=in[i];
			}
		}
		else if((ti==FOS_PAR_SET_MUL) || (ti==FOS_PAR_SET_ONE))
		{
			if(2 == i%8)	//写定值时3位地址位+4位数据位+1位QOS位
				continue;	//跳过地址位第3位
			else
			{
				out[j++]=in[i];
			}
		}
	}

	return (size-parNum);
}
//获取通道信息
bool get_par_ch_info(WORD parAddr,struct do_table *parTab)
{
	WORD Addr=0;
	int ykCp=0;
	//判断遥参ID是否合法
	if(parAddr<FOS_PAR_STATR_ADDR) 
		return false;
	
	Addr = parAddr-FOS_PAR_STATR_ADDR;

#ifdef YC_QUANTITY
	parTab->link_cp = (Addr/pRunSet->dYC_num)+1;		//转发通信地址
#else
	parTab->link_cp = (Addr/FOS_PAR_SW_NUM)+1;		//转发通信地址
#endif

	//通过遥控的级联地址去判断遥参是否合法
	ykCp = get_yk_info_cp(parTab->link_cp);

	if(ykCp<0)
	{
		rt_printf("定值参数未找到Yk对应的级联地址=%X link_ch=%x \r\n",parAddr,parTab->link_cp);
		return false;
	}
	
	memcpy(parTab,&g_do_table[ykCp],sizeof(g_do_table[0]));	//将遥控配置的转发信息赋到遥参
	#if 0
	parTab->cp = Addr%FOS_PAR_SW_NUM + FOS_PAR_STATR_ADDR;	//转发点号
	#endif
	parTab->link_cp = parTab->cp = parAddr;		//转发通信地址

#ifdef YC_QUANTITY
	rt_printf("\n parAddr=%x link_cp=%x cp=%x link_ch=0X%X ykCp=%d linkAddr=%x\r\n",parAddr,parTab->link_cp,parTab->link_ch,parTab->cp,ykCp, Addr%pRunSet->dYC_num);
#else
	rt_printf("\n parAddr=%x link_cp=%x cp=%x link_ch=0X%X ykCp=%d linkAddr=%x\r\n",parAddr,parTab->link_cp,parTab->link_ch,parTab->cp,ykCp, Addr%FOS_PAR_SW_NUM);
#endif
	return true;
}

#ifdef FUNC_MORE_PRESET
	YT_BUF PresetData101[256];
	YT_BUF SetData101[256];
	int WrongCount101[256];
	int g_CountPreset101=0, g_CountSet101=0;
	bool gb_101_yt=false;
#endif
static void IEC101_ParCtrl(IEC101_DEF *pt,u8 *pdat)	// ps从传送原因开始
{
	u8 cot,ykcot,ykvalue;
	WORD parNum=1;
	WORD ykno=0;	
	WORD yksave=0;
	WORD parAddr=0;
	u8 AppByte=0;
	u8 CotByte=0;
	u8 *ps=&pdat[2];
	u8 ti=pdat[0];	//类型标示
	struct do_table parTab;
	BYTE buf[256];
	BYTE len=0;
	BYTE addrStart=0;
	WORD sw=1;
	static DWORD ctrlParTime=0;    //系统定时器
	static bool bSetParaDisable=false;

	if(tRunPara.b101App2Byte)		//应用单元双字节地址
	{
		AppByte=1;
	}

	if(tRunPara.b101Cot2Byte)		//双字节传送原因
	{
		CotByte=1;
	}
	
	ykcot=(ps[0]&0x01f);

	parNum=(pdat[1]&0xff);//遥参数量,支持遥调大于30个定值
	
	ykno=ps[2+AppByte+CotByte]+(WORD)ps[3+AppByte+CotByte]*256;	
	addrStart = 2+AppByte+CotByte;
	yksave=ykno;
	cot=IEC_COT_UN_OA | IEC_COT_PN;

	
	parAddr = (ps[addrStart+1]<<8)+ps[addrStart];//遥参通信地址
	
	len = get_par_link_data(&buf[0],&ps[addrStart],parNum,ti);//2+(parNum*4);//2:点号 4:参数值 

	ykvalue=ps[AppByte+CotByte+2-1+7];//
		
	rt_printf("ti=%d parAddr=%x parNum= %d recDataLen=%d addrStart=%d  ykvalue=%x ykcot=%02x\r\n",ti,parAddr,parNum,len,addrStart,ykvalue,ykcot);

	if(pRunSet->wEquTypeManager)// 通信管理机模式，转发
	{
	#ifdef YC_QUANTITY
		if(!get_par_ch_info(parAddr,&parTab)||parNum>pRunSet->dYC_num)
	#else
		if(!get_par_ch_info(parAddr,&parTab)||parNum>FOS_PAR_SW_NUM)
	#endif
		{
			cot = IEC_COT_ACTCON | IEC_COT_PN;  // 否定应答
			//refresh_co_file(true,ti,yktype,ykno,cot,yk_cmd);
			return ;
		}
		//parTab.vsq = parNum;
		if(lnk_master_par(&parTab ,ti,ps[0],len,&buf[0],parNum) == 0)
		{
		    #ifdef FUNC_MORE_PREREAD		/*级联遥参方式:0开关,1队列*/
				pt->lnk_par_send_flag |= (1<<g_link_par_cnt);
				rt_printf("-------lnk_par_send_flag=%x,g_link_par_cnt=%d\r\n",pt->lnk_par_send_flag,g_link_par_cnt);
		   	 	g_link_par_cnt = (g_link_par_cnt+1)%SWITCH_NUM_EXT_PUB;				  	
			#else
				pt->lnk_par_send_flag |= (1<<parTab.owner);				 		
			#endif						
			pt->us0_lnk_par = ustimer_get_origin();     			
		}
	}
	else if (pRunSet->wEquTypeDFTU)// 测控装置遥参交互
	{
		WORD parCount;
		WORD addLenParLen;
	#ifdef FUNC_MORE_PRESET
		int n;
	#endif
		cot=IEC_COT_ACTCON;
		//ykvalue = (ti==FOS_PAR_SET_ONE) ? (ykvalue|0x80) :ykvalue;
	#ifndef FUNC_MORE_PRESET
		bfirst_sel = false;
	#endif

	#ifdef FUNC_MORE_PRESET
		if((dTCounter-ctrlParTime) > tRunPara.dYKTime)
		{//超时清零预置/执行/出错数据存储区
			memset(SetData101,0,sizeof(SetData101));
			g_CountSet101 = 0;
			memset(PresetData101,0,sizeof(PresetData101));
			g_CountPreset101 = 0;
			memset(WrongCount101,0,sizeof(WrongCount101));
			rt_printf("超时清除101报文缓存区\r\n");
		}
	#endif
		if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
		{
			if(ykvalue&0x80)
			{
			#ifdef FUNC_MORE_PRESET
				memcpy(&PresetData101[g_CountPreset101++].yc_data,&ps[addrStart],parNum*7);
						
				rt_printf_time("101预置报文[第%d帧]：",g_CountPreset101);
				for(n=0;n<parNum*7;n++)
				{
					rt_printf("%02x  ",PresetData101[g_CountPreset101-1].yc_data[n]);
				}
			#else
				//预置时先对buf_forCompare进行初始化，避免此数组有101/104共用的原因导致存储出错
				memset(buf_forCompare,0,sizeof(buf_forCompare));
				i_forCompare = 0;
				j_forCompare = 0;
				bfirst_sel = true;	//为预置报文时才置1
			#endif
			}
		#ifdef FUNC_MORE_PRESET
			else
			{
				memcpy(&SetData101[g_CountSet101++].yc_data,&ps[addrStart],parNum*7);
				
				rt_printf_time("101固化报文[第%d帧]：",g_CountSet101);
				for(n=0;n<parNum*7;n++)
				{
					rt_printf("%02x  ",SetData101[g_CountSet101-1].yc_data[n]);
				}
			}
		#endif
		}
		for(parCount=0;parCount<parNum;parCount++)//多参数处理
		{
		#ifdef FUNC_MORE_PRESET
            gb_101_yt = true;
        #endif
			if(ti == FOS_PAR_SET_MUL)//读取参数带品质描述
				addLenParLen = (parCount)*(2+4+1);//ID=2,PAR=4,QDS=1
			else
				addLenParLen = (parCount)*(2+4);//ID=2,PAR=4
		#ifdef YC_QUANTITY
			parAddr = (ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]-((tRunPara.byAddr-1)*pRunSet->dYC_num);
		#else
			parAddr = (ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]-((tRunPara.byAddr-1)*FOS_PAR_SW_NUM);
		#endif
			//parAddr = (ps[addrStart+1]<<8)+ps[addrStart];//遥参通信地址
		#ifdef YC_QUANTITY
			if( ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) >= FOS_PAR_STATR_ADDR 
				&& ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) < (FOS_PAR_STATR_ADDR+pRunSet->dYC_num))
		#else
			if( ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) >= FOS_PAR_STATR_ADDR 
				&& ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) < (FOS_PAR_STATR_ADDR+FOS_PAR_SW_NUM))
		#endif
			{	
				parAddr = (ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen];
			}
			rt_printf("parAddr=%x addr2=%X %x addrStart=%x\r\n",parAddr,(ps[addrStart+1]<<8)+ps[addrStart],addLenParLen,addrStart);
			
			if(ti==FOS_PAR_READ_ONE || ti==FOS_PAR_READ_MUL)
			{
				rt_printf("读取遥参激活 parAddr=%x\r\n",parAddr);
				if(!readRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen]))//暂默认为0开关
					cot = IEC_COT_ACTCON | IEC_COT_PN;  // 否定应答// memcpy(&ps[addrStart+2],"1234",sizeof(float));//2:参数点表地址
			} 
			else
			{
			#ifdef FUNC_FIT_QOS
				if(ykcot == IEC_COT_DEACT)
				{
					cot = IEC_COT_DEACTCON;  // 预置取消确认
					bSetParaDisable = true;	//取消预置时置1
					rt_printf("QOS(101)=%02x,取消遥参预置...\r\n",ykvalue);
					break;
				}
			#endif
			
				if(ykvalue&0x80)
				{
					ctrlParTime = dTCounter;    //系统时间戳
					
					if(!selectRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen]))
					{
						cot = IEC_COT_ACTCON | IEC_COT_PN;
						
					#ifdef FUNC_MORE_PRESET
						WrongCount101[g_CountPreset101-1] = g_CountPreset101;
					#else
						bSetParaDisable = true;	//预置报文出错时置1，禁止固化定值
					#endif
					
						break;
					}
					else
					{
						bSetParaDisable = false;	//重新预置时清0
					}
				}
				else
				{
				
				#ifdef FUNC_MORE_PRESET
					int s_stop;
					s_stop = WrongCount101[g_CountSet101-1];
				#endif
				
				#if defined FUNC_MORE_PRESET
                    if(ykcot == IEC_COT_DEACT)
					{
						cot = IEC_COT_DEACTCON;  // 预置取消确认
						bSetParaDisable = true;	//取消预置时置1
						rt_printf("取消遥参预置...\r\n");
						break;
					}
					else if( dTCounter-ctrlParTime > tRunPara.dYKTime || (0 != s_stop) || bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen],ti))//暂默认为0开关
				#elif defined FUNC_FIT_QOS
					if( dTCounter-ctrlParTime > tRunPara.dYKTime || bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen],ti))//暂默认为0开关
				#else
					if(ykcot == IEC_COT_DEACT)
					{
						cot = IEC_COT_DEACTCON;  // 预置取消确认
						bSetParaDisable = true;	//取消预置时置1
						rt_printf("取消遥参预置...\r\n");
						break;
					}
					else if( dTCounter-ctrlParTime > tRunPara.dYKTime || bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen],ti))//暂默认为0开关
				#endif			
					{
						if(dTCounter-ctrlParTime > tRunPara.dYKTime)
						{
							bSetParaDisable = false;	//超时后才清0，避免取消预置后在超时时间内多次发执行帧而误执行
							rt_printf("设置遥参超时...\r\n");
						}
					#ifdef FUNC_MORE_PRESET
						else if(0 != s_stop)
						{
							rt_printf("[第%d帧]101预置报文出错,请检查...\r\n",s_stop);
						}
					#endif
						else if(bSetParaDisable)
						{
						#ifdef FUNC_MORE_PRESET
							rt_printf("取消预置,禁止固化定值\r\n");
						#else
							rt_printf("预置报文出错或取消预置,禁止固化定值\r\n");
						#endif
						}
						cot = IEC_COT_ACTCON | IEC_COT_PN;  // 否定应答
						//屏蔽break语句，除出错定值外均可修改，与104遥调统一做法
						//break;
					}
				}
			}
		}
	#ifdef FUNC_MORE_PRESET
		gb_101_yt = false;
	#endif
		//parTab.vsq = parNum;
		//
		if(cot&IEC_COT_PN)
			rt_printf("设置&&读取 遥参失败 parAddr=%x\r\n",parAddr);
		else 
			rt_printf("设置&&读取 遥参OK parAddr=%x\r\n",parAddr);
		
	#ifndef FUNC_MORE_PRESET
		if((!(ykvalue&0x80) && ti==FOS_PAR_SET_MUL)
			#ifdef FUNC_FIT_QOS
				|| (ykcot == IEC_COT_DEACT)
			#endif
			)
			ctrlParTime = 0;
	#endif
	
		IEC101_Echo_PAR(pt,yksave,ti,cot,&ps[addrStart],len,parNum);
	
	#ifndef FUNC_MORE_PRESET
		if((!(ykvalue&0x80))
			#ifdef FUNC_FIT_QOS
				|| (ykcot == IEC_COT_DEACT)
			#endif
			)
		{
			memset(buf_forCompare,0,sizeof(buf_forCompare));
			i_forCompare = 0;
			j_forCompare = 0;
		}
	#endif
		return ;
	}
	cot = (ykcot == IEC_COT_ACT) ? IEC_COT_ACTCON : IEC_COT_DEACTCON;
	cot |= IEC_COT_PN;
	//rt_printf("设置遥参     error parAddr=%x\r\n",parAddr);
	//IEC101_Echo_PAR(pt,yksave,ti,cot,&ps[addrStart],len);
}

static void IEC101_RmtCtrl(IEC101_DEF *pt,u8 *pdat)	// ps从传送原因开始
{
	u8 cot,ykcot,ykvalue,yktype,ykThz,v;	
	WORD ykno;	
	WORD yksave;
	u8 AppByte=0;
	u8 CotByte=0;
	int i,ret;
	bool bValidYk=false;
	u8 *ps=&pdat[2];
	u8 ti=pdat[0];  //类型标示
	u8 yk_cmd;

	yktype = 0;		
	if(tRunPara.b101App2Byte)		//应用单元双字节地址
	{
		AppByte=1;
	}

	if(tRunPara.b101Cot2Byte)		//双字节传送原因
	{
		CotByte=1;
	}
	
	ykcot=(ps[0]&0x01f);
	ykvalue=ps[4+AppByte+CotByte];	
	yk_cmd = ykvalue&0x03;
	ykno=ps[2+AppByte+CotByte]+(WORD)ps[3+AppByte+CotByte]*256;	
	yksave=ykno;
	cot=IEC_COT_UN_OA | IEC_COT_PN;
	v=ykvalue;
	if(ti==45)
	{
		ykvalue+=1;		//单点遥控转为双点遥控
	}	
	for(i=0;i<g_table_head->do_num;i++)  //查找遥控转发表，看是否有有效的遥控点
	{
		if(ykno != g_do_table[i].cp)  // 遥控点号对应
		{
			continue;

		}

		// 如果配置了级联通道，转发
		if(g_do_table[i].link_ch)
		{
			if(lnk_master_yk(&g_do_table[i] ,ti,ps[0],v) == 0)
			{
				pt->lnk_yk_send_flag |= (1<<g_do_table[i].owner);
				pt->us0_lnk_yk = ustimer_get_origin();
			}
			else
			{	cot = (ykcot == IEC_COT_ACT) ? IEC_COT_ACTCON : IEC_COT_DEACTCON;
				cot |= IEC_COT_PN;
				IEC101_Echo_YK(pt,yksave,ti,cot,v);
			}
			if(ykvalue&0x80)
			{
				yktype=YK_TYPE_SEL;
			}
			else
			{
				yktype=YK_TYPE_EXE;
			}
			refresh_co_file(true,ti,yktype,ykno,cot,yk_cmd);
			return;
		}
		// 本装置遥控
		{
			cot=IEC_COT_ACTCON;
			bValidYk=true;
			if(ykvalue&0x80)
			{
				yktype=YK_TYPE_SEL;
			}
			else
			{
				yktype=YK_TYPE_EXE;
			}

			if(ykcot==8)  //遥控撤销
			{
				yktype=YK_TYPE_CANCEL;
				cot=IEC_COT_DEACTCON;
			}
				
			if((ykvalue&0x03)==2)
			{
				ykThz=YK_VAL_HZ;
			}
			else
			{
				ykThz=YK_VAL_TZ;
			}		

			ret = RemoteCtrl(yktype,i,ykThz,MASTER_101+pt->chnl);
			if(ret != 0)
			{
				cot = IEC_COT_ACTCON | IEC_COT_PN;  // 否定应答
			}

			//接收到主站的遥控信息:选择,执行,撤销
			refresh_co_file(true,ti,yktype,ykno,cot,yk_cmd);
			
			break;
		}
	}

	// 没有找到对应遥控点
	if(i == g_table_head->do_num)
	{
		char buf[128];
		char log_info[128];
		sprintf(buf,"遥控:转发点表未找到对应点(ykno=0x%04x,yktype=%d,ykvalue=%d)",ykno,yktype,ykvalue);
		log_str_time(LOG_OPERATE,buf,0,0);
		rt_printf_time("%s\r\n",buf);

		sprintf(log_info,"IEC101(%d):Remote control:Corresponding di is not in the iectable(ykno=0x%04x,yktype=%d,ykvalue=%d)",pt->chnl+1,ykno,yktype,ykvalue);
		load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
		log_flag.com_err[pt->chnl]  = true;
		
	}

	pt->bRmtSM2 = false;

	IEC101_Echo_YK(pt,yksave,ti,cot,v);

	//必须有对应点号
	if(i != g_table_head->do_num)
	{
		//终端发出遥控信息:选择确认，执行确认
		refresh_co_file(false,ti,yktype,ykno,cot,yk_cmd);
	}
	
	if((yktype==YK_TYPE_EXE)&&(cot==IEC_COT_ACTCON))
	{  
		IEC101_Echo_YK(pt,yksave,ti,10,v);
		
		refresh_co_file(false,ti,yktype,ykno,IEC_COT_ACTTERM,yk_cmd);
		
	}
}


/**************************************************************************
函数名称：IEC101_MFrameCall
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：应答短帧报文
输入参数：平衡方式下，发送帧
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

static  void IEC101_MFrameCall(IEC101_DEF *pt,unsigned char ZoneByte)
{
	u8 *pd=pt->tx_buf_m;	

	IEC101_SETPRM(ZoneByte);	
	if(!pt->bDIR)IEC101_SETDIR(ZoneByte);	
	if(!tRunPara.b101Addr2Byte)
	{
		pd[0]=5;
		pd[1]=0x10;
		pd[2]=ZoneByte;
		pd[3]=(u8)tRunPara.byAddr;
		pd[4]=pd[2]+pd[3];
		pd[5]=0x16;
	}
	else
	{
		pd[0]=6;
		pd[1]=0x10;
		pd[2]=ZoneByte;
		pd[3]=(u8)tRunPara.byAddr;
		pd[4]=(u8)(tRunPara.byAddr>>8);
		pd[5]=pd[2]+pd[3]+pd[4];
		pd[6]=0x16;
	}
}


static  void IEC101_CallGprsInf(IEC101_DEF *pt)
{
	u8 *pd=pt->tx_buf_s;	
	
	pd[0]=9;
	pd[1]=0x55;
	pd[2]=0xAA;		
	pd[3]=0x55;
	pd[4]=0xAA;		
	pd[5]=0x1A;
	pd[6]=0x00;		
	pd[7]=0x00;
	pd[8]=0x9A;		
	pd[9]=0x7C;
}
/**************************************************************************
函数名称：IEC101_link_par
函数版本：1.00 
作者：   
创建日期：2019.4.19     
函数功能说明：101规约遥参级联处理
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC101_link_par(IEC101_DEF *pt)
{

	int i;
	LINKPAR_DEF *par;
	LINK_PARDATA *yd;


	
	if(pt->lnk_par_send_flag == 0)
	{   
		return;
	}

	for(i=0; i<SWITCH_NUM_EXT_PUB; i++)
	{
		if(pt->lnk_par_send_flag & (1<<i))
		{
			par = &g_link_par[i];

			// 遥控应答帧
			if(par->flag[IECLINK_YK_ACK])
			{
				yd = &par->data[IECLINK_YK_ACK];
				IEC101_Echo_PAR(pt, par->cp_m, yd->ti, yd->cot,&yd->buf[0], yd->len,yd->vsq);
				
				par->flag[IECLINK_YK_ACK] = 0;
				rt_printf("参数应答帧\r\n");
			}

			// 遥控结束帧
			if(par->flag[IECLINK_YK_END])
			{
				yd = &par->data[IECLINK_YK_END];
				IEC101_Echo_PAR(pt, par->cp_m, yd->ti, yd->cot,&yd->buf[0], yd->len,yd->vsq);
				
				par->flag[IECLINK_YK_END] = 0;

				pt->lnk_par_send_flag &= ~(1<<i);
				rt_printf("参数结束帧\r\n");
			}

			// 遥控超时，复位
			if(ustimer_get_duration(pt->us0_lnk_par)  > tRunPara.dSM2Time*USTIMER_SEC)
			{
				yd = &par->data[IECLINK_YK_SND];
				rt_printf("参数遥控超时复位:cp_m=%04x,cp_s=%04x,ti=%d,v=%d.\r\n",par->cp_m,par->cp_s,yd->ti,yd->len);
				g_link_par[i].flag[IECLINK_YK_SND] = false;	
				pt->lnk_par_send_flag &= ~(1<<i);
			}
		}
	}
}

/**************************************************************************
函数名称：IEC101_Applay
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：101规约链路数据处理
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

void IEC101_lnk_yk(IEC101_DEF *pt)
{

	int i;
	LINKYK_DEF *yk;
	LINK_YKDATA *yd;

	
	if(pt->lnk_yk_send_flag == 0)
	{
		return;
	}

	for(i=0; i<SWITCH_NUM_EXT_PUB; i++)
	{
		if(pt->lnk_yk_send_flag & (1<<i))
		{
			u8 yktype,yk_cmd;
			bool yk_hs=false;
			yk = &g_lnk_yk[i];

			// 遥控应答帧
			if(yk->flag[IECLINK_YK_ACK])
			{
				yd = &yk->data[IECLINK_YK_ACK];
				IEC101_Echo_YK(pt, yk->cp_m, yd->ti, yd->cot, yd->v);
				
				yk->flag[IECLINK_YK_ACK] = 0;
			}

			// 遥控结束帧
			if(yk->flag[IECLINK_YK_END])
			{
				yd = &yk->data[IECLINK_YK_END];
				IEC101_Echo_YK(pt, yk->cp_m, yd->ti, yd->cot, yd->v);
				
				yk->flag[IECLINK_YK_END] = 0;

				pt->lnk_yk_send_flag &= ~(1<<i);
			}

			// 遥控超时，复位
			if(ustimer_get_duration(pt->us0_lnk_yk)  > tRunPara.dSM2Time*USTIMER_SEC)
			{
				yd = &yk->data[IECLINK_YK_SND];
				rt_printf("级联遥控超时复位:cp_m=%04x,cp_s=%04x,ti=%d,v=%d.\r\n",yk->cp_m,yk->cp_s,yd->ti,yd->v);
				
				pt->lnk_yk_send_flag &= ~(1<<i);
			}
			if(yk_hs)
			{
				if(yd->v&0x80)
				{
					yktype=YK_TYPE_SEL;
				}
				else
				{
					yktype=YK_TYPE_EXE;
				}

				if(yd->cot==IEC_COT_DEACT)	//遥控撤销
				{
					yktype=YK_TYPE_CANCEL;			
				}

				yk_cmd = yd->v&0x03;
				
				refresh_co_file(false,yd->ti,yktype,yk->cp_m,yd->cot,yk_cmd);

				
			}
		}
	}
}

#ifdef YPARA_LINK
void IEC101_lnk_msg_ypara(IEC101_DEF *pt101)
{
	u8 *pd;
	int num=0;	
	u8 pdi=0,cot=0,ti=0,vsq=0;
	int index=0,i=0;	
	msg_data_struct	*msg_data = &pt101->msg_ypara_recv;
	
	pd = g_arrIECBuf;
#if 0
/*遥参是否超时*/
	if(!pt101->msg_ypara_send.flag)	return;		/*没有下发遥参不返回*/	
	if(ustimer_get_duration(pt101->us0_msg_ypara)	> tRunPara.dSM2Time*USTIMER_SEC)
	{
		pt101->msg_ypara_send.flag = 0;	/*超时*/		
		return;
	}
/*遥参是否超时*/
#endif

	if(!msg_data->flag)	return;		/*有返回消息*/	
	msg_data->flag = 0;		
	
	if(msg_data->type == modify_set)
	{	
		msg_ypara_data_struct *ypara_msg = &pt101->msg_ypara_recv.ypara_msg;
		
		ti = 203;	
		/*该协议各类遥参返回格式一致,统一接口即可*/	
		switch(ypara_msg->cmd)
		{					
			case YP_SEL_RETURN:	
				pdi = 0x80;
				cot = IEC_COT_ACTCON;			
				break;

			case YP_SEL_FAULT:				
				pdi = 0x80;
				cot = IEC_COT_ACTCON|IEC_COT_PN;	
				break;	

			case YP_EXE_RETURN:				
				pdi = 0x00;
				cot = IEC_COT_ACTCON;		
				break;

			case YP_EXE_FAULT:
				pdi = 0x00;
				cot = IEC_COT_ACTCON|IEC_COT_PN;	
				break;					

			case YP_CANCEL_RETURN:			
				pdi = 0x40;
				cot = IEC_COT_DEACTCON;	
				break;	

			case YP_CANCEL_FAULT:				
				pdi = 0x40;
				cot = IEC_COT_DEACTCON|IEC_COT_PN;	
				break;	
						
			default:
				break;
		}
					
		pd[num++]=(u8)m_runsection;	//当前定值区
		pd[num++]=(u8)(m_runsection>>8);	//当前定值区		
		pd[num++] = pdi;		/*特征符*/

		//print_msg_base(ypara_msg);			

		for(i=0;i<ypara_msg->num;i++)
		{			
			index = get_index_link(msg_data->sport,ypara_msg->base_data[i].cp_s);
			if(index < 0) continue;

			pd[num++]=(u8)tParaID[index].parId;
			pd[num++]=(u8)(tParaID[index].parId>>8);
			pd[num++]=ypara_msg->base_data[i].val_iec.tag;	
			pd[num++]=ypara_msg->base_data[i].val_iec.size;	
			if(ypara_msg->base_data[i].val_iec.size >0)
			{
				memcpy(&pd[num],ypara_msg->base_data[i].val_iec.value,ypara_msg->base_data[i].val_iec.size);			
				num += ypara_msg->base_data[i].val_iec.size;					
			}				
			vsq++;
		}				

		IEC101_Asdu_Add(pt101,pd,num,vsq,cot,ti,FRAME_I);				
	}	

}

void IEC101_Rmt_msg_ypara_write(IEC101_DEF *pt,BYTE *pdat,BYTE vsq,u8 link_cp)	
{
	WORD section=0;
	BYTE *ps=0; //从点号开始
	int   i,j=0;		
	u16 di = 0;	
	msg_data_struct *msg_data;
	msg_ypara_data_struct *msg;	
	iec_val_struct v;	
	BYTE   pdi;  //参数特征标识符
	
	msg_data = &pt->msg_ypara_send;
	msg = &pt->msg_ypara_send.ypara_msg;
	memset(msg,0,sizeof(msg_ypara_data_struct));

	pt->msg_ypara_send.dport = link_cp;	
	pt->msg_ypara_send.sport = pt->chnl;
	pt->msg_ypara_send.flag = 1;

	
	section=pdat[0]+(pdat[1]<<8);	  // 定值区号
	pdi = pdat[2];	
	
	msg->section_p = section;	//定值区
	if((pdi&0xc0)==0) // 定值固化
	{
		msg->cmd = YP_EXE;	
	}	
	else if((pdi&0xc0)==0x40)	/*撤销*/
	{
		msg->cmd = YP_CANCEL;
	}
	else  // 参数预置
	{
		msg->cmd = YP_SEL;
		ps=&pdat[3];
		for(i=0;i<vsq;i++)
		{	
			di=ps[0]+(ps[1]<<8);		
			ps += 2;
			v.tag = *ps++;
			v.size = *ps++;
			memcpy(v.value,ps,v.size);
			ps += v.size;			
			
			for(j=0;j<ParaIDNum;j++)
			{
				if(tParaID[j].link_ch == 0) continue;
				if(di != tParaID[j].parId) continue;

				msg->base_data[msg->num].cp_m = tParaID[j].parId;
				msg->base_data[msg->num].cp_s = tParaID[j].link_cp;				
				msg->base_data[msg->num].val_iec = v;
						
				msg->num++;	
				break;
			}			
		}						
	}	
	
	msg_data->type = modify_set;
	msg_data->sport = pt->chnl;
	msg_data->dport = link_cp;		
	mananger_msg_ypara_s(msg_data,msg_data->sport,msg_data->dport);
	pt->us0_msg_ypara = ustimer_get_origin();		
}

#endif

int IEC101_TransFile(IEC101_DEF *pt,unsigned char *buf,int len);
int IEC101_Maintain(IEC101_DEF *pt,unsigned char *buf,int len);

#ifdef	RCD_STRAN_S
static void IEC101_Rcd_Ack(IEC101_DEF *pt101)			//发送新录波文件名
{
	u8 *pd;
	int num;
	u8  rcd_num=0;	
	char tmp[128]={0};
	char note[100];

	//组织报文
	pd=g_arrIECBuf;	
	num=0;

	
	if(get_queue_count(&pt101->rcd_qt.queue) >0)	/*有新录波*/
	{
		check_data_queque(&pt101->rcd_qt.queue,tmp);
		sprintf(note, "%s----------准备\r\n",tmp); 
		log_str_time(LOG_OPERATE,note,0,1); 
					
		pd[num++]=0;				//信息体地址	
		pd[num++]=0;				
		pd[num++]=1;				/*文件数量*/		
		pd[num++]=strlen(tmp);		/*文件名长度*/						
		memcpy(&pd[num],tmp,strlen(tmp));		
		num += strlen(tmp);										
	}
	else
	{
		pd[num++]=0;				//信息体地址	
		pd[num++]=0;				//
		pd[num++]=rcd_num;
	}	
	
	IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,7,RCD_TYPE,FRAME_I|FRAME_I_PRIOR_LOW);//
}
#endif


void IEC101_Applay(IEC101_DEF *pt)
{
	u8 *pd;
	u8 AddByte=0;
	u8 CotByte=0;	//传送原因双字节
	u8 AppByte=0;	//应用单元地址双字节
	u8 ti,vsq,cot,L,*info_addr,info_len;
	bool bLFrame=false;  //短帧标志

#ifdef FUNC_ENCRY_IN_ONE_SERIAL
	if(tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC)
	{
		if((pt->chnl != 1) && (pt->chnl != 3) && (pt->chnl != 4))	//投入海南加密时，固定串口2、4、5用于加密数据处理，不允许接收串口数据
		{
			// 接收报文
			IEC101_Recv_App(pt);
			
			if(pt->chnl == 0)
			{
				return;
			}
		}
	}
	else
#endif
	{
		// 接收报文
		IEC101_Recv_App(pt);
	}

#ifdef IEC_NOLINK_NO_CALLYX
	if(g_link_comm.link_flag == false) return;
#endif
	
	// 主动SOE处理，放在报文处理之前，使时间同步报文之前的SOE报文在时间报文应答前发送
	IEC101_SendEvent(pt,tRunPara.bAutoCos);

	// 处理级联遥控
	IEC101_lnk_yk(pt);
	
	// 处理级联遥参
	IEC101_link_par(pt);
	
#ifdef YPARA_LINK
	IEC101_lnk_msg_ypara(pt);
#endif
	// 处理接收的加密报文
	// 先处理加密报文，再处理普通报文，保证同一报文一个循环处理完
	if(pt->bSM2Data)
	{
		if(pRunSet->bTT_SM2)  //加密功能投入
		{
		#ifdef ENCRYPT_SM2
			IEC101_SM2(pt);
		#endif	
		}
		pt->bSM2Data=false;		
	}
	// 处理接收的普通报文
	else if(pt->bData)				
	{
		// 如果从动站buf有数据，说明至少有2帧报文还没有发送完，
		// 在gprs网络，同时收到多帧报文后连续应答处理时会出现这种情况。
		// 为了保证过程的完整性,待数据发送完后再处理接收的报文。
		if(pt->btx_buf_s)
		{
			return;
		}


		// 报文长度调整变量赋值
		if(tRunPara.b101Addr2Byte) AddByte=1;		
		if(tRunPara.b101Cot2Byte)  CotByte=1;		
		if(tRunPara.b101App2Byte)  AppByte=1;

		// 打印接收的报文
		if(g_print_101 && (g_print_port & (1<<pt->chnl)))
		{
			if(pt->recvbuf[0]==0x10)  
			{
				rt_printf("RX_S101_%d:", pt->chnl);
				print_msg("",pt->recvbuf,(5+AddByte));
			}
			else
			{
				rt_printf("RX_L101_%d:", pt->chnl);
				print_msg("",pt->recvbuf,pt->recvbuf[1]+6);
			}	
		}

		// 由于101在本函数开始处接收数据，所以可以在此处复位接收标志
		pt->bData=false;

		// 检查帧标志并跳过固定报文头
		if(pt->recvbuf[0]==0x10)  
		{
			pd=&pt->recvbuf[1];//短帧
			L = 2;
		}
		else if(pt->recvbuf[0]==0x68)  
		{
			pd=&pt->recvbuf[4];//长帧
			L = pt->recvbuf[1];
			bLFrame=true;
		}
		else 
		{	
			return;		
		}

		// 检查是否是GPRS维护信息
		if(pd[0]==0x05) 
		{
			memcpy(tGprsInf.data,&pd[1],64);
			tGprsInf.bInf=true;
			return;
		}

		// 检查地址
		if(tRunPara.b101Addr2Byte)		//双字节地址
		{
			WORD addr;
			addr=pd[1]+(pd[2]<<8);
			if(addr!=tRunPara.byAddr && addr!=0xffff)
				return; //不是发给本装置的数据
		}
		else
		{
			if(pd[1]!=(u8)tRunPara.byAddr && pd[1]!=0xff)
				return; //不是发给本装置的数据
		}

		// 通道空闲时间复位
		pt->dValidTime=0;                

		// 私有101文件传输协议
		if ((((pd[0]&0x0f) == 3) || ((pd[0]&0x0f) == 4))
			&& (pd[2+AddByte] ==137))	
		{
			IEC101_TransFile(pt,&pd[2+AddByte],pt->recvbuf[1]-(2+AddByte));
			pt->btx_buf_s = true;
			return;
		}

		// 私有维护工具软件接口
		if ((((pd[0]&0x0f) == 3) || ((pd[0]&0x0f) == 4))
			&& (pd[2+AddByte] ==138))	
		{
			IEC101_Maintain(pt,&pd[2+AddByte],pt->recvbuf[1]-(2+AddByte));
			pt->btx_buf_s = true;
			return;
		}

		//主站主动发送数据PRM=1  master
		if(pd[0]&0x40)	
		{			
			if(pd[0]&0x80)
			{
				pt->bDIR=true;
			}
			else
			{
				pt->bDIR=false;
			}

			// 非重复报文
			if(!pRunSet->bTT_101FCB
				||(((pd[0]&0x10)&&pt->FCBSave!=(pd[0]&0x20)) 	// FCV位有效，FCB位变位
					|| (!(pd[0]&0x10))							// FCV位无效
					|| pt->bFirstFCV)	)						// 该标志有效，不判断FCB位
			{   
				//FCB位
				if(pd[0]&0x10)
				{
					// 如果不是第一个FCB位且和以前FCB不同，确认发送BUF
					if((pt->bFirstFCV == 0) && (pt->FCBSave != (pd[0]&0x20)))
					{
						IEC101_AckOneBuf(pt);
					}
					
					pt->FCBSave=pd[0]&0x20;	// 保存帧记数位	
					pt->bFirstFCV = 0;		// 如果FCV位有效，标志清0
				}
                
				switch((pd[0]&0x0f))
				{
				// 只有非平衡101才会请求一二级数据
				case 10:			//请求/响应(一级数据）
				case 11:				//请求/响应(二级数据)
					// 如果不是数据状态，不应收到2、3、4、10、11，不进行任何应答，让主站超时复位
					if(pt->st101!=IEC101_ST_DATA)
					{
						return;
					}
					if(!IEC101_SendOneBuf(pt))
					{
						if(pt->tfile.bSegtransing) // 文件传输，放入一级数据缓冲区
						{
							IEC101_SegmentContinue(pt);
						}
						else if(pt->tf.bTransing)
						{
							iec_readfile_send(pt,false);
						}

						if(!IEC101_SendOneBuf(pt))
						{
							pt->bycsend = IEC101_GetYc(pt);
							if(!pt->bycsend) // add by sunxi
							{
								IEC101_FrameEcho(pt,IEC101_ECHO_NO_DATA);
							}
						}
					}
					break;
				case 9:			// 请求链路状态
					IEC101_FrameEcho(pt,IEC101_ECHO_LINK_OK);
					break;
				case 3:				//请求/确认
				case 4:				//请求/无应答	
					// 如果不是数据状态，不应收到2、3、4、10、11，不进行任何应答，让主站超时复位
					// 如果是复位进程状态，不应答主站发送应用层报文
					if(pt->st101!=IEC101_ST_DATA || pt->bResetProcess)
					{
						return;
					}
					
					ti  = pd[2+AddByte]; 
					vsq = pd[3+AddByte];
					cot = pd[4+AddByte];
					info_addr = pt->recvbuf + IEC101_InfoAddr();
					info_len = L + 4 - IEC101_InfoAddr();
					switch(ti)		//类型标识判断
					{
					case 250: // 级联报文数据上送
						IEC101_LinkInf(pt);
					break;

					case  106:
							if(cot == 6)	//延时获得
							{
								// 此处不修改报文，真正发送的时候再计算修改
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_ACTCON,ti,FRAME_I| FRAME_I_106);
							}
							else if(cot == 3)	//延时传递
							{
								// 保存主站下发的传输延时
								pt->ms_delay = info_addr[2] + (info_addr[3]<<8);
							}
							else
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);
							}
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认

						break;

					case  103://0xFF:	//set time ,BroadCast Command
						// 检查COT
						if(cot == IEC_COT_REQ||cot == IEC_COT_ACT)
						{
							IEC101_TimeSetEcho(pt,&pd[8+AddByte+AppByte+CotByte],cot);// Addbyte 链路地址和应用单元地址多了2个字节，传送原因多1个字节
						}
						else
						{
							IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
						}

						pt->bTimeSyn= true;
						
						IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
						break;
					case 100:					//总查询
						// 检查COT
						if(cot != IEC_COT_ACT)
						{
							IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
						}
						else
						{
							IEC101_CallAll(pt,pd[8+AddByte+AppByte+CotByte]);// Addbyte 链路地址和应用单元地址多了2个字节，传送原因多1个字节
							lnk_call_all();
						}
						IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
						break;

					case 101:					//电度总召
						// 检查COT
						if(cot != IEC_COT_ACT)
						{
							IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
						}
						else
						{
							IEC101_DDAll(pt,pd[8+AddByte+AppByte+CotByte]);// Addbyte 链路地址和应用单元地址多了2个字节，传送原因多1个字节
						}
						IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
						break;


						
					case 104:					//测试帧
						IEC101_TestFrame(pt,&pd[8+AddByte+AppByte+CotByte]);
						IEC101_FrameEcho(pt,IEC101_ECHO_SURE);	
						break;	
					case 105:
						// 检查COT
						if(cot != IEC_COT_ACT)
						{
							IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
						}
						else
						{
							u8 qrp;
							qrp = pd[8+AddByte+AppByte+CotByte];
							
						#ifdef FUNC_RESET_EQU
							gb_ResetEqu = true;
						#endif
						
							switch(qrp)//复位进程命令限定词 QRP
							{
								//进程的总复位
								case 1:
									rt_printf_time("IEC101(%d) Reset: 进程的总复位!\r\n",pt->chnl);
									IEC101_ResetTxBuf(pt);

									// 记录复位计时起点和设置复位进程标志
									pt->us0_ResetProcess = ustimer_get_origin();
									pt->bResetProcess = true;
								break;
								
								//复位事件缓冲区等待处理的带时标信息
								case 2:
									rt_printf_time("IEC101(%d) Reset: 复位事件缓冲区!\r\n",pt->chnl);
									g_soe_queue.tail_send[pt->chnl].n = g_soe_queue.head.n;
									g_soe_queue.tail_ack[pt->chnl].n = g_soe_queue.tail_send[pt->chnl].n;
									
								break;
							}
							
							IEC101_ResetProcess(pt,qrp);	
						}
						
						IEC101_FrameEcho(pt,IEC101_ECHO_SURE);
						break;
					case 45:				//单点遥控
					case 46:				//双点遥控
						// 检查单双点是否和参数设置相符
						if((ti == 45 && tRunPara.YKtype == 1) || (ti == 46 && tRunPara.YKtype == 0) )
						{
							// 对不支持的类型标识，否定应答
							IEC101_FrameEcho(pt,IEC101_ECHO_DENY);
							rt_printf("101遥控类型不符(ti=%d,YKtype=%d)\r\n",ti,tRunPara.YKtype);
							break;
						}
						
						// 检查COT
						if(cot != IEC_COT_ACT && cot != IEC_COT_DEACT)
						{
							IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I|FRAME_I_PRIOR_HIGH);			
						}
						else
						{
							IEC101_RmtCtrl(pt,  &pd[2+AddByte]);
						}
						IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
						break; 

						case 122:	//召目录，选择文件，召唤文件，召唤节
							IEC101_File_Echo(pt,&pd[2+AddByte]);  //响应文件召唤
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
							break;
						case 124:		//确认文件，确认节的应答
							IEC101_File_Sure(pt,&pd[2+AddByte]); //对文件操作确认
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
							break;

						case 176: //  本装置复归
							ResetHzLed(0);
							SignalReset(0,false);
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
							break;
					#ifdef RCD_STRAN_S
						case RCD_TYPE: //  本装置复归
							IEC101_Rcd_Ack(pt);
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认						
							break;
					#endif
													
						case 200: //  切换定值区
							if(cot == IEC_COT_ACT)
							{
								IEC101_SectionWrite(pt,&pd[8+AddByte+AppByte+CotByte]);// Addbyte 链路地址和应用单元地址多了2个字节，传送原因多1个字节
							}
							else
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
							}						
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
							break;
						case 201: //  读取当前定值区号
							if(cot == IEC_COT_ACT)
							{
								IEC101_SectionRead(pt);// Addbyte 链路地址和应用单元地址多了2个字节，传送原因多1个字节
							}
							else
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
							}						
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认						
							break;
						break;
						case FOS_PAR_READ_ONE://南网佛山局 读取单个参数
						case FOS_PAR_READ_MUL://南网 读取多个参数
						case FOS_PAR_SET_ONE: //南网佛山局 设置单个参数
						case FOS_PAR_SET_MUL://南网 设置多数参数						
							// 检查COT
							if(cot != IEC_COT_ACT && cot != IEC_COT_DEACT)
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
							}
							else
							{
								IEC101_ParCtrl(pt,  &pd[2+AddByte]);
							}
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认
							break;
						case 202: // 读参数

							if(cot == IEC_COT_ACT)
							{	
								int ret;
#ifdef YPARA_LINK
								ret=iec_setread_mananger((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,false);
#else
								ret=iec_setread((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,false);//vsq为需读参数的个数，0，代表读全部参数，参数为从定值区开始的数据
#endif								
								if(ret!=0) // 读定值出错，否定应答
								{
									IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_OA,ti,FRAME_I);			
								}
							}
							else
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
							}						
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认						
							break;
						case 203: //  修改参数
							if(cot == IEC_COT_ACT||cot==IEC_COT_DEACT)
							{
								int ret;
#ifdef YPARA_LINK
								int link_ch;
								link_ch = get_ypara_linkch_write((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,false);
								if(link_ch == 0)
								{									
									rt_printf("101------写本地定值\r\n");
									ret=iec_setwrite((void*)pt,&pd[6+AddByte+AppByte+CotByte],cot,vsq,false);//vsq为需读参数的个数，0，代表固化参数，参数为从定值区开始的数据
									if(ret!=0) // 写定值出错，否定应答
									{
										BYTE rcot=IEC_COT_ACTCON|IEC_COT_PN;
										if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON|IEC_COT_PN; 	
										IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); 							
									}
									else
									{
										BYTE rcot=IEC_COT_ACTCON;
										if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON;									
										IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); 							
									}	
								}
								else
								{
									rt_printf("101------写级联定值\r\n");
									IEC101_Rmt_msg_ypara_write((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,link_ch);
						
									if((link_ch&0x1f) == 0x1f)		/*广播写直接返回确认*/
									{
										BYTE rcot= IEC_COT_ACTCON;
										if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON;										
										IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); 															
									}							
								}											
#else		
								ret=iec_setwrite((void*)pt,&pd[6+AddByte+AppByte+CotByte],cot,vsq,false);//vsq为需读参数的个数，0，代表固化参数，参数为从定值区开始的数据
								if(ret!=0) // 写定值出错，否定应答
								{
									BYTE rcot=IEC_COT_ACTCON|IEC_COT_PN;
									if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON|IEC_COT_PN; 	
									IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); 							
								}
								else
								{
									BYTE rcot=IEC_COT_ACTCON;
									if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON;									
									IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); 							
								}
							
#endif													
							}
							else
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
							}						
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认						
				
						break;		
						case 210: // 文件传输
						{
							pt->tf.vsq=vsq;
							if(cot == IEC_COT_ACT||cot == IEC_COT_REQ)
							{	int ret;
								ret=iec_file_app((void*)pt,&pt->recvbuf[12+AddByte+AppByte+CotByte],cot,pt->recvbuf[1]-(AddByte+CotByte+AppByte)-9,false);// 从操作标识开始
								if(ret!=0) // 文件处理错误
								{
									BYTE rcot=IEC_COT_UN_OA;
									if(ret==-2)rcot=IEC_COT_UN_COT;
									IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I);								
								}							
							}
							else
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
							}						
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认						

						}		
						break;
						case 211: // 文件升级
						{
							if(cot == IEC_COT_ACT||cot == IEC_COT_DEACT)
							{	int ret;
								ret=iec_pro_update((void*)pt,cot,pd[8+AddByte+CotByte+AppByte],false);// 从操作标识开始
								if(ret!=0) // 文件处理错误
								{
									BYTE rcot=IEC_COT_UN_OA;
									IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I);								
								}							
							}
							else
							{
								IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I);			
							}						
							IEC101_FrameEcho(pt,IEC101_ECHO_SURE);		//链路应答确认						

						}	
						break;						
					default:
						IEC101_FrameEcho(pt,IEC101_ECHO_DENY);	
						break;
					}
					break;

				case 0:  //复位通信单元
					// 复位通道
					IEC101_Reset(pt);
					if(pd[0]&0x80)  // add by sunxi 防止IEC101_Reset调用后，bDIR变为默认值，后续发送报文，标示出错
					{
						pt->bDIR=true;
					}
					else
					{
						pt->bDIR=false;
					}					
					g_comm_link_status |= (1<<pt->chnl);
					IEC101_State(pt,tRunPara.tUartPara[pt->chnl].b101PH ? IEC101_ST_LINK : IEC101_ST_DATA);
					pt->LinkCount++;

					// 应答报文
					IEC101_InitOK(pt);
					IEC101_FrameEcho(pt,IEC101_ECHO_SURE);

					// "重新连接后，总召前，发送完所有老的SOE,不发送COS"
					IEC101_SendEvent(pt,0);
					break;
				case 2:  //链路测试功能
					// 如果不是数据状态，不应收到2、3、4、10、11，不进行任何应答，让主站超时复位
					if(pt->st101!=IEC101_ST_DATA)
					{
						return;
					}
					// 南瑞主站，功能码是2，发送长帧测试帧 
					if(bLFrame)
					{
						ti  = pd[2+AddByte]; 
						if(ti==104)
						{
							IEC101_TestFrame(pt,&pd[8+AddByte+AppByte+CotByte]);
						}
					}	
					IEC101_FrameEcho(pt,IEC101_ECHO_SURE);
					break;				

				default:
					IEC101_FrameEcho(pt,IEC101_ECHO_DENY);	
					break;
				}	//类型判断结束

				// 广播地址不用应答
				if(	   (pd[1]!=0xff) 
					|| (tRunPara.b101Addr2Byte && (pd[2]!=0xff))
					// 平衡101广播查询链路状态回本机地址，江西吉安博威主站要求
					|| (tRunPara.tUartPara[pt->chnl].b101PH && (pd[0]&0x0f) == 9)) 
				{
					pt->btx_buf_s = true;
				}
			}
			// FCV位有效，FCB没有变位，重发保存的确认帧。
			else   
			{
				//重发数据
				if(pt->st101 == IEC101_ST_DATA)
				{
					if(pt->btx_buf_s)
					{
						rt_printf_time("IEC101_Applay:,btx_buf_s==1.\r\n");
					}
					pt->btx_buf_s = true;
				}
			}
		}
		//平衡101  ，接收到主站回复的数据
		else 	
		{		
			if(!tRunPara.tUartPara[pt->chnl].b101PH)
				return; 
				
			switch(pt->st101)
			{
				case IEC101_ST_LINK:
				{
					if((pd[0]&0x0f)==11) //应答链路状态
					{
						IEC101_State(pt,IEC101_ST_RSTLINK);
						pt->bMSend=false;
					}
					
					break;
				}
				case IEC101_ST_RSTLINK:
				{
					if((pd[0]&0x0f)==0) //应答链路状态
					{
						IEC101_State(pt,IEC101_ST_DATA);
						pt->bMSend=false;
					}
					break;
				}					
				case IEC101_ST_DATA:
				{
					if((pd[0]&0x0f)==0) // 确认应答，调整一级数据缓冲区
					{
						IEC101_AckOneBuf(pt);
						pt->bMSend=false;
					}
					break;
				}
			}
		}
	}
	// 获取GPRS信息
	else if(pt->bCallGprsInf)
	{
		if(g_tRsComm[pt->chnl].bextsend|| pt->btx_buf_s|| pt->btx_buf_m)
			return;  //正在发送数据，不处理
		
		pt->bCallGprsInf=false;
		IEC101_CallGprsInf(pt);
		pt->btx_buf_s = true;
		
	}
	//无有效数据接收，平衡式101处理自动发送数据
	else			
	{
		if(!tRunPara.tUartPara[pt->chnl].b101PH)return; 

		//如果启动了主站发送，必须先处理应答
		if(pt->bMSend) 
		{
			bool bRtn=true;
			// 20秒后，未收到应答报文
			if(ustimer_get_duration(pt->us0_msend) > pRunSet->dT101Resend*USTIMER_SEC)   
			{
				pt->us0_msend=ustimer_get_origin();	
				if(++pt->msend_retries>2)  //重发三次后,重新初始化
				{
					//rt_printf_time("IEC101(%d):启动帧重发错误!\r\n",pt->chnl);
					char log_info[128];
					sprintf(log_info,"IEC101(%d):Restart the frame to make three errors",pt->chnl+1);
					load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
					log_flag.com_err[pt->chnl]  = true;
					
					IEC101_Info_Printf_One(pt);
					IEC101_Reset(pt);
				}
				else
				{
					//需重新组织发送
					pt->btx_buf_m = true;			
				}
				return;
			}

			
#ifdef COMM_STATION_NR
				if(pt->tx_buf[0].tb_head!=pt->tx_buf[0].tb_tail_send)  //有遥控报文要发送
				{
					if(ustimer_get_duration(pt->us0_msend) > 2*USTIMER_SEC)    //且上帧报文发送超过2秒,不等待应道报文，直接发送
					{
						IEC101_AckOneBuf(pt);
						pt->bMSend=false;
						bRtn=false;
					}
				}

				
				if(pt->bTestFrameL||pt->bSoeResume||pt->bTimeSyn)//总召前有SOE续传不需要主站确认
				{
					
					pt->bTestFrameL = false;
					pt->bSoeResume = false;
					pt->bTimeSyn =false;
					IEC101_AckOneBuf(pt);
					pt->bMSend=false;
					bRtn=false;
				}
				
#endif
			if(bRtn)
			{
				return; // 发送，未收到数据，返回
			}

		}
		// 平衡101应答完成
		// 先复位应答相关变量
		pt->msend_retries=0;
		pt->us0_msend=ustimer_get_origin();	

		// 开始主动发送流程
		switch(pt->st101)
		{
			case IEC101_ST_LINK:
			{
				IEC101_MFrameCall(pt,IEC101_CALL_LINK);
				pt->bMFCB=false;
				pt->bMSend=true;
				break;
			}
			case IEC101_ST_RSTLINK:
			{
				IEC101_MFrameCall(pt,IEC101_CALL_RSTLINK);
				pt->bMFCB=false;
				pt->bMSend=true;
				break;
			}				
			case IEC101_ST_DATA:
			{
				pt->bMSend=IEC101_SendOneBuf(pt);
				if(!pt->bMSend) // 无数据 判断
				{
					if(pt->tfile.bSegtransing) // 文件传输，放入一级数据缓冲区
					{
						IEC101_SegmentContinue(pt);
					}
					else if(pt->tf.bTransing)  // 国网新标准文件传输
					{
						iec_readfile_send(pt,false);
					}
					else
					{
						pt->bycsend=IEC101_GetYc(pt);
						if (pt->bycsend)pt->bMSend = TRUE;
					}
				}
				break;
			}
		}

		//有数据要发送
		if(pt->bMSend) 
		{
			if(pt->btx_buf_m)
			{
				//rt_printf_time("IEC101_Applay:bMSend==1,btx_buf_m==1.\r\n");
			}
			pt->bMFCB=!pt->bMFCB;
			pt->btx_buf_m = true;
		}
	}	
}
/**************************************************************************
函数名称：IEC101_SendEvent
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：上送事件记录及变位信息,在RS485定时处理任务(IEC101_AutoTimer)中调用
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

EVENT_STRUCT event_p[64];

u32 IEC101_SendEvent(IEC101_DEF *pt,int is_send_cos) 
{
	u8 type;
	int vsq=0, num_vsp;
	u8 arrCos[200],arrSoe[200];
	u8 arrCos1[200],arrSoe1[200];
	u16 head,cnt;
	u16 soe_no_s,soe_no_d,arg_index[64];
	bool    bFault;	
	FAULT_PARA_BUF *tf=&pt->tfault;
	
	// 主动上送条件:
	// 1、是IEC101_ST_DATA状态
	// 2、没有在处理加密遥控报文
	// 3、不是复位进程状态
	if((pt->st101 != IEC101_ST_DATA) || pt->bRmtSM2 || pt->bResetProcess)
	{
		return 0;
	}

	// 不允许通讯续传的情况下，总召后才允许上送事件
	if(pt->bSendChange == 0)// && pRunSet->bTT_SoeResumeComm == 0)
	{
		return 0;
	}
	
#ifdef FUN_SOE_DELAY_TIME
	if((pt->bfirst_call) && (dTCounter - pt->DTSendChange <= 15*T_1s))
	{
		return 0;
	}
	pt->bfirst_call = false;
#endif

	//一级缓冲区未空，不添加事件记录
	if(pt->tx_buf[0].tb_head != pt->tx_buf[0].tb_tail_send)
	{
		//rt_printf("%s(0):tb_head=%d,tb_tail_send=%d\r\n",__func__,pt->tx_buf[0].tb_head,pt->tx_buf[0].tb_tail_send);
		return 0;
	}
	if(pt->tx_buf[1].tb_head != pt->tx_buf[1].tb_tail_send)
	{
		return 0;
	}

#ifndef IEC_JXYB_DEAL
	if(g_run_stu.bjx)	//检修压板投入，不发送变化的事件记录
	{
		g_soe_queue.tail_send[pt->chnl].n = g_soe_queue.tail_eep.n;	//soe cos
		tf->head = tf->tail;//故障参数
		return 0;
	}
#endif

	//发送级联来的故障参数
	if(tRunPara.bEvPara)
	{
		if(tf->head!=tf->tail)
		{	
			u8 index=tf->tail;
			IEC101_Asdu_Add_sts(pt,&tf->data[index][1],tf->data[index][0],0,3,42,FRAME_I_PRIOR_LOW ,0);
			tf->tail++;
			return 0;
		}
	}	
	// 等SOE保存后再发送，避免通讯确认没有保存的事件
	head = g_soe_queue.tail_eep.n;

	// 一次调用，将事件池中所有事件发送完，
	// 已保证满足"重新连接后，总召前，发送完所有老的SOE,不发送COS"的需求。
	cnt = 0;
	while(1)
	{
		u8 yxtype=0;
		u8 pcosnum=0,pcosnum1=0;
		// 检查发送队列空间,空间应大于1/4，防止发送缓冲区满
		if(IEC101_BufFreeNum(&pt->tx_buf[0]) < (IEC_TBUF_NUM>>2))
		{
			rt_printf("IEC101_SendEvent:BufFreeNum=%d.\r\n",IEC101_BufFreeNum(&pt->tx_buf[0]));
			break;
	  	}

		// 获取一帧报文的事件
		bFault=false;
		num_vsp = asdu_get_event(pt->chnl,head,arrCos,arrSoe,arrCos1,arrSoe1,&soe_no_s,&soe_no_d,0,&bFault,&event_p[0],&arg_index[0],&yxtype,&pcosnum,&pcosnum1);
		if(num_vsp==0)
			break;

		//rt_printf("%s:vsq_s=%d\r\n",__func__,vsq);
		if(pcosnum != 0)
		{
			// 发送COS
			if(is_send_cos&&pt->bSendChange)
			{
				type = 1;
				IEC101_Asdu_Add(pt,arrCos,pcosnum*3,pcosnum,3,type,FRAME_I_COS);
			}			
		}
		
		vsq=(u8)(num_vsp&0xff);
		if(vsq != 0)
		{
			cnt += vsq;			
			// 发送SOE
			type = 30;
			IEC101_Asdu_Add_sts(pt,arrSoe,vsq*10,vsq,3,type,FRAME_I_SOE ,soe_no_s);
		
		}

		if(pcosnum1 != 0)
		{
			// 发送COS
			if(is_send_cos)
			{
				type = 3;
				IEC101_Asdu_Add(pt,arrCos1,pcosnum1*3,pcosnum1,3,type,FRAME_I_COS);
			}					
		}
						
		vsq=(u8)((num_vsp>>8)&0xff);
		//rt_printf("%s:vsq_d=%d\r\n",__func__,vsq);
		if(vsq != 0)
		{
			cnt += vsq;									
			// 发送SOE
			type = 31 ;
			IEC101_Asdu_Add_sts(pt,arrSoe1,vsq*10,vsq,3,type,FRAME_I_SOE ,soe_no_d);
			// rt_printf_time("IEC101_SendEvent:soe_no=%d.\r\n",soe_no);
		}
		vsq=(u8)((num_vsp>>16)&0xff);

		//rt_printf("%s:vsq_e=%d\r\n",__func__,vsq);
		if(vsq>0 && tRunPara.bEvPara)  // 上送故障信息
		{
			int i;
			u16 no;
			u8 SIQ,week;
			struct rtc_time_t rt;

			for(i=0;i<vsq;i++)
			{
				u8 *pd=g_arrIECBuf;
				u8 yc_num;
				no = soe_lp2cp(event_p[i].ev_type,event_p[i].ev_code,event_p[i].ev_arg[1]);
				SIQ = event_p[i].ev_value;
			
				if((yxtype==1) )
				{
					SIQ+=1;	//转为双点信息
				}

				#if  !defined GD_AREA_SHANTOU && !defined GD_AREA_FOS_2020 && !defined FUNC_JX_YC_SIQ00
					if(g_run_stu.bjx)
					{
						SIQ|=0x80;//带品质描述，有效 <1>：=无效
					}
				#endif
				timespec_to_rtc(event_p[i].ts,&rt,1);
				week = WEEK_DAY(event_p[i].ts.tv_sec);

				//if(pRunSet->bTT_faultparaDKY)
				//{			
					yc_num=asdu_get_event_yc(arrSoe,0,arg_index[i]);  // 101规约是6个字节 按照规约规定，101 及104 遥信信息体地址都是两个字节
				//}
				//else
				//{
				//	vsq=asdu_get_event_yc(arrSoe,1,arg_index);  // 101规约是6个字节 按照规约规定，101 及104 遥信信息体地址都是两个字节

				//}
				
				
				*pd++=1; //遥信个数
				*pd++ =( yxtype==1)  ? 31 : 30;		//类型标识		
				*pd++=(u8)no;		// 信息体地址L
				*pd++=(u8)(no>>8);	// 信息体地址H
                if(pRunSet->bTT_faultparaDKY)
                {
                    *pd++=0;			// 信息体地址H2  按照规约规定，101 及104 遥信信息体地址都是两个字节
                }
				*pd++=SIQ;			// 遥信
				*pd++=(u8)rt.ms;
				*pd++=(u8)(rt.ms>>8);
				*pd++=(u8)rt.min;
				*pd++=(u8)rt.hour;
				*pd++=((week<<5) | rt.day);
				*pd++=(u8)rt.month;
				*pd++=(u8)rt.year;			
				*pd++=yc_num; //遥 测个数
				*pd++ =13;		//类型标识	浮点值
				//if(pRunSet->bTT_faultparaDKY) 
				//{			
					memcpy(pd,arrSoe,yc_num*6);	 // 标准规定，遥测点号是2个字节
                    IEC101_Asdu_Add_sts(pt, g_arrIECBuf, yc_num * 6 + (pd - g_arrIECBuf), 0, 3, 42, FRAME_I_PRIOR_LOW, 0);
                //}
                // else
                //{
                //	memcpy(pd,arrSoe,vsq*7);	 // 标准规定，遥测点号是3个字节
                //	IEC101_Asdu_Add_sts(pt,g_arrIECBuf,vsq*7+14,0,3,42,FRAME_I_PRIOR_LOW ,0);
                //}
			}
			
			
		}
		//pt->bSoeRelink=false;
	}

	return cnt;
}



/**************************************************************************
函数名称：IEC101_Recv_Rst
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：复归数据的接受
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


static void IEC101_Recv_Rst(IEC101_DEF *pt)
{

	if(pt->nTypeCounter)
	{
		rt_printf_time("IEC101_Recv_Rst:state=%d,counter=%d\r\n",pt->nTypeCounter,pt->nRecvCounter);


	}
	
	pt->nTypeCounter=0;
	pt->nRecvLenth=0;
	pt->nRecvCounter=0;
}


/**************************************************************************
函数名称：IEC101_AutoTimer
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：101规约链路内容定时处理部分,100毫秒调用1次
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC101_AutoTimer(IEC101_DEF *pt)
{
	// 如果有数据就发送
	IEC101_Send_Polling(pt);
	
	// 配电终端在发送复位进程确认命令后需等待3秒，然后重启进程
	if(pt->bResetProcess && ustimer_get_duration(pt->us0_ResetProcess) > 3*USTIMER_SEC)
	{
		IEC101_Reset(pt);
#ifdef IEC_RESET_PROCESS
		rt_printf("装置重启\r\n");				
		watchdog_reset_cpu(2);	//装置复位
#endif
	}

	// 如果连接没有建立，不处理下面流程
	if(pt->st101 == IEC101_ST_NONE)
	{
		return;
	}

	// 处理加密遥控报文标志超时返回
	if(pRunSet->bTT_SM2)
	{
		if(ustimer_get_duration(pt->us0_SM2Over)>60*USTIMER_SEC)
		{
			pt->bRmtSM2 = false;
		}
	}

	 // 1秒处理任务
	if(ustimer_get_duration(pt->us0_sec) > USTIMER_SEC)
	{
		pt->us0_sec += USTIMER_SEC;
		pt->secs++;

		// 通道检查 默认10分钟没有收到数据，认为通道错误
		if(pt->dValidTime++>pRunSet->dT101Check)  
		{
			char log_info[128];
			rt_printf_time("IEC101(%d):通道超时\r\n",pt->chnl);

			sprintf(log_info,"IEC101(%d):Channel timeout",pt->chnl+1);
			load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
			log_flag.com_err[pt->chnl]  = true;

			IEC101_Info_Printf_One(pt);
			IEC101_Reset(pt);
		}
	}
 	if(pt->tf.bTransing||pt->tf.bdatTraned)  // 文件启动了传输，在3次重发间隔内，未有文件内容传输更新，释放已申请的文件
	{
		if(ustimer_get_duration(pt->tf.us0_file_trans)>(pRunSet->dT101Resend*USTIMER_SEC*3))
		{		
		#ifdef	RCD_STRAN_S
			log_str_time(LOG_OPERATE,"读文件传输超时!!!!!!!",0,1); 		
		#endif
			iec_freefile(&pt->tf);
			pt->tf.bTransing=false;
			pt->tf.bdatTraned=false;
		}
	}
 	if(pt->tf.bdatwriting)  // 文件启动了传输，在3次重发间隔内，未有文件内容传输更新，释放已申请的写文件内存
	{
		if(ustimer_get_duration(pt->tf.us0_file_write)>(pRunSet->dT101Resend*USTIMER_SEC*3))
		{
			if(!pt->tf.writebuf)
			{	
				rt_free(&pt->tf.writebuf);
				pt->tf.writebuf=NULL;
			}
			pt->tf.bdatwriting=false;
		}
	}

 	if(pt->tf.bUpdateReset)  // 文件启动了传输，在3次重发间隔内，未有文件内容传输更新，释放已申请的写文件内存
	{
		if(ustimer_get_duration(pt->tf.us0_updatereset)>(USTIMER_SEC*2))
		{
			rt_printf("101远程升级，装置复位\r\n");
			pt->tf.bUpdateReset=false;
			watchdog_reset_cpu(2);	//装置复位
		}
	}	

 	if(pt->tr.bpreset)  // 参数预置
	{
		if(ustimer_get_duration(pt->tr.us0_preset)>(tRunPara.dSM2Time*USTIMER_SEC))
		{
			int i;
			rt_printf("101预置参数超时清除\r\n");
			pt->tr.bpreset=false;
			for(i=0;i<RMT_SET_NUMBER;i++)
			{
				pt->tr.di[i]=0;
			}			
		}
	}
	
}


static bool IEC101_RmtCheck(IEC101_DEF *pt)  //判断是否是遥控报文
{
	u8 AddByte=0;

	if(!pRunSet->bTT_SM2)return false;  //加密功能未投入
	if(tRunPara.b101Addr2Byte) AddByte=1;	
	
	if((pt->recvbuf[0]==0x68)
		&&(pt->recvbuf[4]&0x40)	
		&&((pt->recvbuf[6+AddByte]==45)||(pt->recvbuf[6+AddByte]==46)))	// 遥控报文
	{
		return true;
	}

	return false;
}


/**************************************************************************
函数名称：IEC101_Recv
函数版本：1.00 
作者：   
创建日期：2008.9.1     
函数功能说明：101规约数据接受判断，只判断是否满足报文格式
输入参数：
输出参数：有完整帧接受，返回1，否则返回 0
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static int  IEC101_Recv(IEC101_DEF *pt,u8 byRevData) 
{
#ifdef FUNC_ENCRY_IN_ONE_SERIAL
	IEC101_DEF *pt1;
	static int iCount=0,enc_tunnel=0;
#endif
	
	// 帧内字符间隔超时检测
	
	if(g_tRsComm[pt->chnl].b_recv_reset)
	{
// 不能复位fifo		rt_fifo_reset(&pt->recv_fifo);
		//IEC101_Recv_Rst(pt);
		g_tRsComm[pt->chnl].b_recv_reset = 0;
	}
	
	

	pt->recvbuf[pt->nRecvCounter++]=byRevData;
	pt->dTRecvPiece=dTCounter;   //接收字符时的时刻，在大循环中判断，是否需要复归

	switch(pt->nTypeCounter)
	{
		case 0:
		//统计接收帧数
		s_stat_rx_frame(pt->chnl);	
		if(byRevData==0x68)
		{
			pt->nTypeCounter=1;
		}
		else if(byRevData==0x10)
		{
			if(!tRunPara.b101Addr2Byte)
			{
				pt->nRecvLenth=2;
			}
			else
			{
				pt->nRecvLenth=3;
			}				
			pt->nTypeCounter=6;
			pt->nRecvCnt=0;	
			pt->sum=0;
		}
		else if(byRevData==0x16) //SM2 报文
		{
			pt->nTypeCounter=20;
			pt->nRecvCounter=256;
			pt->recvbuf[pt->nRecvCounter++]=byRevData;
			pt->recvbuf[0]=0x68;  //恢复被0x16 覆盖的第一字节数据
			if(!pRunSet->bTT_SM2)  //加密功能未投入
			{
				IEC101_Recv_Rst(pt);
				//统计接收错误帧数
				s_stat_rx_errframe(pt->chnl);
			}
		}
	#ifdef FUNC_ENCRY_IN_ONE_SERIAL
		else if(byRevData==0xAA)//海南101规约报文
		{
			pt->nTypeCounter=26;
			pt->sum=0;
		}
	#endif
		else
		{
			rt_printf_time("IEC101_Recv(%d):0x%02x.\r\n",pt->chnl,byRevData);
			IEC101_Recv_Rst(pt);
			//统计接收错误帧数
			s_stat_rx_errframe(pt->chnl);
		}
		break;
		case 1:
			pt->nTypeCounter=2;
			pt->nRecvLenth=byRevData;			//长帧长度
			if(pt->nRecvLenth>IEC101_RECVBUF_MAX)
			{
				IEC101_Recv_Rst(pt);
				//统计接收错误帧数
				s_stat_rx_errframe(pt->chnl);
			}
			break;
		case 2:
			if(pt->nRecvLenth==byRevData)
			{
				pt->nTypeCounter=3;
			}
			else
			{
				IEC101_Recv_Rst(pt);
				//统计接收错误帧数
				s_stat_rx_errframe(pt->chnl);
			}
			break;
		case 3:
			if(byRevData==0x68)
			{
				pt->nTypeCounter=6;
				pt->nRecvCnt=0;
				pt->sum=0;
			}
			else
			{
				IEC101_Recv_Rst(pt);
				//统计接收错误帧数
				s_stat_rx_errframe(pt->chnl);
			}
			break;
			
		//case 4:  //控制域
		//	pt->sum=byRevData;
		//	pt->nTypeCounter=5;
		//	break;
		//case 5: //目标地址
		//		pt->sum+=byRevData;
		//		pt->nRecvCnt=0;
		//		pt->nTypeCounter=6;
		//		if(pt->nRecvLenth==2)//短帧 
		//			pt->nTypeCounter=7;
		//	break;

			
		case 6:	 //有效数据			//有效数据
			if(pt->recvbuf[4]==0x05&&pt->recvbuf[0]==0x68)   // 映翰通 GPRS信息返回报文，同101不同，需特殊处理
			{
				pt->nRecvLenth=0x30+1;
			}
			if(++pt->nRecvCnt>=pt->nRecvLenth)
			{
				pt->nTypeCounter=7;
			}
			pt->sum+=byRevData;
			break;
		case 7:		 //校验和	
			pt->recvsum=byRevData;
				pt->nTypeCounter=8;
			break;
		case 8:  //结束符
			pt->nTypeCounter = 0;
			IEC101_Recv_Rst(pt);
			if(byRevData==0x16)	//校验结束字符,报文接收完毕，
			{
				if((pt->sum != pt->recvsum)&&(pt->recvbuf[4]!=0x05))  // 映翰通 GPRS信息返回报文，同101不同，需特殊处理
				{
					s_stat_rx_errframe(pt->chnl);//统计接收帧错误计数
				}				
				else if(IEC101_RmtCheck(pt))  //是遥控报文
				{
					pt->bData=false;  //应用层暂不处理，等解密完成后处理
					if(pRunSet->bTT_SM2)
					{
						pt->bRmtSM2 = true;
						pt->us0_SM2Over=ustimer_get_origin();
					}
				}
				else
				{
					pt->bData=true;

					if (uart_test_begin)
					{
						uart_test_flag[pt->chnl][1] = 1;
					}

					// 唤醒主循环
					mainloop_wakeup();
					return 1;
				}
			}
			break;
		case 20:  // SM2报文 L2长度
			pt->SM2L2=byRevData;
			pt->nTypeCounter++;				
			break;
		case 21:  // SM2报文 L2长度
			pt->nTypeCounter++;		
			if(byRevData>=pt->SM2L2) 	//L2长度包含L3长度，L3<L2，做一简单校验
			{
				//统计接收错误帧数
				s_stat_rx_errframe(pt->chnl);
				IEC101_Recv_Rst(pt);
			}
			break;
		case 22:
			if(byRevData==0x16)
			{
				pt->nTypeCounter++;		
				pt->nRecvCnt=0;
			}
			else
			{
				//统计接收错误帧数
				s_stat_rx_errframe(pt->chnl);
				IEC101_Recv_Rst(pt);
			}
			break;
		case 23:
			if(++pt->nRecvCnt>=pt->SM2L2)
			{
				pt->nTypeCounter++;		
			}
			break;

		case 24:		 //校验和	
			pt->nTypeCounter++;
			break;
			
		case 25:  //结束符
			pt->nTypeCounter = 0;
			IEC101_Recv_Rst(pt);
			if(byRevData==0x16)	//校验结束字符,报文接收完毕，
			{
				pt->bSM2Data=true;
				mainloop_wakeup();
				return 1;
			}
			else
			{
				s_stat_rx_errframe(pt->chnl);
			}
			break;	

	#ifdef FUNC_ENCRY_IN_ONE_SERIAL
		case 26:	//帧长低字节
			pt->nTypeCounter++;
			pt->nRecvLenth = byRevData;
			pt->sum += byRevData;
			break;

		case 27:	//帧长高字节
			pt->nTypeCounter++;
			pt->nRecvLenth = (byRevData<<8) | pt->nRecvLenth;
			pt->sum += byRevData;
			break;

		case 28:	//加密隧道编号
			pt->nTypeCounter++;
			pt->enc_tunnel = byRevData;
			if(pt->enc_tunnel>1)
			{
				enc_tunnel = pt->enc_tunnel + 1;
			}
			else
			{
				enc_tunnel = pt->enc_tunnel;
			}

			pt->sum += byRevData;
			pt->nRecvCnt=0;
			break;

		case 29:	//数据
			if(++pt->nRecvCnt>=pt->nRecvLenth)
			{
				pt->nTypeCounter++;
			}
			pt->sum += byRevData;
			if(tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC)
			{
				pt1 = (IEC101_DEF *)g_tRsComm[enc_tunnel].ptBuf;
				pt1->recvbuf[iCount] = byRevData;
				pt1->enc_tunnel = pt->enc_tunnel;
				pt1->nRecvLenth = pt->nRecvLenth;
				iCount++;
			}
			break;
		
		case 30:	//校验和	
			pt->recvsum = byRevData;
			pt->nTypeCounter++;
			break;

		case 31:	//帧尾
			pt->nTypeCounter = 0;
			IEC101_Recv_Rst(pt);
			iCount = 0;
			if(byRevData==0x55)	//校验结束字符,报文接收完毕，
			{
				if(pt->sum != pt->recvsum)
				{
					s_stat_rx_errframe(pt->chnl);//统计接收帧错误计数
				}				
				else
				{
					pt1 = (IEC101_DEF *)g_tRsComm[enc_tunnel].ptBuf;
					pt1->bData=true;
					enc_tunnel = 0;
					// 唤醒主循环
					mainloop_wakeup();
					return 1;
				}
			}
			break;
	#endif
		
		default:   
			//统计接收错误帧数
			s_stat_rx_errframe(pt->chnl);
			IEC101_Recv_Rst(pt);
			break;
		}	//类型校验结束			

	return 0;
}	


void  IEC101_Recv_App(IEC101_DEF *pt) 
{
	u8 c;
	// 接收数据
	while(pt->bSM2Data== false && pt->bData == false)
	{
		if(rt_fifo_get(&pt->recv_fifo,&c,1) == 0)
		{
			if(g_tRsComm[pt->chnl].b_recv_reset)
			{
				IEC101_Recv_Rst(pt);
				g_tRsComm[pt->chnl].b_recv_reset = 0;
			}
		
			break;
		}
		if(pRunSet->bTT_ESAM&&(!pt->bMaintainComm))
		{
			int ret;
			ret =  sec_recv(&pt->rx_buf,c,pt->extsendbuf,true);
			if(ret==SEC_FRAME_OK_APP) // 带规约的报文
			{
				pt->bData = 1;
			}
			else if(ret==SEC_FRAME_OK_EXT) //纯安全扩展报文 发送
			{
				IEC101_ext_send(pt,pt->extsendbuf+2,((pt->extsendbuf[0]<<8)+pt->extsendbuf[1]));
			}		
		}
		else
		{
		#ifdef FUNC_ENCRY_IN_ONE_SERIAL
			int ret;
			ret = IEC101_Recv(pt,c);
			if(ret == 1)
				break;
		#else
			IEC101_Recv(pt,c);
		#endif
		}
 	}
}

void  IEC101_Recv_Int(IEC101_DEF *pt,u8 byRevData) 
{

	//记录每个不定长帧报文的接收时刻

	if(!pRunSet->bTT_ESAM||pt->bMaintainComm)
	{	
		if(byRevData == 0x68)
		{
			struct rt_fifo *f;
			f = &pt->recv_fifo;

			// 判断不定帧长报头 0x68 L L 0X68
			if( (f->buffer[(f->in-1)&(f->size - 1)]) == (f->buffer[(f->in-2)&(f->size - 1)])
				&& (f->buffer[(f->in-3)&(f->size - 1)]) == 0x68)
			{
				pt->us0_frame = ustimer_get_origin();
			}
			
		}
	}	
	rt_fifo_put(&pt->recv_fifo,&byRevData,1);
}



int IEC101_PH_GprsInfo(void)
{
	int i;
	
	for(i=0;i<CFG_UART_NUM_MAX;i++)
	{
		if(UART_CHANNEL[i]< 0)
		{
			continue;
		}
		
		//外部通信报文处理
		if(tRunPara.tUartPara[i].wProtocol==PROTOCOL_101_PH)  // 101规约
		{
			IEC101_DEF *pt=(IEC101_DEF *)g_tRsComm[i].ptBuf;
			pt->bCallGprsInf=true;
		}
	}
	return 0;
}

void IEC101_Info_Printf_One(IEC101_DEF *p)
{
	iec_tbuf_t *pt ;
	
	rt_printf("\r\n  通道属性:type=%d,chnl=%d\r\n",p->type,p->chnl);
	if(p->st101 == 0)
	{
		return;
	}
	rt_printf("  规约状态:state=%d,bSendChange=%d\r\n",p->st101,p->bSendChange);
	rt_printf("  连接计时:LinkCount(%d):%d秒\r\n",p->LinkCount,p->secs);
	pt= &p->tx_buf[0];
	rt_printf("发送BUF[0]:head=%d,tail_send=%d,tail_ack=%d\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack);
	pt= &p->tx_buf[1];
	rt_printf("发送BUF[1]:head=%d,tail_send=%d,tail_ack=%d\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack);
	rt_printf("SOE通讯BUF:head=%d,tail_send=%d,tail_ack=%d\r\n",g_soe_queue.head.n,g_soe_queue.tail_send[p->chnl].n,g_soe_queue.tail_ack[p->chnl].n);
	rt_printf("SOE事件BUF:head=%d,tail_send=%d\r\n",
		g_soe_queue.head.n,g_soe_queue.tail_eep.n);
	pt= &p->tx_buf[2];
	rt_printf("发送BUF[1]:head=%d,tail_send=%d,tail_ack=%d\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack);
	rt_printf("SOE通讯BUF:head=%d,tail_send=%d,tail_ack=%d\r\n",g_soe_queue.head.n,g_soe_queue.tail_send[p->chnl].n,g_soe_queue.tail_ack[p->chnl].n);
	rt_printf("SOE事件BUF:head=%d,tail_send=%d\r\n",
		g_soe_queue.head.n,g_soe_queue.tail_eep.n);

	return;
}
 
 
int IEC101_Info_Printf(void)
{
	int i;
	IEC101_DEF *p;

	//串口101
	rt_printf("\r\n串口101:");
	for(i=0;i<CFG_UART_NUM_MAX;i++)	
	{
		// 如果串口不存在，跳过
		if(UART_CHANNEL[i]< 0)
		{
			continue;
		}
		if(tRunPara.tUartPara[i].wProtocol != PROTOCOL_101 && tRunPara.tUartPara[i].wProtocol != PROTOCOL_101_PH&&tRunPara.tUartPara[i].wProtocol != PROTOCOL_MAINTAIN)
		{
			rt_printf("\r\n通道属性:非104通道(chnl=%d)\r\n",i);
			continue;
		}
		
		p = (IEC101_DEF *)g_tRsComm[i].ptBuf;
		IEC101_Info_Printf_One(p);
	}

	return 0;
} 


/************************************************下面是远方修改定值代码******************************************************/

void IEC101_setion_chg_msg(IEC101_DEF *pt101,u8 *ps)
{
#ifdef YPARA_LINK
	WORD section;
	msg_data_struct *msg_data = &temp_par;						
	msg_oper_setion_struct *setion_msg = &msg_data->setion_msg;
	
	section=ps[0]+(ps[1]<<8);	  // 定值区号
	msg_data->sport = pt101->chnl;
	msg_data->dport = 0x1F;			//进行广播
	msg_data->type  = settion_swht;
		
	setion_msg->section_p = 	section;
	mananger_msg_ypara_s(msg_data,msg_data->sport,msg_data->dport);
#endif
}

void IEC101_SectionWrite(IEC101_DEF *pt101,u8 *ps)			//确认帧
{
	u8 *pd;
	int num;
	u8 cot;
	WORD section;
	if(pRunSet->wEquTypeManager)	IEC101_setion_chg_msg(pt101,ps);

	//组包
	section=ps[0]+(ps[1]<<8);	  // 定值区号
	cot=0x47;
	if(section<SEC_NUMBER)
	{
		m_dstsection=section;
		if(mmd_changesection(section)==M_JUMP0)	cot =0x07;
	}	
	//组织报文
	pd=g_arrIECBuf;	
	num=0;
	pd[num++]=0;				//信息体地址
	pd[num++]=0;				//信息体地址
	pd[num++]=ps[0];                    //当前定值区
	pd[num++]=ps[1];                    //当前定值区
	IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,cot,200,FRAME_I);//
	
}

void IEC101_SectionRead(IEC101_DEF *pt101)			// 读定值区
{
	u8 *pd;
	int num;
	u8 cot;
	
	//组织报文
	pd=g_arrIECBuf;	
	num=0;
	pd[num++]=0;			//信息体地址
	pd[num++]=0;			//信息体地址
	pd[num++]=(u8)m_runsection;                    //当前定值区
	pd[num++]=(u8)(m_runsection>>8);                    //当前定值区
	pd[num++]=0;                    //最小定值区
	pd[num++]=0;                    //最小定值区
	pd[num++]=SEC_NUMBER-1;                    //最大定值区	1 标准上是7个字节，暂时认为是2个字节
	pd[num++]=0;                    //最大定值区	2
	
	cot =IEC_COT_ACTCON;		
	IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,cot,201,FRAME_I);//
}


/**************************************************远方修改定值结束*****************************************************/



/***********************************************下面是101规约文件传输部分代码2016-8-10**************************************************/

/**************************************************************************
函数名称：IEC101_Direct_Echo
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：101规约目录响应
输出参数：pt101缓冲区地址，COT 传送原因
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
 
void IEC101_Direct_Echo(IEC101_DEF *pt101,DWORD InfoAddr) 
{
	BYTE *pd=g_arrIECBuf;  //从第7个字节开始
	BYTE vsq;
	int filenum;
	struct dir_file_struct *pdir,*pdirfree;
	int i;

	vsq=0;
	pdir=hf_get_dir_file(IEC_Getdir(InfoAddr),&filenum);	 // 根据信息体地址，判断读读哪个目录，获取目录信息
	if(pdir==NULL)
	{
		IEC101_Asdu_Add(pt101,g_arrIECBuf,0,vsq,5,126, FRAME_I); //无文件应答
		return;
	}
	pdirfree=pdir;
	*pd++ =(BYTE)InfoAddr;
	*pd++ =(BYTE)(InfoAddr>>8);
	for(i=0;i<filenum;i++)
	{	
		struct rtc_time_t rtc;
	        /*文件名称*/
	        *pd++ = (BYTE)(pdir->file_name);
	        *pd++ = (BYTE)(pdir->file_name>>8);

	        /*文件长度*/
	        *pd++ = (BYTE)(pdir->file_size);
	        *pd++ = (BYTE)(pdir->file_size>>8);
	        *pd++ = (BYTE)(pdir->file_size>>16);

	        /*文件的状态SOF*/
	        *pd++ = 0;

	        /*日历时钟*/
	        timespec_to_rtc(pdir->file_time,&rtc,1);		        
	        *pd++  = (BYTE)(rtc.ms);
	        *pd++  = (BYTE)(rtc.ms>>8);
	        *pd++  = (BYTE)(rtc.min);
	        *pd++  = (BYTE)(rtc.hour);
	        *pd++  = (BYTE)(rtc.day);
	        *pd++  = (BYTE)(rtc.month);
	        *pd++  = (BYTE)(rtc.year);
	        vsq++;
		if((vsq>=15)&&i<(pdir->file_size-1))// 目录超出一帧上送范围，分帧，VSQ置后续标准位
		{
			vsq|=0x80;
			IEC101_Asdu_Add(pt101,g_arrIECBuf,(vsq&0x7f)*13+2,vsq,5,126, FRAME_I); //无文件应答
			vsq=0;
			pd=g_arrIECBuf;	
		}
		pdir++;
	}
	rt_free(pdirfree);	
	if(vsq>0)
	{
		if(vsq>1)
		{
			vsq|=0x80;
		}
		IEC101_Asdu_Add(pt101,g_arrIECBuf,(vsq&0x7f)*13,vsq,5,126, FRAME_I); //无文件应答
	}
	else
	{
		IEC101_Asdu_Add(pt101,g_arrIECBuf,0,vsq,5,126, FRAME_I); //无文件应答

	}
	
}

/**************************************************************************
函数名称：IEC101_EchoFileReady
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：应答文件准备好
输出参数：pt101缓冲区地址，filename 文件名
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


static void IEC101_EchoFileReady(IEC101_DEF *pt101,DWORD InfoAddr,WORD filename)
{

	BYTE *pd=g_arrIECBuf;  //从第7个字节开始
	int filelenth,sectionnum;
	filelenth=hf_get_file_inf(IEC_Getdir(InfoAddr),filename,&sectionnum);
        /*文件名称*/

  	*pd++ =(BYTE)InfoAddr;
	*pd++ =(BYTE)(InfoAddr>>8);      
        *pd++ = (BYTE)(filename);  // 10
        *pd++ = (BYTE)(filename>>8); // 11
	*pd++=(BYTE)filelenth;		 // 文件长度
	*pd++=(BYTE)(filelenth>>8);	  //文件长度
	*pd++=(BYTE)(filelenth>>16);	 //文件长度
	if(filelenth>0)
	{
		*pd++ = 0; /*FRQ=0肯定认可*/
	}
	 else
	 {
	      *pd++ = 0x80; /*FRQ=0否定定认可*/
	 }
	pt101->tfile.filelenth=filelenth;
	pt101->tfile.setctionnum=sectionnum;
	pt101->tfile.filename=filename;	 
	pt101->tfile.InfoAddr=InfoAddr;
	pt101->tfile.filechecksum=0;	
	IEC101_Asdu_Add(pt101,g_arrIECBuf,8,1,13,120, FRAME_I); //无文件应答
}

/**************************************************************************
函数名称：IEC101_EchoSectionReady
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：应答节准备好
输出参数：pt101缓冲区地址，filename 文件名
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


static void IEC101_EchoSectionReady(IEC101_DEF *pt101,DWORD InfoAddr,WORD filename,BYTE section)
{
	BYTE *pd=g_arrIECBuf;  //从第7个字节开始
	int setctionlenth;
	char *pdat=NULL;
	IEC101_FreeFile(pt101);	

	*pd++ =(BYTE)InfoAddr;
	*pd++ =(BYTE)(InfoAddr>>8);	
        /*文件名称*/
        *pd++ = (BYTE)(filename);  // 10
        *pd++ = (BYTE)(filename>>8); // 11
	if(pt101->tfile.InfoAddr==InfoAddr&&pt101->tfile.filename==filename&&section<=pt101->tfile.setctionnum)  //文件名相同文件有效
	{
		pdat=hf_get_file_part(IEC_Getdir(InfoAddr),  filename,  section,  &setctionlenth);
	}
	*pd++=section;  //节名
	/*节长度长度*/
	*pd++ = (BYTE)(setctionlenth);  //12
	*pd++ = (BYTE)(setctionlenth>>8); // 13
	*pd++ = (BYTE)(setctionlenth>>16); // 14
	if(pdat==NULL||setctionlenth==0)
	{
		*pd++ = 0x80; /*FRQ=0否定定认可*/
	}
	else
	{
	        *pd++ = 0x0; /*FRQ=0 肯定认可*/ // 15
		pt101->tfile.pdat=pdat;			//保存节内容地址，后续需要释放
		pt101->tfile.sectionlenth=setctionlenth;
		pt101->tfile.transsection=section;
		pt101->tfile.bFiletransing=true;	 
		pt101->tfile.bSegtransing=false;
		pt101->tfile.us0_file_trans=ustimer_get_origin();
		pt101->tfile.transBytes=0;
		pt101->tfile.sectionchecksum=0;
	 }
	IEC101_Asdu_Add(pt101,g_arrIECBuf,9,1,13,121, FRAME_I); //无文件应答
}

/**************************************************************************
函数名称：IEC101_SegmentSend
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：应答节传输中段的数据
输出参数：pt101缓冲区地址，filename 文件名,section 节名
返回值：  

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


static void  IEC101_SegmentSend(IEC101_DEF *pt101,DWORD InfoAddr,WORD filename,BYTE section)
{
	BYTE *pd=g_arrIECBuf;  //从第7个字节开始
	BYTE sectionchecksum=0;  //节校验和
	BYTE segmentbytes;
	BYTE cot;	
	int i;
	cot=13;
	segmentbytes=0;
	if(pt101->tfile.bFiletransing
		&&pt101->tfile.InfoAddr==InfoAddr
		&&pt101->tfile.filename==filename
		&&section==pt101->tfile.transsection)  //文件名相同 节相同
	{
		if(pt101->tfile.transBytes+FILE_SEGMENT_BYTES>=pt101->tfile.sectionlenth)
		{
			segmentbytes=pt101->tfile.sectionlenth%FILE_SEGMENT_BYTES;  // 最后的段的字节数
		}
		else
		{
			segmentbytes=FILE_SEGMENT_BYTES;
		}
		pt101->tfile.sectionchecksum=sectionchecksum;
		pt101->tfile.us0_file_trans=ustimer_get_origin();	  
		pt101->tfile.bSegtransing=true;
	}
	else	
	{
		cot|=0x40;
		segmentbytes=0;
		IEC101_FreeFile(pt101);	
	}

        /*文件名称*/

	*pd++ =(BYTE)InfoAddr;
	*pd++ =(BYTE)(InfoAddr>>8);        
        *pd++ = (BYTE)(filename);  // 10
        *pd++ = (BYTE)(filename>>8); // 11
	*pd++=section;  //节名  12
	*pd++=segmentbytes;  //段的长度 13
	for(i=0;i<segmentbytes;i++)
	{
		*pd=pt101->tfile.pdat[pt101->tfile.transBytes++];
		sectionchecksum+=*pd++;
	}
	IEC101_Asdu_Add(pt101,g_arrIECBuf,6+segmentbytes,1,cot,125, FRAME_I); //无文件应答
}




/**************************************************************************
函数名称：IEC101_EchoLastSection
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：应答最后的节或段
输出参数：pt101缓冲区地址，filename 文件名,section 节名,LSQ限定词
返回值：   

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


static void  IEC101_EchoLastSection(IEC101_DEF *pt101,WORD filename,BYTE section,BYTE LSQ)
{
	BYTE *pd=g_arrIECBuf;  //从第7个字节开始

        /*文件名称*/

	*pd++ =(BYTE)pt101->tfile.InfoAddr;
	*pd++ =(BYTE)(pt101->tfile.InfoAddr>>8);
        *pd++ = (BYTE)(filename);  // 10
        *pd++ = (BYTE)(filename>>8); // 11
	*pd++=section;  //节名  12
	switch(LSQ)
	{
		case 1:
			*pd++=1;						//LSQ=1：不带停止激活文件传输13
			*pd++=pt101->tfile.filechecksum;			//文件校验和 14
			
			break;
		case 3:
			*pd++=3;						//LSQ=3：不带停止激活的节传输
			*pd++=pt101->tfile.sectionchecksum;			//节校验和			
			break;
	}

	IEC101_Asdu_Add(pt101,g_arrIECBuf,7,1,13,123, FRAME_I); //无文件应答
}


/**************************************************************************
函数名称：IEC101_SegmentContinue
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：主循环中继续发送节中剩余的段
输出参数：pt101缓冲区地址，filename 文件名,section 节名
返回值：  

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

void  IEC101_SegmentContinue(IEC101_DEF *pt101)
{
	BYTE *pd=g_arrIECBuf;  //从第7个字节开始
	BYTE sectionchecksum=0;  //节校验和
	BYTE segmentbytes;
	int i;
	
	if(!pt101->tfile.bSegtransing)return;

		if(pt101->tfile.transBytes<pt101->tfile.sectionlenth)  // 段还未传输完毕
		{
		
			if(pt101->tfile.transBytes+FILE_SEGMENT_BYTES>=pt101->tfile.sectionlenth)
			{
				segmentbytes=pt101->tfile.sectionlenth%FILE_SEGMENT_BYTES;  // 最后的段的字节数
			}
			else 
			{
				segmentbytes=FILE_SEGMENT_BYTES;
			}
			
			*pd++ =(BYTE)pt101->tfile.InfoAddr;
			*pd++ =(BYTE)(pt101->tfile.InfoAddr>>8);
		        /*文件名称*/
		        *pd++ = (BYTE)(pt101->tfile.filename);  // 10
		        *pd++ = (BYTE)(pt101->tfile.filename>>8); // 11
			*pd++=pt101->tfile.transsection;  //节名  12
			*pd++=segmentbytes;  //段的长度 13
			for(i=0;i<segmentbytes;i++)
			{
				*pd=pt101->tfile.pdat[pt101->tfile.transBytes++];
				sectionchecksum+=*pd++;
			}
			pt101->tfile.sectionchecksum+=sectionchecksum;
			pt101->tfile.us0_file_trans=ustimer_get_origin();	 
			IEC101_Asdu_Add(pt101,g_arrIECBuf,6+segmentbytes,1,13,125, FRAME_I); //无文件应答			
			
		}
		else
		{
			IEC101_EchoLastSection(pt101,pt101->tfile.filename,pt101->tfile.transsection,3);
			IEC101_FreeFile(pt101);	
		}
}


void IEC101_File_Respone(IEC101_DEF *pt101,BYTE *dat,BYTE cot,BYTE SCAFQ) // 
{
	BYTE *pd=g_arrIECBuf;  //从第7个字节开始
	u8 vsq,type;
	u8 CotByte=0;	//传送原因双字节
	u8 AppByte=0;	//应用单元地址双字节
	
	if(tRunPara.b101Cot2Byte)  CotByte=1;		
	if(tRunPara.b101App2Byte)  AppByte=1;	
	type=dat[0];
	vsq=dat[1];
       *pd++=dat[4+CotByte+AppByte];  // 应用单元地址
       *pd++=dat[5+CotByte+AppByte];  // 
       *pd++=dat[6+CotByte+AppByte];  // 文件名 
       *pd++=dat[7+CotByte+AppByte]; // 
	*pd++=dat[8+CotByte+AppByte];  //节名  12
	*pd++=SCAFQ;  //限定词
	IEC101_Asdu_Add(pt101,g_arrIECBuf,6,vsq,cot,type, FRAME_I); //无文件应答
}


/**************************************************************************
函数名称：IEC101_File_Echo
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：对文件召唤的应答处理
输出参数：pt101缓冲区地址，dat报文缓冲区地址从报文的帧类型开始
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

void IEC101_File_App(IEC101_DEF *pt101,BYTE *dat) //dat 从报文的帧类型开始
{
	BYTE COT,SCQ;  //传送原因
	WORD filename,section; //文件名 节名
	DWORD InfoAddr;       //信息体地址
	u8 CotByte=0;	//传送原因双字节
	u8 AppByte=0;	//应用单元地址双字节
	if(tRunPara.b101Cot2Byte)  CotByte=1;		
	if(tRunPara.b101App2Byte)  AppByte=1;	
	COT=dat[2];
	InfoAddr=dat[4+CotByte+AppByte]+(dat[5+CotByte+AppByte]<<8); 
	filename=dat[6+CotByte+AppByte]+(dat[7+CotByte+AppByte]<<8); //文件名
	section=dat[8+CotByte+AppByte]; //节名
	SCQ = dat[9+CotByte+AppByte]; //选择和召唤限定词

	//rt_printf("\r\n COT=%d addr=%4x filename=%d,section=%d,SCQ=%d",COT,InfoAddr,filename,section,SCQ);

	if(IEC_Getdir(InfoAddr)==NULL)
	{
		IEC101_File_Respone(pt101,dat,(47|0x40),SCQ); // 否定应答，非所希望的通信服务
		return;
	}
	
	if(COT == 5)     /*请求目录 */
	{				
		IEC101_Direct_Echo(pt101,InfoAddr);  /*目录响应 cot =5代表召唤目录*/		
	}
	else if(COT == 13)    
	{
		switch(SCQ)                     
		{
			case 1:                       /*选择文件*/                            
				IEC101_EchoFileReady(pt101,InfoAddr,filename);
				break;                            
			case 2:                       /*请求文件*/       
				IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//节准备好报文
				break;                            
			case 6:                     /*请求节*/         
				IEC101_SegmentSend(pt101,InfoAddr,filename,section);		//发送本节中所有的段
				break;  
			default:
				IEC101_File_Respone(pt101,dat,COT,SCQ|0x30); // 否定应答，非所希望的通信服务
				break;
		}
	}
	else
	{
		IEC101_File_Respone(pt101,dat,(45|0x40),SCQ); // 否定应答，未知的传送原因
	}
}
/**************************************************************************
函数名称：IEC101_File_Echo
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：对文件召唤的应答处理
输出参数：pt101缓冲区地址，dat报文缓冲区地址从报文的帧类型开始
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/

void IEC101_File_Echo(IEC101_DEF *pt101,BYTE *dat) //dat 从报文的帧类型开始
{
	BYTE COT,SCQ;  //传送原因
	WORD filename,section; //文件名 节名
	DWORD InfoAddr;       //信息体地址
	u8 CotByte=0;	//传送原因双字节
	u8 AppByte=0;	//应用单元地址双字节
	if(tRunPara.b101Cot2Byte)  CotByte=1;		
	if(tRunPara.b101App2Byte)  AppByte=1;	
	COT=dat[2];
	InfoAddr=dat[4+CotByte+AppByte]+(dat[5+CotByte+AppByte]<<8); 
	filename=dat[6+CotByte+AppByte]+(dat[7+CotByte+AppByte]<<8); //文件名
	section=dat[8+CotByte+AppByte]; //节名
	SCQ = dat[9+CotByte+AppByte]; //选择和召唤限定词

	//rt_printf("\r\n COT=%d addr=%4x filename=%d,section=%d,SCQ=%d",COT,InfoAddr,filename,section,SCQ);

	if(IEC_Getdir(InfoAddr)==NULL)
	{
		IEC101_File_Respone(pt101,dat,(47|0x40),SCQ); // 否定应答，非所希望的通信服务
		return;
	}
	
	if(COT == 5)     /*请求目录 */
	{				
		IEC101_Direct_Echo(pt101,InfoAddr);  /*目录响应 cot =5代表召唤目录*/		
	}
	else if(COT == 13)    
	{
		switch(SCQ)                     
		{
			case 1:                       /*选择文件*/                            
				IEC101_EchoFileReady(pt101,InfoAddr,filename);
				break;                            
			case 2:                       /*请求文件*/       
				IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//节准备好报文
				break;                            
			case 6:                     /*请求节*/         
				IEC101_SegmentSend(pt101,InfoAddr,filename,section);		//发送本节中所有的段
				break;  
			default:
				IEC101_File_Respone(pt101,dat,COT,SCQ|0x30); // 否定应答，非所希望的通信服务
				break;
		}
	}
	else
	{
		IEC101_File_Respone(pt101,dat,(45|0x40),SCQ); // 否定应答，未知的传送原因
	}
}


/**************************************************************************
函数名称：IEC101_File_Sure
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：对文件处理的确认应答
输出参数：pt缓冲区地址，dat报文缓冲区地址从报文的帧类型开始
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/


void IEC101_File_Sure(IEC101_DEF *pt101,BYTE *dat) //dat 从报文的帧类型开始
{

	BYTE COT,AFQ;  //传送原因
	WORD filename,section; //文件名 节名
	DWORD InfoAddr;       //信息体地址
	u8 CotByte=0;	//传送原因双字节
	u8 AppByte=0;	//应用单元地址双字节
	if(tRunPara.b101Cot2Byte)  CotByte=1;		
	if(tRunPara.b101App2Byte)  AppByte=1;	
	COT=dat[2];
	InfoAddr=dat[4+CotByte+AppByte]+(dat[5+CotByte+AppByte]<<8); 
	filename=dat[6+CotByte+AppByte]+(dat[7+CotByte+AppByte]<<8); //文件名
	section=dat[8+CotByte+AppByte]; //节名
	AFQ = dat[9+CotByte+AppByte]; //选择和召唤限定词

	if(IEC_Getdir(InfoAddr)==NULL)
	{
		IEC101_File_Respone(pt101,dat,(47|0x40),AFQ); // 否定应答，未知的信息体地址
		return;
	}
	if(COT != 13)    
	{				
		IEC101_File_Respone(pt101,dat,(45|0x40),AFQ); // 否定应答，未知的传送原因
	}
	
	switch(AFQ)             /*AFQ文件确认或节确认*/
	{
		case 1:			//文件传输的正确认可					
			IEC101_FreeFile(pt101);	
			IEC101_File_Respone(pt101,dat,COT,AFQ); // 需商讨，文件传输完毕后，对文件的确认，需要上送什么信息合适，以前是上送更新后的目录 sunxi
			break;
		case 2:						//文件传输的否定认可
			IEC101_FreeFile(pt101);	
			IEC101_File_Respone(pt101,dat,COT,AFQ); // 需商讨，文件传输完毕后，对文件的确认，需要上送什么信息合适，以前是上送更新后的目录 sunxi
			break;
		case 3:						//节传输的正确认可

			IEC101_FreeFile(pt101);		
			if(pt101->tfile.transsection<pt101->tfile.setctionnum-1)//现有调试工具UTDEBU，节名从0开始， tfile.setctionnum 需减1 sunxi ,后续程序恢复
			{		
				pt101->tfile.filechecksum+=pt101->tfile.sectionchecksum;	//文件校验和
				section+=1;
				IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//下一节准备好						
			}
			else if(section>=pt101->tfile.setctionnum-1) // //现有调试工具UTDEBU，节名从0开始， tfile.setctionnum 需减1 sunxi,后续程序恢复
			{
				IEC101_EchoLastSection(pt101,filename,section,1);		//LSQ = 3;      最后的段
			}
			break;
		case 4:						//节传输的否定认可
			IEC101_FreeFile(pt101);	
			IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//节准备好报文
			break;
		default:
			break;
	}                            
}

void IEC101_FreeFile(IEC101_DEF *pt101)
{
	if(pt101->tfile.bFiletransing)
	{
		pt101->tfile.bFiletransing=false;
		pt101->tfile.bSegtransing=false;
		if(!pt101->tfile.pdat)
		{
			rt_free(pt101->tfile.pdat);
			pt101->tfile.pdat=NULL;
		}
		rt_printf("\r\nIEC101文件节传输完毕，释放内存，文件:%d 节:%d\r\n",pt101->tfile.filename,pt101->tfile.transsection);
	}	
}



