/********************************************************************
版权所有：	
文件版本： V1.00
文件名称： IEC104.c
生成日期： 2007年04月26日
作    者：xxxxxx
使用范围：
   
功    能： IEC104规约处理
更新信息：
更新日志1：
修改者:
修改日期:
修改内容:
修改原因:
*********************************************************************/
#include "head.h"
#ifdef LINUX_KERNEL_APP
#include <linux/delay.h>
#endif

#define IEC104_printf  rt_printf_time

IEC104_DEF g_t104[IEC104_TOTAL_SOCKETS];/*104规约结构定义*/
static bool g_104_comm_status[IEC104_TOTAL_SOCKETS];

void IEC104_Info_Printf_One(IEC104_DEF *p);


void IEC104_UnPackFrame(IEC104_DEF *pt104,BYTE *pd,int  type)
{

	if(!g_bCommLed)g_bCommLed=true;
	if(g_print_104 && (g_print_port & (1<<pt104->chnl))) 
	{
		if(type == 0)
		{
			print_msg("RX_104:",pd,pd[1]+2);

		}
		else if(type == 1)
		{
			print_msg("TX_104:",pd,pd[1]+2);
		}
		else if(type == 2)
		{
			print_msg("TX_104:",&pd[1],pd[0]);
		}
	}
}
void set_104_link_status(WORD net)
{
	if(net>CFG_ETH_NUM_MAX-1) return ;

	g_104_comm_status[net]=true;

}
void cls_104_link_status(WORD net)
{
	if(net>CFG_ETH_NUM_MAX-1) return ;

	g_104_comm_status[net]=false;

}

bool get_104_link_status(WORD net)
{
	if(net>CFG_ETH_NUM_MAX-1) return 0;

	return g_104_comm_status[net];
}

void IEC104_Init(void)
{
	int i;
	IEC104_DEF *pt104;
	for(i=0;i<CFG_ETH_MAX_LOGIC;i++)
	{
		pt104=&g_t104[i];
		
		memset(pt104,0,sizeof(IEC104_DEF));
		pt104->type=IEC104_TYPE_NET;		
		pt104->chnl=i+CFG_UART_NUM_MAX;
		
		pt104->cNetIndex=i;

		// 初始化接收buf结构
		pt104->RxBuf.chn = pt104->chnl;
		pt104->RxBuf.buf = pt104->arrRecvBuf;		
		rt_fifo_init(&pt104->recv_fifo,pt104->recv_fifo_buf,IEC104_FIFO_SIZE);

		s_stat_protocol(pt104->chnl, PROTOCOL_104);	//统计规约
#ifdef RCD_STRAN_S	
		queue_init(&pt104->rcd_qt.queue,NEWRCD_COUNT,&pt104->rcd_qt.data,NEWRCD_SIZE);
#endif
	}
}

void IEC104_Init_RS(IEC104_DEF *pt104,BYTE chnl)
{
	memset(pt104,0,sizeof(IEC104_DEF));
	pt104->type=IEC104_TYPE_RS;		
	pt104->chnl=chnl;

	// 初始化接收buf结构
	pt104->RxBuf.chn = pt104->chnl;
	pt104->RxBuf.buf = pt104->arrRecvBuf;	
	rt_fifo_init(&pt104->recv_fifo,pt104->recv_fifo_buf,IEC104_FIFO_SIZE);
}

static void IEC104_Reset(IEC104_DEF *pt104)
{
	// 通道属性、连接计数不允许复位
	
	// 规约状态复位
	if(g_comm_link_status & (1<<pt104->chnl))
	{
		char buf[64];
		char log_info[128];
		if(pt104->type == IEC104_TYPE_NET)
		{
			sprintf(buf,"104主站断开(%sIP=%d.%d.%d.%d)!\r\n", get_comm_name(pt104->chnl + 1), pt104->ip[0],pt104->ip[1],pt104->ip[2],pt104->ip[3]);
		}
		else
		{
			sprintf(buf,"104主站断开(%s)!\r\n",get_comm_name(pt104->chnl + 1));
		}
		log_str_time(LOG_OPERATE,buf,0,0);

		sprintf(log_info,"IEC104(%d):104 main station disconnection",pt104->chnl + 1);
		load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,0);
		
		
		g_comm_link_status &= ~(1<<pt104->chnl);
	}

	IEC104_printf("IEC104(%d) reset state: %d => %d,linkcount=%d!\r\n",
		pt104->chnl,pt104->state,0,pt104->LinkCountApp);
		
	pt104->state = 0;
	pt104->bSendChange=false;

	// 序号复位
	pt104->SendNum.ss=0; //发送序列号
	pt104->RecvNum.ss=0; //接收序列号
	pt104->AckNum.ss=0;  //确认序列号
	pt104->AckRecvNum=0;
	// 计时
	pt104->us0_yc = ustimer_get_origin();
	pt104->us0_sec = pt104->us0_yc;
	pt104->secs = 0;
	pt104->us0_client= ustimer_get_origin();


	// 测试状态复位
	pt104->bStartTest=false;
	pt104->us0_test=ustimer_get_origin();	

	// 其它状态复位
	pt104->bRmtSM2=false;
	pt104->bClientStart=false;

	// SOE发送缓冲区复位，发送指针回到ACK的位置
	if(tRunPara.w104K && pRunSet->bTT_SoeResumeComm)
	{
		g_soe_queue.tail_send[pt104->chnl].n=g_soe_queue.tail_ack[pt104->chnl].n;	
	}

	// 遥控信息复位
	ResetRctrl(MASTER_104+pt104->chnl);
	
	// 104发送缓冲区复位
	memset(&pt104->TxBuf,0,sizeof(pt104->TxBuf));
#ifdef	RCD_STRAN_S
	rt_printf_time("-> Link Reset, Clear File Cache\r\n");
	iec_freefile(&pt104->tf);
	pt104->tf.bTransing=false;
	pt104->tf.bdatTraned=false;
#endif
}


// 规约主动关闭连接
static void IEC104_ActiveClose(IEC104_DEF *pt104)
{
	// 复位状态
	IEC104_Reset(pt104);

	// 如果是网络104，直接断开TCP连接
	if(pt104->type != IEC104_TYPE_RS)  
	{	
		net_104_close_flag(pt104->cNetIndex);
	}
}


// 检查是否当前连接的报文
static int IEC104_IsCurFrame(IEC104_DEF *pt104)
{
	int n;

	if(tRunPara.b104Client)return 1;
	// 检查是否当前连接的报文
	n = pt104->LinkCountRecv - pt104->LinkCountApp;
	// 是新连接的报文，直接退出，待重新初始化后再处理
	if(n > 0)
	{
		IEC104_printf("IEC104(%d) Recv new frame: Recv=%d,App=%d!\r\n",
			pt104->chnl,pt104->LinkCountRecv,pt104->LinkCountApp);
		return 0;
	}
	// 是旧报文，直接丢弃不处理
	else if(n < 0)
	{
		IEC104_printf("IEC104(%d) Recv old frame: n=%d,Recv=%d,App=%d!\r\n",
			pt104->chnl,n,pt104->LinkCountRecv,pt104->LinkCountApp);
		pt104->bRecvOk = false;
		pt104->bSM2Data = false;
		return 0;
		
	}
	// 是旧报文，直接丢弃不处理(在没有连接的情况下，只有在串口104下，启动数据传输命令可以处理)
	else if(pt104->state == 0 && !(pt104->type == IEC104_TYPE_RS && pt104->arrRecvBuf[2] == FRAME_U_SATRT))
	{
		IEC104_printf("IEC104(%d) Recv old frame: n=%d,state=%d,type=%d,frame=%02x!\r\n",
			pt104->chnl,n,pt104->state,pt104->type,pt104->arrRecvBuf[2]);
		pt104->bRecvOk = false;
		pt104->bSM2Data = false;
		return 0;
		
	}

	return 1;
}


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

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC104_Recv_Reset(IEC104_DEF *pt104)
{
	pt104->cTypeCounter=0;
	pt104->cRecvCnt=0;
}


static bool IEC104_SM2RmtCheck(IEC104_DEF *pt)  //判断是否是遥控报文
{
	if(!pRunSet->bTT_SM2)return false;  //加密功能未投入
	
	if((pt->arrRecvBuf[0]==0x68) && (pt->arrRecvBuf[1] > 4)
		&&((pt->arrRecvBuf[6]==46) || (pt->arrRecvBuf[6]==45)))	// 遥控报文
	{
		return true;
	}

	return false;
}


/**************************************************************************
函数名称：IEC104_Recv
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：104数据接受，只判断是否满足帧格式
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
int  IEC104_Recv_Normal(IEC104_DEF *pt104,BYTE cRcvData) 
{
	pt104->arrRecvBuf[pt104->cRecvCnt++]=cRcvData;	
	s_stat_rx(pt104->chnl,1);

	switch(pt104->cTypeCounter)
	{
	case 0:
		if( cRcvData==0x68)
		{
			pt104->cTypeCounter=1;
			s_stat_rx_frame(pt104->chnl);//统计接收帧数
			
			memset(&pt104->arrRecvBuf[1],0,30);
		}
		else if(cRcvData==0x16) //SM2 报文
		{
			pt104->cTypeCounter=20;
			pt104->cRecvCnt=256;
			pt104->arrRecvBuf[pt104->cRecvCnt++]=cRcvData;
			pt104->arrRecvBuf[0]=0x68;  //恢复被0x16 覆盖的第一字节数据
			s_stat_rx_frame(pt104->chnl);//统计接收帧数
			
			if(!pRunSet->bTT_SM2)  //加密功能未投入
			{
				IEC104_Recv_Reset(pt104);
				s_stat_rx_errframe(pt104->chnl);//统计接收错误帧数
			}
		}
		else
		{
			IEC104_Recv_Reset(pt104);
			s_stat_rx_err(pt104->chnl,1);//统计接收错误字节数
		}
		break;
	case 1:
			pt104->cRecvLenth=cRcvData+2;
			pt104->cTypeCounter=2;
		break;
		
	case 2:
		if(pt104->cRecvCnt>=pt104->cRecvLenth)	
		{
			// 报文接受完毕
			IEC104_Recv_Reset(pt104);
			
			if(IEC104_SM2RmtCheck(pt104))
			{
				// 如果是加密遥控报文，应用层暂不处理，等解密完成后处理
				pt104->bRmtSM2 = true;
				pt104->us0_SM2Over=ustimer_get_origin();
			}
			else
			{
				// 接收到一帧普通报文：
				// 1、设置普通帧接收标志
				// 2、唤醒主循环
				pt104->LinkCountRecv = pt104->LinkCountNet;
				pt104->bRecvOk=true;
				mainloop_wakeup();
				return 1;
			}
		}
		break;

	case 20:  // SM2报文 L2长度
		pt104->SM2L2=cRcvData;
		pt104->cTypeCounter++;	
		break;
	case 21:  // SM2报文 L2长度
		pt104->cTypeCounter++;		
		if(cRcvData>=pt104->SM2L2) 	//L2长度包含L3长度，L3<L2，做一简单校验
		{
			s_stat_rx_errframe(pt104->chnl);//统计接收帧数
			IEC104_Recv_Reset(pt104);
		}
		break;
	case 22:
		if(cRcvData==0x16)
		{
			pt104->cTypeCounter++;		
			pt104->csm2RecvCnt=0;
		}
		else
		{
			s_stat_rx_errframe(pt104->chnl);//统计接收帧数
			IEC104_Recv_Reset(pt104);
		}
		break;
	case 23:
		if(++pt104->csm2RecvCnt>=pt104->SM2L2)
		{
			pt104->cTypeCounter++;		
		}
		break;

	case 24:		 //校验和	
		pt104->cTypeCounter++;
		break;
		
	case 25:  //结束符
		IEC104_Recv_Reset(pt104);
		if(cRcvData==0x16)	//校验结束字符,报文接收完毕，
		{
			// 接收到一帧加密报文
			// 1、设置加密帧接收标志
			// 2、唤醒主循环
			pt104->LinkCountRecv = pt104->LinkCountNet;
			pt104->bSM2Data=true;
			mainloop_wakeup();
			return 1;
		}
		else
		{
			s_stat_rx_errframe(pt104->chnl);//统计接收帧数
		}
		break;			
	default:		
		s_stat_rx_errframe(pt104->chnl);//统计接收帧数
		IEC104_Recv_Reset(pt104);
		break;
	}

	return 0;
}

void  IEC104_Recv_Int(IEC104_DEF *pt,u8 byRevData) 
{	
	rt_fifo_put(&pt->recv_fifo,&byRevData,1);
}

void  IEC104_Recv_App(IEC104_DEF *pt) 
{
	u8 c;
	// 接收数据
	while(pt->bSM2Data== false && pt->bRecvOk == false)
	{
		if(rt_fifo_get(&pt->recv_fifo,&c,1))
		{
			//rt_printf("----[%02x]",c);
			IEC104_Recv(pt,c);		
		}
		else
		{			
			break;
		}		
 	}	
}


int  IEC104_Recv(IEC104_DEF *pt104,BYTE cRcvData) 
{
	int ret;
	if(pRunSet->bTT_ESAM)
	{
		ret =  sec_recv(&pt104->RxBuf,cRcvData,pt104->extSendBuf,false);
		if(ret==SEC_FRAME_OK_APP) // 带规约的报文
		{
			pt104->bRecvOk = 1;				
			pt104->LinkCountRecv = pt104->LinkCountNet;
		}
		else if(ret==SEC_FRAME_OK_EXT) //纯安全扩展报文 发送
		{
			net_104_send_ext(pt104->cNetIndex,pt104->extSendBuf+2, ((pt104->extSendBuf[0]<<8)+pt104->extSendBuf[1]));
		}		
	}
	else
	{
		ret = IEC104_Recv_Normal(pt104,cRcvData);
	}

	return ret;
}


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

static u16 IEC104_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) 
	{
		IEC104_printf("IEC104: IEC104_GetOneBuf error:head=%d,send=%d,ack=%d.\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack);
	}

	return head;
}


static void IEC104_UpdateCOS(u8 *pd)
{
	u8 sq,num,i,ti;
	u16 cp;
	u8 SIQ;
	ti=pd[0];
	sq = pd[1]&0x80;
	num = pd[1]&0x7f;
	cp = pd[6] | (pd[7]<<8);
	pd += 9;
	
	
		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 += 3;
			}
			else
			{
				cp++;
			}
		}
	
	
	
	
	
}
  
static void IEC104_Send_Polling(IEC104_DEF *pt104)
{
	u16	i,k,len,frm_attr;	
	u8 *pd;
	iec_tbuf_t *pt;	

	// 运行到这，如果有数据就无条件发送
	for(i=0;i<2;i++)
	{
		pt = &pt104->TxBuf[i];
		while(pt->tb_head != pt->tb_tail_send)  
		{
			// 如果是串口以太网，中断还有上帧数据没有发送完，直接返回。
			if(pt104->type == IEC104_TYPE_RS) 
			{
				if(g_tRsComm[pt104->chnl].bSend)
				{
					return;
				}
			}
			
			frm_attr = pt->tb_info[pt->tb_tail_send].frm_attr;
			len = pt->tb_data[pt->tb_tail_send][0];
			if((frm_attr&0xff) == FRAME_I)
			{
				// 检查状态
				if(pt104->state != IEC104_ST_STARTED)
				{
					return;
				}

				// 检查K值，k为待确认帧数，如果大于等于设定值就不再发送
				k = pt104->SendNum.ss-pt104->AckNum.ss;
				if(tRunPara.w104K && k >= tRunPara.w104K)
				{
					return;		
				}
				
				// 完善6字节帧头
				pd=&pt->tb_data[pt->tb_tail_send][1];
				*pd++=0x68;
				*pd++=(BYTE)(len-2);
				CopySwap(pd,pt104->SendNum.tt,2,true);//发送序列号
				*pd = (*pd)&0xfe; 
				pd += 2;
				
				CopySwap(pd,pt104->RecvNum.tt,2,true);//接收序列号
				*pd = (*pd)&0xfe; 
				pd += 2;
	
				pt104->AckRecvNum=pt104->RecvNum.ss;

				// 更新低优先级的COS
				if((frm_attr&(FRAME_I_PRIOR_LOW|FRAME_I_COS)) == (FRAME_I_PRIOR_LOW|FRAME_I_COS))
				{
					IEC104_UpdateCOS(pd);
				}

				// 更新发送序号
				pt104->SendNum.ss += 2;
				
				// 保存报文的发送帧号，为待发帧的序号，放在序号加之后
				pt->tb_info[pt->tb_tail_send].tb_SendNum = pt104->SendNum.ss;
			}

			// 真正发送
			pd=&pt->tb_data[pt->tb_tail_send][1];
			if(pt104->type != IEC104_TYPE_RS)   //非串口以太网
			{
				net_104_send(pt104->cNetIndex,&pt->tb_data[pt->tb_tail_send][1] ,len);
			}
			else
			{						
				pd=g_tRsComm[pt104->chnl].sendbuf;
				memcpy(g_tRsComm[pt104->chnl].sendbuf ,pt->tb_data[pt->tb_tail_send],len + 1);  //拷贝数据	
				
				RS_Send_Enable(pt104->chnl);	
				g_tRsComm[pt104->chnl].nSendCnt=1;   // 发送缓冲区的第一个字节为发送长度
				g_tRsComm[pt104->chnl].bSend=true;	

			#ifdef FUN_FUXI_ESAM
				if(UART_CHANNEL[pt104->chnl]==CFG_UART_CHIP_S1 )
				{
					write_chip_s1(&pt->tb_data[pt->tb_tail_send][1], len);	
					g_tRsComm[pt104->chnl].bSend=false;
				}		
			#endif
			}

			// T1 超时计时开始点
			pt->tb_info[pt->tb_tail_send].us0 = ustimer_get_origin();

			// 亮通讯灯和打印报文
			IEC104_UnPackFrame(pt104,pt->tb_data[pt->tb_tail_send],2);
			s_stat_tx_frame(pt104->chnl);//统计发送帧数	
			s_stat_tx(pt104->chnl,len);//统计发送数	

			// 更新发送尾指针
			pt->tb_tail_send++;

#if 1
			// 如果不检查ACK,取消确认功能，不能投入SOE通讯续传和SOE断电续传。
			if(tRunPara.w104K == 0)
			{
				pt->tb_tail_ack = pt->tb_tail_send;
			}
#endif			
		}
	}
}


static int  IEC104_Ack(IEC104_DEF *pt104)
{
	s16 i,n;
	u16 ack_new,ack_old;
	iec_tbuf_t *pt;

#if 1
	// 如果系统不能检查ACK，就不能正确的进行ACK确认，直接返回。
	if(tRunPara.w104K == 0)
	{
		return 0;
	}
#endif

	// ACK序号必须在pt104->SendNum之前。
	// 如果ack序号大于发送序号，出错，主动关闭
	n = pt104->SendNum.ss - pt104->AckNum.ss;
	if(n < 0)// 根据C语言算法，此差值判断在两个值距离小于32768时有效
	{
		char log_info[128];
		IEC104_printf("IEC104(%d) Reset: 报文接收帧号错误(AckNum=%d,SendNum=%d)!\r\n",pt104->chnl,pt104->AckNum.ss,pt104->SendNum.ss);

		sprintf(log_info,"IEC104(%d):Message receiving frame number error(AckNum=%d,SendNum=%d)",pt104->chnl+1,pt104->AckNum.ss,pt104->SendNum.ss);
		load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
		log_flag.com_err[pt104->chnl]  = true;

		return -1;
	}

	// 循环搜索所有没有确认的帧
	ack_old = g_soe_queue.tail_ack[pt104->chnl].n;
	ack_new = ack_old;
	for(i=0;i<2;i++)
	{
		pt = &pt104->TxBuf[i];
		while(pt->tb_tail_ack != pt->tb_tail_send)
		{
			n = pt104->AckNum.ss - pt->tb_info[pt->tb_tail_ack].tb_SendNum;
			if(n >= 0)
			{
				// 如果是SOE报文，需确认SOE缓冲池中SOE.
				if(pt->tb_info[pt->tb_tail_ack].frm_attr&FRAME_I_SOE)
				{
					ack_new = pt->tb_info[pt->tb_tail_ack].soe_tail_send;
					soe_ack(pt104->chnl,ack_new);
				}

				// 更新tail_ack
				pt->tb_tail_ack++;
			}
			else
			{
				break;
			}
		}

		
	}

#if 0
	if(ack_new != ack_old)
	{
		sbi_t cnt_send,cnt_remain;
		cnt_send.n = ack_new - ack_old;
		cnt_remain.n = g_soe_queue.tail_send[pt104->chnl].n - g_soe_queue.tail_ack[pt104->chnl].n;
		IEC104_printf("IEC104_Ack:cnt_send=%d,cnt_remain=%d,head=%d,send=%d,ack=%d.\r\n",
			cnt_send.n,cnt_remain.n,g_soe_queue.head.n,g_soe_queue.tail_send[pt104->chnl].n,g_soe_queue.tail_ack[pt104->chnl].n);
	}
#endif

	// K值可能发生变化，尝试发送报文
	IEC104_Send_Polling(pt104);

	return 0;
}
 

// 发送带soe_tail_send指针
static void IEC104_Send_sts(IEC104_DEF *pt104,UINT frm_attr,UINT len,u16 soe_tail_send)
{
	u16 head;
	iec_tbuf_t *pt;	

	// 检查状态	
	if(pt104->state == 0)
	{
		return;
	}

	// 检查长度，由于串口BUF只有255长，所以是255.
	len += 6;
	if(len > 255)
	{
		char log_info[128];
		IEC104_printf("IEC104(%d) IEC104_AddToBuf: 帧太长(len=%d)!\r\n",pt104->chnl,len);

		sprintf(log_info,"IEC104(%d):The frame is too long(len=%d)",pt104->chnl+1,len);
		load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
		log_flag.com_err[pt104->chnl]  = true;

		return;
	}

	pt=(frm_attr&FRAME_I_PRIOR_LOW) ? &pt104->TxBuf[1] : &pt104->TxBuf[0];

	// 保存数据
	head=IEC104_GetOneBuf(pt);
	pt->tb_info[head].frm_attr = frm_attr;
	pt->tb_info[head].soe_tail_send = g_soe_queue.tail_send[pt104->chnl].n;
	pt->tb_data[head][0] = len;
	memcpy(&pt->tb_data[head][1] ,pt104->arrSendBuf,len);  

	// 直接尝试发送报文
	IEC104_Send_Polling(pt104);

	// 复位T3定时器(测试帧超时)
	pt104->us0_test=ustimer_get_origin();
}

static void IEC104_Send(IEC104_DEF *pt104,UINT frm_attr,UINT len)
{
	IEC104_Send_sts(pt104,frm_attr,len,0);
}

void IEC104_SM2Send(IEC104_DEF *pt104)
{
	u16 head;
	iec_tbuf_t *pt=&pt104->TxBuf[0];	
	int len=pt104->arrSendBuf[0];
	

	if(pt104->type != IEC104_TYPE_RS)  //非串口以太网
	{
		net_104_send(pt104->cNetIndex,&pt104->arrSendBuf[1],len);
		IEC104_UnPackFrame(pt104,pt104->arrSendBuf,2);
		s_stat_tx_frame(pt104->chnl);
		s_stat_tx(pt104->chnl,len);//统计发送数	
	}
	else
	{
		// 保存数据
		head=IEC104_GetOneBuf(pt);
		pt->tb_info[head].frm_attr = FRAME_SM2;
		pt->tb_data[head][0] = len;
		memcpy(&pt->tb_data[head][1], &pt104->arrSendBuf[1],len);  
 	}
}



/**************************************************************************
函数名称：IEC104_Echo
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：IEC104应答帧响应
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC104_Echo_US(IEC104_DEF *pt104,BYTE frametype)
{
	BYTE *pd=pt104->arrSendBuf;
	*pd++=0x68;
	*pd++=4;
	*pd++=frametype;
	*pd++=0;
	if(frametype==FRAME_S)
	{
		CopySwap(pd,pt104->RecvNum.tt,2,true);//接收序列号
		*pd = (*pd)&0xfe; 
		pd += 2;	
		pt104->AckRecvNum=pt104->RecvNum.ss;
	}
	else
	{
		*pd++=0;
		*pd++=0;
	}		

	if(pt104->type != IEC104_TYPE_RS)  // 以太网，直接发送
	{
		net_104_send(pt104->cNetIndex,pt104->arrSendBuf,6);
		IEC104_UnPackFrame(pt104,pt104->arrSendBuf,1);
		s_stat_tx_frame(pt104->chnl);
		s_stat_tx(pt104->chnl,6);//统计发送数	
	}
	else								//串口以太网，将数据放入缓冲器
	{
		IEC104_Send(pt104,frametype,0);
	}
}

static void IEC104_Echo_I(IEC104_DEF *pt104,BYTE cot,bool brst)
{
	BYTE *ps=pt104->arrRecvBuf;
	BYTE *pd=pt104->arrSendBuf;
	int len;

	len = ps[1]+2;
	memcpy(pd,ps,len);
	pd[8]  = cot;
	pd[10] =(BYTE)tRunPara.byAddr;		//应用服务数据单元地址低 
	pd[11] =(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址高

#if defined GD_AREA_ZHONGSHAN || defined GD_AREA_ZHONGSHAN_2020
		if((cot&0x40)&&brst)//否定应答
		{
			IEC104_ActiveClose(pt104);
		}
		else
		{
			IEC104_Send(pt104,FRAME_I,len-6);
		}

	#else
		IEC104_Send(pt104,FRAME_I,len-6);

	#endif

}

static void IEC104_Echo_I_Direct(IEC104_DEF *pt104,BYTE cot)
{
	BYTE *ps=pt104->arrRecvBuf;
	BYTE *pd=pt104->arrSendBuf;
	int len;

	len = ps[1]+2;
	memcpy(pd,ps,len);

	CopySwap(&pd[2],pt104->SendNum.tt,2,true);//发送序列号
	pd[2] &= 0xfe; 

	CopySwap(&pd[4],pt104->RecvNum.tt,2,true);//接收序列号
	pd[4] &= 0xfe; 
	
	pt104->AckRecvNum=pt104->RecvNum.ss;	
	// COT
	pd[8] = cot;

	// 地址
	pd[10] =(BYTE)tRunPara.byAddr;		//应用服务数据单元地址低 
	pd[11] =(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址高

	if(pt104->type != IEC104_TYPE_RS)  // 以太网，直接发送
	{
		net_104_send(pt104->cNetIndex,pt104->arrSendBuf,len);
		IEC104_UnPackFrame(pt104,pt104->arrSendBuf,1);
		s_stat_tx_frame(pt104->chnl);
		s_stat_tx(pt104->chnl,len);//统计发送数	
	}
	else								//串口以太网，将数据放入缓冲器
	{
		IEC104_Send(pt104,FRAME_I,len-6);
	}
}



/**************************************************************************
函数名称：IEC104_Timer
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：定时处理函数，判断socekt超时
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void IEC104_Timer(IEC104_DEF *pt104)
{	
	int i;
	iec_tbuf_t *pt;

#if 0
	if(pt104->secs > 10)
	{
		IEC104_printf("IEC104(%d) Reset: 测试10秒关闭!\r\n",pt104->chnl);
		IEC104_ActiveClose(pt104);
		return;
	}
#endif

	// 检查I帧超时
	if(tRunPara.w104K )
	{
		for(i=0;i<2;i++)
		{
			pt = &pt104->TxBuf[i];
			while(pt->tb_tail_ack != pt->tb_tail_send)
			{
				if((pt->tb_info[pt->tb_tail_ack].frm_attr&0xff) == FRAME_I)
				{
					if(ustimer_get_duration(pt->tb_info[pt->tb_tail_ack].us0) > (tRunPara.w104AckTime*USTIMER_SEC))
					{
						char log_info[128];
						
						IEC104_printf("IEC104(%d) Reset: 发送I帧确认超时!\r\n",pt104->chnl);
						IEC104_ActiveClose(pt104);

						sprintf(log_info,"IEC104(%d):Send I frame confirmation timeout",pt104->chnl+1);
						load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
						log_flag.com_err[pt104->chnl]  = true;

						return;
					}
					break;
				}
				else
				{

					// 不是I帧，不需要确认(在串口模式下，发送队列中有U帧)
					pt->tb_tail_ack++;
				}
			}
		}
	}

	// 检查测试帧超时
	if(pt104->bStartTest)//启动了
	{
		if(ustimer_get_duration(pt104->us0_test_wait)>(tRunPara.w104AckTime*USTIMER_SEC))
		{
			char log_info[128];
			// 测试帧没有及时返回，主动关闭连接
			IEC104_printf("IEC104(%d) 测试帧超时!\r\n",pt104->chnl);
			IEC104_ActiveClose(pt104);

			sprintf(log_info,"IEC104(%d):Test frame timeout",pt104->chnl+1);
			load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
			log_flag.com_err[pt104->chnl]  = true;
						
			return;
		}
	}
	else if(ustimer_get_duration(pt104->us0_test) > (tRunPara.w104TestTime*USTIMER_SEC))//超时测试
	{
		if(pt104->state == IEC104_ST_STARTED) //测试帧在收到启动命令后发送
		{
			// 发送测试帧
			IEC104_Echo_US(pt104,FRAME_U_TEST);	
			pt104->bStartTest=true;
		}
		pt104->us0_test=ustimer_get_origin();
		pt104->us0_test_wait=ustimer_get_origin();
	}

	IEC104_SegmentContinue(pt104);
	if(pt104->tfile.bFiletransing)
	{
		if(ustimer_get_duration(pt104->tfile.us0_file_trans)>(tRunPara.w104AckTime*USTIMER_SEC*5))
		IEC104_FreeFile(pt104);
	}
	
	//新国网标准文件传输处理
	if(pt104->tf.bTransing)  
	{
		// 检查发送队列空间,空间应大于1/4，防止发送缓冲区满
		if(IEC104_BufFree(&pt104->TxBuf[1]) >= (IEC_TBUF_NUM>>2))
		{
			iec_readfile_send(pt104,true);
		}		
	}
	if(pt104->bEchosecmd5)
	{
		u8 md5buf[24];
		u16 len;
		len=sec_md5_echo(md5buf);
		net_104_send_ext(pt104->cNetIndex,md5buf,len);
		pt104->bEchosecmd5=false;
	}
	if(pt104->tf.bTransing||pt104->tf.bdatTraned)
	{
		if(ustimer_get_duration(pt104->tf.us0_file_trans)>(tRunPara.w104AckTime*USTIMER_SEC*5))
		{
#ifdef	RCD_STRAN_S
			log_str_time(LOG_OPERATE,"读文件传输超时!!!!!!!",0,1);		
#endif
			iec_freefile(&pt104->tf);
			pt104->tf.bTransing=false;
			pt104->tf.bdatTraned=false;
		}
	}

 	if(pt104->tf.bdatwriting)  // 文件启动了传输，在3次重发间隔内，未有文件内容传输更新，释放已申请的写文件内存
	{
		if(ustimer_get_duration(pt104->tf.us0_file_write)>((tRunPara.w104AckTime*USTIMER_SEC*5)))
		{
			if(!pt104->tf.writebuf)
			{
				rt_free(&pt104->tf.writebuf);
				pt104->tf.writebuf=NULL;
			}
			pt104->tf.bdatwriting=false;
		}
	}	

 	if(pt104->tf.bUpdateReset)  // 文件启动了传输，在3次重发间隔内，未有文件内容传输更新，释放已申请的写文件内存
	{
		if(ustimer_get_duration(pt104->tf.us0_updatereset)>(USTIMER_SEC*2))
		{
			rt_printf("104远程升级，装置复位\r\n");
			pt104->tf.bUpdateReset=false;
			watchdog_reset_cpu(2);	//装置复位
		}
	}	
 	if(pt104->tr.bpreset)  // 参数预置
	{
		if(ustimer_get_duration(pt104->tr.us0_preset)>(tRunPara.dSM2Time*USTIMER_SEC))
		{
			rt_printf("104预置参数超时清除\r\n");
			pt104->tr.bpreset=false;
			for(i=0;i<RMT_SET_NUMBER;i++)
			{
				pt104->tr.di[i]=0;
			}
			
		}
	}	
}

/**************************************************************************
函数名称：IEC104_InitOK
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：子站初始化完毕
输入参数：
输出参数：
返回值：

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


static void IEC104_InitOK(IEC104_DEF *pt104)
{	
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始

	*pd++=70;		//总召唤确认帧	  1
	*pd++=1;		//可变结构限定词
	*pd++=IEC_COT_INIT;	//传送原因：被初始化
	*pd++=0;		//传送原因：时间确认
	*pd++=(BYTE)tRunPara.byAddr;	//应用服务数据单元地址
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址
	*pd++=0;		//信息体地址L
	*pd++=0;		//信息体地址H
	*pd++=0;		//信息体地址H
	if(pt104->bInitCOI)
	{
		*pd++=2; 	//远方复位
	}
	else
	{
		*pd++=0; 	//当地电源合上
	}
	pt104->bInitCOI=true;
	
	IEC104_Send(pt104,FRAME_I,10);
}



/**************************************************************************
函数名称：IEC104_SetTime
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：时间同步
输入参数：
输出参数：
返回值：

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


static void IEC104_SetTime(IEC104_DEF *pt104,BYTE *ps,BYTE scot)
{	
	TIME ct;
	int check_time=false;
	u8 cot;
	struct timespec ts;	
#ifdef GD_AREA_DONGGUAN//东莞局要求返回设置后的时间
	u8 week;
#endif
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始

	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)
		{
			sys_time_set_rmt(&ct);
		}
	}
	else  // 时钟读取命令上送终端时间，设置命令上送下发命令时间
	{
		clk_time_get(&ts);
		timespec_to_rtc(ts,&ct,1);	
	}

	if(scot==IEC_COT_REQ)
	{
		cot=0x05;
	}
	else
	{ 	
		cot = check_time ? 0x07:0x47;	//对时内容非法，即将传送原因的P/N位置为1
	}
#ifdef GD_AREA_DONGGUAN//东莞局要求返回设置后的时间
		ustimer_delay(1600);
		clk_time_get(&ts);
		timespec_to_rtc(ts,&ct,1);	
		// 得到星期
		rtc_to_timespec(&ct, &ts);
		week = WEEK_DAY(ts.tv_sec);	
#endif
	*pd++=103;		//时钟同步
	*pd++=1;		//可变结构限定词
	*pd++=cot;	//传送原因：被初始化
	*pd++=0;		//传送原因：时间确认
	*pd++=(BYTE)tRunPara.byAddr;	//应用服务数据单元地址
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址
	*pd++=0;		//信息体地址L
	*pd++=0;		//信息体地址H
	*pd++=0;		//信息体地址H
#if defined GD_AREA_ZHONGSHAN || defined GD_AREA_ZHONGSHAN_2020
	if(scot==IEC_COT_ACT)
	{
		*pd++=ps[0];    //MsL
		*pd++=ps[1];    //MsH
		*pd++=ps[2];    //Min
		*pd++=ps[3];    //hour
		*pd++=ps[4]; //Day
		*pd++=ps[5];    //month
		*pd++=ps[6];    //year
	}
	else
	{
		*pd++=(u8)ct.ms;    //MsL
		*pd++=(u8)(ct.ms>>8);  //MsH
		*pd++=ct.min;    //Min
		*pd++=ct.hour;    //hour
		*pd++=ct.day;    //Day
		*pd++=ct.month;    //month
		*pd++=ct.year;    //year
	}
#else
	*pd++=(u8)ct.ms;				//MsL
	*pd++=(u8)(ct.ms>>8);		//MsH
	*pd++=ct.min;				//Min
	*pd++=ct.hour;				//hour
#ifdef GD_AREA_DONGGUAN
	*pd++=((week<<5) | ct.day); //Day
#else
	*pd++=ct.day;				//Day
#endif
	*pd++=ct.month;				//month
	*pd++=ct.year;				//year
#endif 
	IEC104_Send(pt104,FRAME_I,16);
}
/**************************************************************************
函数名称：IEC104_CallAll
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：响应总查询
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
static void IEC104_CallAll(IEC104_DEF *pt104,BYTE QOI)
{
	int num;
	BYTE *pd;
	int i,begin,end;	
	WORD cp=0;
	long lmeaval=0; 
	
	//总召唤确认帧	
	IEC104_Echo_I(pt104,IEC_COT_ACTCON,false);

	// 遥测帧
	if((QOI == IEC_QOI_CALL_ALL_GROUP) || (QOI >= IEC_QOI_CALL_YC_BEGIN && QOI <= IEC_QOI_CALL_YC_END))
	{
		int maxsendnum;
		int ycvalue;
		BYTE frametype;
		union{

			float ff;
			BYTE tt[4];
		}ff;	
		if(pRunSet->bDIContinue)  //点号连续上送
		{
			pd=&pt104->arrSendBuf[15];	
			if(tRunPara.bYcFloat) 	maxsendnum=40;  // 浮点值字数 4+1  
			else maxsendnum=70;		 //非 浮点值字数 2+1
		}
		else
		{
			pd=&pt104->arrSendBuf[12];	
			if(tRunPara.bYcFloat) 	maxsendnum=25; // 浮点值字数 4+1+3  浮点4字节，描述符1字节，信息体3字节
			else maxsendnum=35;		//非 浮点值字数 2+1+3
		}

		if(tRunPara.bYcBDH)  //标度化值
		{
			frametype=11;			//带品质描述的遥测帧  1
		}
		else if(tRunPara.bYcFloat)  //浮点值
		{
			frametype=13;			//带品质描述的遥测帧  1
		}
		else
		{
			frametype=9;			//带品质描述的遥测帧  1
		}	

		// 确认起止点号
		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;
			}
		}
		
		num=0;
		if(g_ac_table)
		{
			cp=g_ac_table[begin].cp;
			
		}
		for(i=begin;i<end;i++)
		{
			BYTE  QDS=0;	//品质描述， 默认为零
			u8  link_qds=0;
			BYTE no=g_ac_table[i].indexno;	
			if(no>0)
			{		
				DWORD dwMax;
				DWORD dwDeadVal;
				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(pt104->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;		//遥测溢出
						}
					}
				}
			}
			else		//备用点号，数值上送0
			{
				ycvalue=0;
				ff.ff=0;
			}

			if(g_run_stu.bToolRmtTest||g_run_stu.bHmiRmtTest) //在测试模式下，置无效状态
			{
				if(pRunSet->bTT_RmtTest)QDS|=0x80;
			}
			if(i<MAX_YC_NUMBER)pt104->yc_save[i]=lmeaval;				
			if(!pRunSet->bDIContinue)  //
			{
				cp=g_ac_table[i].cp;
				*pd++=(BYTE)cp;
				*pd++=(BYTE)(cp>>8);
				*pd++=0;			//信息体地址H
			}
			if(tRunPara.bYcFloat)  //浮点值上送
			{
				pd += CopySwap(pd,ff.tt,sizeof(ff.tt),true);
			}
			else
			{				
				*pd++=(BYTE)(BYTE)(ycvalue);	
				*pd++=(BYTE)(BYTE)(ycvalue>>8);	
			}
        #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
	
			*pd++=QDS;  //品质描述
			

			// 数量达到最大值或是最后一个,发送
			num++;		
			if((num>maxsendnum) || (i == (end -1))) //超过一帧数据  一帧最大数据66
			{
				pd=&pt104->arrSendBuf[6];
				*pd++=frametype;			//带品质描述的遥测帧  1
				if(pRunSet->bDIContinue)  //点号连续上送
				{					
					*pd++=(num|0x80);		//可变结构限定词  
					*pd++=QOI;			//传送原因：总召唤     
					*pd++=0;			//传送原因：总召唤     
					*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
					*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
					*pd++=(BYTE)cp;
					*pd++=(BYTE)(cp>>8);
					*pd++=0;			//信息体地址H
					cp+=num;
					num = tRunPara.bYcFloat ? (num*5+9) : (num*3+9);
					pd=&pt104->arrSendBuf[15];		
				}
				else
				{
					*pd++=(num);		//可变结构限定词  
					*pd++=QOI;			//传送原因：总召唤     
					*pd++=0;			//传送原因：总召唤     
					*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
					*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
					num = tRunPara.bYcFloat ? (num*8+6) : (num*6+6);
					pd=&pt104->arrSendBuf[12];		
				}

				if(pt104->bSendChange)
				{
					IEC104_Send(pt104,FRAME_I_PRIOR_LOW,num);
				}
				else
				{
					IEC104_Send(pt104,FRAME_I,num);
				}
				num=0;	
			}	
		}	
	}	


	// 事件帧
	if((QOI == IEC_QOI_CALL_ALL_GROUP) || (QOI >= IEC_QOI_CALL_YX_BEGIN && QOI <= IEC_QOI_CALL_YX_END))
	{  
		BYTE frameType,SIQ;
		int maxsendnum;
		u8 yxtype=0;

		// 确定最大发送数量和起始地址
		if(pRunSet->bDIContinue)  
		{
			pd=&pt104->arrSendBuf[15];	
			maxsendnum=120;  	// ngd最高位需置1，因此最大遥信个数不能超过128
		}
		else
		{
			pd=&pt104->arrSendBuf[12];	
			maxsendnum=50;		// 1个字节遥信+3个字节信息体地址
		}

		// 确定报文类型，单点(1)或双点(3)
		frameType = 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;
			}
		}

		// 循环发送所有遥信通讯点
		num=0;
		if(g_di_table)
		{
			cp=g_di_table[begin].cp;	
			yxtype=tbl_get_dpi(begin);
		}

		
		
		for(i=begin;i<end;i++)
		{
			if(tRunPara.bSDPI) // 单双点分别上送
			{
				u8 type=tbl_get_dpi(i);
				if(yxtype!=type&&num>0)
				{
					pd=&pt104->arrSendBuf[6];					
					*pd++= yxtype ? 3:1;;			//带品质描述的遥信帧  1
					if(pRunSet->bDIContinue)  //点号连续上送
					{
						*pd++=(num|0x80);		//可变结构限定词  
						*pd++=QOI;			//传送原因：总召唤	   
						*pd++=0;			//传送原因：总召唤	   
						*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
						*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
						*pd++=(BYTE)cp;
						*pd++=(BYTE)(cp>>8);
						
						
						*pd++=0;			//信息体地址H
						pd=&pt104->arrSendBuf[15];		
						cp+=num;
						num=num+9;
					}
					else
					{
						*pd++=(num);		//可变结构限定词  
						*pd++=QOI;			//传送原因：总召唤	   
						*pd++=0;			//传送原因：总召唤	   
						*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
						*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
						pd=&pt104->arrSendBuf[12];		
						num=num*4+6;
					}	
					if(pt104->bSendChange)
					{
						IEC104_Send(pt104,FRAME_I_PRIOR_LOW | FRAME_I_COS,num);
					}
					else
					{
						IEC104_Send(pt104,FRAME_I,num);
					}
					num=0;
					yxtype=type;
				}
				// 填写SIQ
				frameType =type ? 3:1;
				SIQ = tbl_get_yx(i,type);
			}
			else
			{
				// 填写SIQ
				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(!pRunSet->bDIContinue)
			{
				cp=g_di_table[i].cp;
				*pd++=(BYTE)cp;
				*pd++=(BYTE)(cp>>8);	
				*pd++=0;

				
			}	

			if(g_run_stu.bToolRmtTest||g_run_stu.bHmiRmtTest)	//测试模式，遥信置为无效
			{
				if(pRunSet->bTT_RmtTest)SIQ|=0x80;
			}					
			*pd++=SIQ;  // 事件值	SIQ

			// 数量达到最大值或是最后一个,发送
			num++;
			if((num >= maxsendnum) || (i == (end-1))) 
			{
				pd=&pt104->arrSendBuf[6];
				*pd++=frameType;			//带品质描述的遥信帧  1
				if(pRunSet->bDIContinue)  //点号连续上送
				{
					*pd++=(num|0x80);		//可变结构限定词  
					*pd++=QOI;			//传送原因：总召唤     
					*pd++=0;			//传送原因：总召唤     
					*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
					*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
					*pd++=(BYTE)cp;
					*pd++=(BYTE)(cp>>8);
					*pd++=0;			//信息体地址H
					pd=&pt104->arrSendBuf[15];		
					cp+=num;
					num=num+9;
				}
				else
				{
					*pd++=(num);		//可变结构限定词  
					*pd++=QOI;			//传送原因：总召唤     
					*pd++=0;			//传送原因：总召唤     
					*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
					*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
					pd=&pt104->arrSendBuf[12];		
					num=num*4+6;
				}

				if(pt104->bSendChange)
				{
					IEC104_Send(pt104,FRAME_I_PRIOR_LOW | FRAME_I_COS,num);
				}
				else
				{
					IEC104_Send(pt104,FRAME_I,num);
				}
				num=0;
			}
		}
	}	
	
 	//总召唤结束帧
	pd=&pt104->arrSendBuf[6];		
	*pd++=100;		//总召唤确认帧
	*pd++=1;		//可变结构限定词
	*pd++=10;		//传送原因：总召唤
	*pd++=0;		//传送原因：总召唤
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址L
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址H
	*pd++=0;		//信息体地址L
	*pd++=0;		//信息体地址H
	*pd++=0;		//信息体地址H
	*pd++=QOI; 	//QOI 
	
	if(pt104->bSendChange)
	{
		IEC104_Send(pt104,FRAME_I_PRIOR_LOW,10);
	}
	else
	{
		IEC104_Send(pt104,FRAME_I,10);
	}

	// 不允许通讯续传的情况下，第一次总召先清SOE队列，保证上传事件是总召后产生的，后续总召不清SOE队列
	if((pRunSet->bTT_SoeResumeComm == 0)&&(!pt104->bSendChange))
	{
		g_soe_queue.tail_send[pt104->chnl].n=g_soe_queue.head.n;	
		g_soe_queue.tail_ack[pt104->chnl].n = g_soe_queue.tail_send[pt104->chnl].n;	
	}

	
#ifdef FUN_SOE_DELAY_TIME
	if(pt104->bSendChange == false)
	{
		pt104->DTSendChange = dTCounter;
		pt104->bfirst_call = true;	
	}
#endif
	// 置总召标志
	pt104->bSendChange=true;
}



static void IEC104_DDAll(IEC104_DEF *pt104,u8 QCC)		//确认帧
{
	u8 *pd;
	u8 cot;//通用分类数据集数目
	u16 cp;
	int i,num;	
	
	//总召唤确认帧	
	IEC104_Echo_I(pt104,IEC_COT_ACTCON,false);

	// 电度值
	if((QCC==1)||(QCC==5))
	{   
		// 初始数据
		num=0;
		cp=g_dd_table[0].cp;			
		if(QCC==5)cot=37;  // 总召
		else cot=QCC+37;  // 召某一组
		pd=&pt104->arrSendBuf[15];	
		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;
			}
			pd += CopySwap(pd,ff.tt,sizeof(ff.tt),true);

			*pd++=0;	// QDS	
			num++;			
			// 发送一帧
			if(num>30||(i==g_table_head->dd_num-1))
			{
				pd=&pt104->arrSendBuf[6];
				*pd++=206;			//带品质描述的遥测帧  1
				*pd++=(num|0x80);		//可变结构限定词  
				*pd++=cot;			//传送原因：总召唤     
				*pd++=0;			//传送原因：总召唤     
				*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 
				*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址 6
				*pd++=(BYTE)cp;
				*pd++=(BYTE)(cp>>8);
				*pd++=0;			//信息体地址H
				IEC104_Send(pt104,FRAME_I_PRIOR_LOW,num*5+9);
				pd=&pt104->arrSendBuf[15];	
				cp+=num;
				num=0;
				
			}					
		}		
			
	}
	if(QCC==0x45)//主站扩展瞬时冻结电度命令
	{
		//rand_file_info.b_frz=true;
	}
	// 总召唤结束帧
	pd=&pt104->arrSendBuf[6];		
	*pd++=101;		//总召唤确认帧
	*pd++=1;		//可变结构限定词
	*pd++=IEC_COT_ACTTERM;		//传送原因：总召唤
	*pd++=0;		//传送原因：总召唤
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址L
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址H
	*pd++=0;		//信息体地址L
	*pd++=0;		//信息体地址H
	*pd++=0;		//信息体地址H
	*pd++=QCC; 	//QOI 
	
	IEC104_Send(pt104,FRAME_I_PRIOR_LOW,10);

}



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

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC104_Echo_YK(IEC104_DEF *pt104,u16 cp,u8 ti,u8 cot,u8 v)
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	*pd++=ti;		// 类型标识
	*pd++=1;		//可变结构限定词
	*pd++=cot;		//传送原因：
	*pd++=0;		//传送原因：
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址
	*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址
	*pd++=(BYTE)cp;		//信息体地址L
	*pd++=(BYTE)(cp>>8);//信息体地址H
	*pd++=0;			//信息体地址H
	*pd++=v;			//DCS
	IEC104_Send(pt104,FRAME_I,10);
	pt104->bRmtSM2 = false; //遥控执行完成，清除SM2标志，开放事件上传
}


static void IEC104_RmtCtrl(IEC104_DEF *pt104,BYTE *ps,BYTE frametype)	
{
	BYTE cot,ykcot,ykvalue,yktype=0,ykThz;	
	WORD ykno;	
	WORD yksave;
	int i,ret;
	u8 yk_cmd;
	bool bValidYk=false;

	ykcot=(ps[0]&0x01f);
	ykvalue=ps[7];
	yk_cmd = ykvalue&0x03;
	ykno=ps[4]+(WORD)ps[5]*256;	
	yksave=ykno;
	cot = IEC_COT_UN_OA | IEC_COT_PN;
	if(frametype==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] ,frametype,ps[0],ps[7]) == 0)
			{
				pt104->lnk_yk_send_flag |= (1<<g_do_table[i].owner);
				pt104->us0_lnk_yk = ustimer_get_origin();

				// 先回S帧，告诉主站已收到
				IEC104_Echo_US(pt104,FRAME_S);
			}
			else
			{	cot = (ykcot == IEC_COT_ACT) ? IEC_COT_ACTCON : IEC_COT_DEACTCON;
				cot |= IEC_COT_PN;
				IEC104_Echo_I(pt104,cot,false);
			}
			cot = ps[0]; 
			if(ykvalue&0x80)
			{
				yktype=YK_TYPE_SEL;
			}
			else
			{
				yktype=YK_TYPE_EXE;
			}

			if(ykcot==IEC_COT_DEACT)  //遥控撤销
			{
				yktype=YK_TYPE_CANCEL;
				cot=IEC_COT_DEACTCON;				
			}
			
			
			refresh_co_file(true,frametype,yktype,ykno,cot,yk_cmd);
			return;
		}
		// 本装置遥控
		else
		{
			bValidYk=true;
			cot=IEC_COT_ACTCON;
			if(ykvalue&0x80)
			{
				yktype=YK_TYPE_SEL;
			}
			else
			{
				yktype=YK_TYPE_EXE;
			}

			if(ykcot==IEC_COT_DEACT)  //遥控撤销
			{
				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_104+pt104->chnl);
			if(ret != 0)
			{
				cot = IEC_COT_ACTCON | IEC_COT_PN;  // 否定应答
			}	

			refresh_co_file(true,frametype,yktype,ykno,cot,yk_cmd);
			break;
		}
	}

	// 没有找到对应遥控点
	if(i == g_table_head->do_num)
	{
		char buf[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);

	}

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

	pt104->bRmtSM2 = false; //遥控执行完成，清除SM2标志，开放事件上传

	//应答报文
	IEC104_Echo_I(pt104,cot,false);
	
	if((yktype==YK_TYPE_EXE)&&(cot==IEC_COT_ACTCON))
	{ 
		//执行完毕应答
		IEC104_Echo_YK(pt104,yksave,frametype,IEC_COT_ACTTERM,ps[7]);

		refresh_co_file(false,frametype,yktype,ykno,IEC_COT_ACTTERM,yk_cmd);

	}
}


/**************************************************************************
函数名称：IEC104_SendEvent
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：自动发送事件及变位信息，
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
#define MAX_SOE_PER_FRAME 16
u32 IEC104_SendEvent(IEC104_DEF *pt104,int is_send_cos) /*104规约自动处理,比如收集变位及变化的遥测*/
{
	BYTE arrCos[200],arrSoe[200];
	BYTE *pd,vsq;
	u16 head,soe_no,arg_index;
	u32 cnt;
	EVENT_STRUCT event;
	bool    bFault;
	FAULT_PARA_BUF *tf=&pt104->tfault;
	// 主动上送条件:
	// 1、是IEC104_ST_STARTED状态
	// 2、没有在处理加密遥控报文
	if((pt104->state != IEC104_ST_STARTED) || pt104->bRmtSM2)
	{
		return 0;
	}

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

	// 等SOE保存后再发送，避免通讯确认没有保存的事件
	if(g_soe_queue.tail_eep.n!=g_soe_queue.head.n)
	{
		return 0;
	}
#ifndef IEC_JXYB_DEAL
	if(g_run_stu.bjx)	//检修压板投入，不发送变化的事件记录
	{
		g_soe_queue.tail_send[pt104->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;
			tf->tail++;
			pd=&pt104->arrSendBuf[6];
			*pd++ = 42;	//类型标识				1
			*pd++=0;							//可变结构限定词  		2
			*pd++=IEC_COT_SPONT;				//传送原因：突发		3
			*pd++=0;							//传送原因：总召唤     	4
			*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 	5
			*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址 	6
			memcpy(pd,&tf->data[index][1],tf->data[index][0]);			
			IEC104_Send_sts(pt104,FRAME_I_PRIOR_LOW,tf->data[index][0]+6,0);
			return 0;
		}			
	}	
	head = g_soe_queue.head.n;
// 一次调用，将事件池中所有事件发送完，
// 已保证满足"重新连接后，总召前，发送完所有老的SOE,不发送COS"的需求。
	cnt=0;
	while(1)
	{
		u8 yxtype=0;
		// 检查发送队列空间,空间应大于1/4，防止发送缓冲区满
		if(IEC104_BufFree(&pt104->TxBuf[0]) < (IEC_TBUF_NUM>>2))
		{
			break;
		}

		// 获取一帧报文的事件
		bFault=false;
		vsq = asdu_get_event_104(pt104->chnl,head,arrCos,arrSoe,&soe_no,1,&bFault,&event,&arg_index,&yxtype);
		if(vsq == 0)
		{
			break;
		}
		cnt += vsq;	
		
		// 发送COS
		if(is_send_cos)
		{
			pd=&pt104->arrSendBuf[6];
			*pd++ =(yxtype==1) ? 3 : 1;		//类型标识				1
			*pd++=vsq;							//可变结构限定词  		2
			*pd++=IEC_COT_SPONT;				//传送原因：突发		3
			*pd++=0;							//传送原因：总召唤     	4
			*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 	5
			*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址 	6
			memcpy(pd,arrCos,vsq*4);
			
			IEC104_Send(pt104,FRAME_I_COS|FRAME_I_PRIOR_HIGH,6+4*vsq);
		}

		// 发送SOE
		pd=&pt104->arrSendBuf[6];
		*pd++ =(yxtype==1) ? 31 : 30;	//类型标识				1
		*pd++=vsq;							//可变结构限定词  		2
		*pd++=IEC_COT_SPONT;				//传送原因：突发		3
		*pd++=0;							//传送原因：总召唤     	4
		*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 	5
		*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址 	6
		memcpy(pd,arrSoe,vsq*11);
		
		IEC104_Send_sts(pt104,FRAME_I_SOE|FRAME_I_PRIOR_HIGH,6+vsq*11,soe_no);

		if(bFault)  // 上送故障信息
		{
			u16 no;
			u8 SIQ,week;
			struct rtc_time_t rt;
			pd=&pt104->arrSendBuf[6];
			*pd++ = 42;	//类型标识				1
			*pd++=0;							//可变结构限定词  		2
			*pd++=IEC_COT_SPONT;				//传送原因：突发		3
			*pd++=0;							//传送原因：总召唤     	4
			*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 	5
			*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址 	6

			no = soe_lp2cp(event.ev_type,event.ev_code,event.ev_arg[1]);
			SIQ = event.ev_value;
			if(yxtype==1 )
			{
				SIQ+=1;	//转为双点信息
			}
			timespec_to_rtc(event.ts,&rt,1);
			week = WEEK_DAY(event.ts.tv_sec);
        #if  !defined GD_AREA_SHANTOU && !defined GD_AREA_FOS_2020 && !defined FUNC_JX_YC_SIQ00
			if(g_run_stu.bjx)
			{
				SIQ|=0x80;//带品质描述，有效 <1>：=无效
			}
	    #endif
			*pd++=1; //遥信个数
			*pd++ = yxtype ? 31 : 30;		//类型标识		1
			*pd++=(u8)no;		// 信息体地址L
			*pd++=(u8)(no>>8);	// 信息体地址H
			if(pRunSet->bTT_faultparaDKY)
			{
				*pd++=0;			// 信息体地址H2  按照规约规定，101 及104 遥信信息体地址都是两个字节
			}
			//*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;
			
			vsq=asdu_get_event_yc(arrSoe,1,arg_index);
			*pd++=vsq; //遥 测个数
			*pd++ =13;		//类型标识	浮点值
			memcpy(pd,arrSoe,vsq*7);
            if (pRunSet->bTT_faultparaDKY)
            {
                IEC104_Send_sts(pt104, FRAME_I_PRIOR_LOW, (pd - &pt104->arrSendBuf[6]) + vsq * 7, 0); //  21 改为20 原因: 按照规约规定，101 及104 事件参数 遥信信息体地址都是两个字节
            }
            else
            {
                IEC104_Send_sts(pt104, FRAME_I_PRIOR_LOW, (pd - &pt104->arrSendBuf[6]) + vsq * 7, 0); //  21 改为20 原因: 按照规约规定，101 及104 事件参数 遥信信息体地址都是两个字节
            }
        }
		
	}
	return cnt;
}


void IEC104_SendYC(IEC104_DEF *pt104)
{
	int i;		
	int num;
	BYTE *pd; 
	int ycvalue;
	BYTE frametype;
	union{

		float ff;
		BYTE tt[4];
	}ff;		
	
	// 主动上送条件:
	// 1、是IEC104_ST_STARTED状态
	// 2、没有在处理加密遥控报文
	if((pt104->state != IEC104_ST_STARTED) || pt104->bRmtSM2)
	{
		return;
	}

	// 检查发送队列空间,空间应大于1/4，防止发送缓冲区满
	if(IEC104_BufFree(&pt104->TxBuf[0]) < (IEC_TBUF_NUM>>2))
	{
		return;		
	}

	// 遥测值需总召之后且遥测自动上送投入才允许上送
	if(pt104->bSendChange == 0 || tRunPara.bAutoSendYc == 0  || byc_filter)
	{
		return;
	}


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

	if(tRunPara.bYcBDH)  //标度化值
	{
		frametype=11;			//带品质描述的遥测帧  1
	}
	else if(tRunPara.bYcFloat)  //浮点值
	{
		frametype=13;			//带品质描述的遥测帧  1
	}
	else
	{
		frametype=9;			//带品质描述的遥测帧  1
	}		
	pd=&pt104->arrSendBuf[12];				
	num=0;
	for(i=0;i<g_table_head->ac_num;i++)
	{
		WORD cp;
		bool bsend=false;
		BYTE  QDS=0;	//品质描述， 默认为零
		u8 link_qds=0;
		BYTE no=g_ac_table[i].indexno;
		long lmeaval;
		if(g_ac_table[i].dead_value==0||g_ac_table[i].normal==0) continue;  //死区或额定为零，不主动上送
		if(no>0)
		{					
			DWORD dwMax;
			DWORD dwDeadVal=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(pt104->yc_save[i]);	
			if(dwMax==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-pt104->yc_save[i])>=dwDeadVal)))
			#else
			if(bsend||((i<MAX_YC_NUMBER)&&(_AbsL(lmeaval-pt104->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)pt104->yc_save[i]=lmeaval;// 遥测保存
				*pd++=(BYTE)cp;
				*pd++=(BYTE)(cp>>8);
				*pd++=0;			//信息体地址H

				if(tRunPara.bYcFloat)  //浮点值上送
				{
					pd += CopySwap(pd,ff.tt,sizeof(ff.tt),true);
				}
				else
				{				
					*pd++=(BYTE)(ycvalue);	
					*pd++=(BYTE)(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++=QDS;  //品质描述
				num++;
				if(num>10) //超过一帧数据
				{
					pd=&pt104->arrSendBuf[6];
					*pd++=frametype;			//带品质描述的遥测帧  1
					*pd++=num;		//可变结构限定词  
					*pd++=IEC_COT_SPONT;	//传送原因：突发     
					*pd++=0;				//传送原因：源地址     
					*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 
					*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址 6

					if(tRunPara.bYcFloat)  //浮点值
					{
						IEC104_Send(pt104,FRAME_I,num*8+6);
					}
					else
					{
						IEC104_Send(pt104,FRAME_I,num*6+6);
					}
					pd=&pt104->arrSendBuf[12];		
					num=0;
				}					
			}
		}
	}
	if(num)  //有数据
	{
		pd=&pt104->arrSendBuf[6];
		*pd++=frametype;			//带品质描述的遥测帧  1
		*pd++=num;		//可变结构限定词  
		*pd++=IEC_COT_SPONT;			//传送原因：总召唤     
		*pd++=0;			//传送原因：总召唤     
		*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
		*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6

		if(tRunPara.bYcFloat)  //浮点值
		{
			IEC104_Send(pt104,FRAME_I,num*8+6);
		}
		else
		{
			IEC104_Send(pt104,FRAME_I,num*6+6);
		}			
	}
}	

void IEC104_lnk_yk(IEC104_DEF *pt104)
{

	int i;
	LINKYK_DEF *yk;
	LINK_YKDATA *yd=NULL;

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

	for(i=0; i<SWITCH_NUM_EXT_PUB; i++)
	{
		if(pt104->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];
				IEC104_Echo_YK(pt104, yk->cp_m, yd->ti, yd->cot, yd->v);
				yk->flag[IECLINK_YK_ACK] = 0;
				yk_hs = true;
			}

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

				pt104->lnk_yk_send_flag &= ~(1<<i);
				yk_hs = true;
			}

			// 遥控超时，复位
			if(ustimer_get_duration(pt104->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);
				
				pt104->lnk_yk_send_flag &= ~(1<<i);
			}
			if(yk_hs&&yd)
			{
				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 IEC104_lnk_msg_ypara(IEC104_DEF *pt104)
{
	u8 ti=0,vsq=0;
	int i=0,index=0;

	msg_data_struct	*msg_data = &pt104->msg_ypara_recv;
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	u8   num=0;
	u8 	 cot=0,pdi=0;
	
#if 0
/*返回信息是否超时*/
	if(pt104->msg_ypara.flag)
	{
		if(ustimer_get_duration(pt104->us0_msg_ypara)	> 10*USTIMER_SEC)
		{
			pt104->msg_ypara.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 = &pt104->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[0]=ti;		// 类型标识
		pd[1]=0;		//可变结构限定词
		pd[2]=cot;		//传送原因：
		pd[3]=00;		//传送原因：
		pd[4]=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址
		pd[5]=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址
		num += 6;

		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++]=0;
			
			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++;
		}				

		pd[1] = vsq;
		IEC104_Send(pt104,FRAME_I,num);			
	}		
	
}

void IEC104_Rmt_msg_ypara_write(IEC104_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 += 3;
			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

#ifdef YPARA_LINK_S
void IEC104_lnk_set_up(IEC104_DEF *pt104)
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	u8   num=0;
	
	if(pt104->para_updata_flag)
	{
		pd[0]=SET_UP_TYPE;	// 类型标识
		pd[1]=0x01;			//可变结构限定词
		pd[2]=0x03;			//传送原因：
		pd[3]=00;			//传送原因：
		pd[4]=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址
		pd[5]=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址
		num += 6;
		
		pd[num++] = 0;		/*信息体地址*/
		pd[num++] = 0;		/**/			
		pd[num++] = 0;		/*信息体地址*/		
		pd[num++] = pt104->para_updata_flag;		/*定值是否更新*/	
		
		IEC104_Send(pt104,FRAME_I,num);						
		pt104->para_updata_flag=0;
	}	
}

#endif


#ifdef	RCD_STRAN_S
static void IEC104_Rcd_Ack(IEC104_DEF *pt104)			//发送新录波文件名
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	u8   num=0;
	char tmp[128]={0};
	char note[100];

	pd[0]= RCD_TYPE;		// 类型标识
	pd[1]= 1;		//可变结构限定词
	pd[2]= 0x07;		//传送原因：
	pd[3]= 00;		//传送原因：
	pd[4]=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址
	pd[5]=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址
	num += 6;
	
	if(get_queue_count(&pt104->rcd_qt.queue) >0)	/*有新录波*/
	{
		check_data_queque(&pt104->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++]=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++]=0;				//信息体地址	
		pd[num++]=0;				/*文件数量*/		
	}	
	
	IEC104_Send(pt104,FRAME_I,num);	
}
#endif

/**************************************************************************
函数名称：IEC101_link_par
函数版本：1.00 
作者：   
创建日期：2019.4.19     
函数功能说明：101规约遥参级联处理
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC104_link_par(IEC104_DEF *pt104)
{

	int i;
	LINKPAR_DEF *par;
	LINK_PARDATA *yd;

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

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

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

			// 遥控结束帧
			if(par->flag[IECLINK_YK_END])
			{
				yd = &par->data[IECLINK_YK_END];
				
				IEC104_AddFrame_dz((IEC104_DEF *)pt104,&yd->buf[0],yd->len,yd->vsq,yd->cot,yd->ti,true);
				
				par->flag[IECLINK_YK_END] = 0;
				pt104->lnk_par_send_flag &= ~(1<<i);
				rt_printf("参数结束帧\r\n");
			}

			// 遥控超时，复位
			if(ustimer_get_duration(pt104->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;	
				pt104->lnk_par_send_flag &= ~(1<<i);
			}
		}
	}
}

#ifdef FUNC_MORE_PRESET
	YT_BUF PresetData104[256];
	YT_BUF SetData104[256];
	int WrongCount104[256];
	int g_CountPreset104=0, g_CountSet104=0;
#endif

static void IEC104_ParCtrl(IEC104_DEF *pt104,u8 *pdat)	// ps从传送原因开始
{
	WORD parAddr=0;
	u8 *ps=&pdat[10];	//主站下发帧第10位开始为地址位
	u8 qos=ps[7];
	u8 ti=pdat[4];	//类型标示
	u8 vsq=pdat[5];	//可变帧长限定词
	u8 ytcot=pdat[6];	//传输原因
	struct do_table parTab;
	BYTE buf[256];
	BYTE len=0,addlen=0;
	RMT_SET_VAL yt_buf;
	WORD sw=1;
	bool b104=true;
	u8 cot;
	static DWORD ctrlParTime=0;    //系统定时器
	static bool bSetParaDisable=false;	//为1时：取消预置，禁止写；为0时：非取消预置，允许写；
	
	parAddr = (ps[1]<<8)+ps[0];//遥参通信地址
	len = get_par_link_data_dz(&buf[0],&ps[0],vsq,ti);
	if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
	{
		qos=ps[7];//
	}
	else
	{
		qos=ps[6];//
	}

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

	#ifdef FUNC_MORE_PRESET
		int n;
	#endif
		cot = IEC_COT_ACTCON;
	#ifndef	FUNC_MORE_PRESET
		bfirst_sel = false;
	#endif

	#ifdef FUNC_MORE_PRESET
		if((dTCounter-ctrlParTime) > tRunPara.dYKTime)
		{//超时清零预置/执行/出错数据存储区
			memset(SetData104,0,sizeof(SetData104));
			g_CountSet104 = 0;
			memset(PresetData104,0,sizeof(PresetData104));
			g_CountPreset104 = 0;
			memset(WrongCount104,0,sizeof(WrongCount104));
			rt_printf("超时清除104报文缓存区\r\n");
		}
	#endif
	
		if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
		{
			if(((qos&0x80)==0x80) && (ytcot==IEC_COT_ACT))
			{
			#ifdef FUNC_MORE_PRESET
				memcpy(&PresetData104[g_CountPreset104++].yc_data,ps,vsq*8);
					
				rt_printf_time("104预置报文[第%d帧]：",g_CountPreset104);
				for(n=0;n<vsq*8;n++)
				{
					rt_printf("%02x  ",PresetData104[g_CountPreset104-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
			
				bSetParaDisable = false;	//重新预置时清0
			}
		#ifdef FUNC_MORE_PRESET
			else if(((qos&0x80)==0x00) && (ytcot==IEC_COT_ACT))
			{
				memcpy(&SetData104[g_CountSet104++].yc_data,ps,vsq*8);

				rt_printf_time("104固化报文[第%d帧]：",g_CountSet104);
				for(n=0;n<vsq*8;n++)
				{
					rt_printf("%02x  ",SetData104[g_CountSet104-1].yc_data[n]);
				}
			}
		#endif
		}
		else if(ti==FOS_PAR_SET_ONE_EXT || ti==FOS_PAR_SET_MUL_EXT)
		{
			if(((qos&0x80)==0x80) && (ytcot==IEC_COT_ACT))
			{
			#ifdef FUNC_MORE_PRESET
				memcpy(&PresetData104[g_CountPreset104++].yc_data,ps,vsq*7);
					
				rt_printf_time("104预置报文[第%d帧]：",g_CountPreset104);
				for(n=0;n<vsq*7;n++)
				{
					rt_printf("%02x  ",PresetData104[g_CountPreset104-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
			
				bSetParaDisable = false;	//重新预置时清0
			}
		#ifdef FUNC_MORE_PRESET
			else if(((qos&0x80)==0x00) && (ytcot==IEC_COT_ACT))
			{
				memcpy(&SetData104[g_CountSet104++].yc_data,ps,vsq*7);

				rt_printf_time("104固化报文[第%d帧]：",g_CountSet104);
				for(n=0;n<vsq*7;n++)
				{
					rt_printf("%02x  ",SetData104[g_CountSet104-1].yc_data[n]);
				}
			}
		#endif
		}
		
		for(parCount=0;parCount<vsq;parCount++)//多参数处理
		{
			if(ti==FOS_PAR_READ_ONE || ti==FOS_PAR_READ_MUL)
				addLenParLen = (parCount)*7;	//每7位为一个定值的处理,3位地址,4位数据
			else if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
				addLenParLen = (parCount)*(7+1);	//每8位为一个定值的处理,3位地址,4位数据,1位QOS
			else if(ti==FOS_PAR_READ_ONE_EXT || ti==FOS_PAR_READ_MUL_EXT)	addLenParLen = (parCount)*6;
			else if(ti==FOS_PAR_SET_ONE_EXT || ti==FOS_PAR_SET_MUL_EXT)		addLenParLen = (parCount)*(6+1);
			
			//ps[0]和ps[1]为地址位,ps[2]为地址第三位,默认为0x00
		#ifdef YC_QUANTITY
			parAddr = ((ps[1+addLenParLen]<<8)+ps[addLenParLen]-FOS_PAR_STATR_ADDR)%(pRunSet->dYC_num)+FOS_PAR_STATR_ADDR;//遥参通信地址;
		#else	
			parAddr = ((ps[1+addLenParLen]<<8)+ps[addLenParLen]-FOS_PAR_STATR_ADDR)%(FOS_PAR_SW_NUM)+FOS_PAR_STATR_ADDR;//遥参通信地址;
		#endif

		#ifdef YC_QUANTITY
			if( ((ps[1+addLenParLen]<<8)+ps[addLenParLen]) >= FOS_PAR_STATR_ADDR 
				&& ((ps[1+addLenParLen]<<8)+ps[addLenParLen]) < (FOS_PAR_STATR_ADDR+pRunSet->dYC_num))
		#else
			if( ((ps[1+addLenParLen]<<8)+ps[addLenParLen]) >= FOS_PAR_STATR_ADDR 
				&& ((ps[1+addLenParLen]<<8)+ps[addLenParLen]) < (FOS_PAR_STATR_ADDR+FOS_PAR_SW_NUM))
		#endif
			{	
				parAddr = (ps[1+addLenParLen]<<8)+ps[addLenParLen];
			}
			rt_printf("parCount=%d,vsq=%x,parAddr=%x addrfirst=%X addLenParLen=%x\r\n",parCount,vsq,parAddr,(ps[1]<<8)+ps[0],addLenParLen);
			
			if(ti==FOS_PAR_READ_ONE || ti==FOS_PAR_READ_MUL || ti==FOS_PAR_READ_ONE_EXT || ti==FOS_PAR_READ_MUL_EXT)
			{
				rt_printf("读遥参 parAddr=%x\r\n",parAddr);
				if(!readRunParId(sw,parAddr,&(yt_buf.str[0])))//暂默认为0开关
				{
					rt_printf("读遥参未找到对应地址的定值 parAddr=%x\r\n",parAddr);
					cot = IEC_COT_ACTCON | IEC_COT_PN;
				}
				//yt_buf.di = parAddr;			
				yt_buf.di = (ps[1+addLenParLen]<<8)+ps[addLenParLen];
				yt_buf.len = 4;
				if(ti==FOS_PAR_READ_ONE || ti==FOS_PAR_READ_MUL)
				{
					len=iec_add2frame_dz(buf+addlen,&yt_buf,b104);
				}
				else
				{
					len=iec_add2frame_dz(buf+addlen,&yt_buf,false);
				}
				addlen += len;
				if(parCount==(vsq-1))
				{
					rt_printf("读最后一个参数,上送报文,addlen==%d\r\n",addlen);
					IEC104_AddFrame_dz((IEC104_DEF *)pt104, buf,addlen,vsq,cot,ti,false);
					addlen=0;
				}
			} 
			else if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL || ti==FOS_PAR_SET_ONE_EXT || ti==FOS_PAR_SET_MUL_EXT)
			{			
				if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
				{
					offset = 3+addLenParLen;
				}
				else
				{
					offset = 2+addLenParLen;
				}
				
				if(((qos&0x80)==0x80) && (ytcot==IEC_COT_ACT))
				{
					rt_printf("参数预置...\r\n");
					if(!selectRunParId(sw,parAddr,&ps[offset]))
					{
						rt_printf("预置遥参失败,parAddr=%x,请检查地址或参数值\r\n",parAddr);
						cot = IEC_COT_ACTCON | IEC_COT_PN;
					#ifdef FUNC_MORE_PRESET
						WrongCount104[g_CountPreset104-1] = g_CountPreset104;
					#else
						bSetParaDisable = true;	//预置报文出错时置1，禁止固化定值
					#endif
					}
					//ps[3]、ps[4]、ps[5]、ps[6]为写定值数据位
					yt_buf.str[0] = ps[offset];
					yt_buf.str[1] = ps[offset+1];
					yt_buf.str[2] = ps[offset+2];
					yt_buf.str[3] = ps[offset+3];
					yt_buf.str[4] = 0x80;
					//yt_buf.di = parAddr;				
					yt_buf.di = (ps[1+addLenParLen]<<8)+ps[addLenParLen];
					yt_buf.len = 5;
					if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
					{
						len=iec_add2frame_dz(buf+addlen,&yt_buf,b104);
					}
					else
					{
						len=iec_add2frame_dz(buf+addlen,&yt_buf,false);
					}
					addlen += len;
					if(parCount==(vsq-1))
					{
						rt_printf("预置最后一个参数,上送报文,addlen==%d\r\n",addlen);
						ctrlParTime = dTCounter;    //系统时间戳
						//bSetParaDisable = 0;	//重新预置时置0
						IEC104_AddFrame_dz((IEC104_DEF *)pt104, buf,addlen,vsq,cot,ti,false);
						addlen=0;
					}
				}
				else if(((qos&0x80)==0x00) && (ytcot==IEC_COT_ACT))
				{
				#ifdef FUNC_MORE_PRESET
					int s_stop;
					s_stop = WrongCount104[g_CountSet104-1];
				#endif
				
					rt_printf("参数固化...\r\n");
				#ifdef FUNC_MORE_PRESET
					if(((dTCounter-ctrlParTime) > tRunPara.dYKTime) || (0 != s_stop)|| bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[offset],ti))
				#else
					if(((dTCounter-ctrlParTime) > tRunPara.dYKTime) || bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[offset],ti))
				#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帧]104预置报文出错,请检查...\r\n",s_stop);
						}
					#endif
						else if(bSetParaDisable)
						{
							rt_printf("预置报文出错或取消预置,禁止固化定值\r\n");
						}
						else
						{
							rt_printf("固化遥参失败,parAddr=%x,请检查..\r\n",parAddr);
						}
						cot = IEC_COT_ACTCON | IEC_COT_PN;
					}
					//ps[3]、ps[4]、ps[5]、ps[6]为写定值数据位
					yt_buf.str[0] = ps[offset];
					yt_buf.str[1] = ps[offset+1];
					yt_buf.str[2] = ps[offset+2];
					yt_buf.str[3] = ps[offset+3];
					yt_buf.str[4] = 0x00;
					//yt_buf.di = parAddr;				
					yt_buf.di = (ps[1+addLenParLen]<<8)+ps[addLenParLen];
					yt_buf.len = 5;
					if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
					{
						len=iec_add2frame_dz(buf+addlen,&yt_buf,b104);
					}
					else
					{
						len=iec_add2frame_dz(buf+addlen,&yt_buf,false);
					}
					addlen += len;
					if(parCount==(vsq-1))
					{
						if((dTCounter-ctrlParTime) > tRunPara.dYKTime && !bSetParaDisable)
						{
							rt_printf("设置遥参超时,上送报文...\r\n");
						}
					#ifdef FUNC_MORE_PRESET
						else if(0 != s_stop)
						{
							rt_printf("预置报文出错,不允许写,上送报文...\r\n");
						}
					#endif
						else if(bSetParaDisable)
						{
						#ifdef FUNC_MORE_PRESET
							rt_printf("取消预置,不允许写,上送报文...\r\n");
						#else
							rt_printf("预置报文出错或取消预置,不允许写,上送报文...\r\n");
						#endif
						}
						else
						{
							rt_printf("固化最后一个参数,上送报文,addlen==%d\r\n",addlen);
						}
						IEC104_AddFrame_dz((IEC104_DEF *)pt104, buf,addlen,vsq,cot,ti,false);
					#ifndef FUNC_MORE_PRESET
						memset(buf_forCompare,0,sizeof(buf_forCompare));
						i_forCompare = 0;
						j_forCompare = 0;
						ctrlParTime = 0;
					#endif
						addlen=0;
					}
				}
				else if(
					#ifndef FUNC_FIT_QOS
						((qos&0x80)==0x00) &&
					#endif
						(ytcot==IEC_COT_DEACT))
				{
					rt_printf("取消预置...\r\n");
					//ps[3]、ps[4]、ps[5]、ps[6]为写定值数据位
					if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
					{
						yt_buf.str[0] = ps[3+parCount*8];
						yt_buf.str[1] = ps[3+parCount*8+1];
						yt_buf.str[2] = ps[3+parCount*8+2];
						yt_buf.str[3] = ps[3+parCount*8+3];
					}
					else
					{
						yt_buf.str[0] = ps[3+parCount*7];
						yt_buf.str[1] = ps[3+parCount*7+1];
						yt_buf.str[2] = ps[3+parCount*7+2];
						yt_buf.str[3] = ps[3+parCount*7+3];
					}
						
				#ifdef FUNC_FIT_QOS
					yt_buf.str[4] = qos;
				#else
					yt_buf.str[4] = 0x00;
				#endif
					//yt_buf.di = parAddr;					
					yt_buf.di = (ps[1+addLenParLen]<<8)+ps[addLenParLen];
					yt_buf.len = 5;
					if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL)
					{
						len=iec_add2frame_dz(buf+addlen,&yt_buf,b104);
					}
					else
					{
						len=iec_add2frame_dz(buf+addlen,&yt_buf,false);
					}
					addlen += len;
					if(parCount==(vsq-1))
					{
						bSetParaDisable = true;	//取消预置时置1
						rt_printf("取消预置最后一个参数,上送报文,addlen==%d,QOS(104)=%02x\r\n",addlen,qos);
						IEC104_AddFrame_dz((IEC104_DEF *)pt104, buf,addlen,vsq,IEC_COT_DEACTCON,ti,false);
					#ifndef FUNC_MORE_PRESET
						memset(buf_forCompare,0,sizeof(buf_forCompare));
						i_forCompare = 0;
						j_forCompare = 0;
					#endif

						ctrlParTime = 0;
						addlen=0;
					}
				}
			}
		}
	}
}


/**************************************************************************
函数名称：IEC104_App
函数版本：1.00 
作者：   
创建日期：2007.9.1     
函数功能说明：104数据处理任务
输入参数：
输出参数：
返回值：

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
void IEC104_App(IEC104_DEF *pt104)
{
	int cnt;
	u8 ti;
	//IEC101_DEF *pt = (IEC101_DEF *)g_tRsComm[0].ptBuf;
	// printf("AAStep1: Deal the 104 msg. \n");

	// 接收报文
	IEC104_Recv_App(pt104);

#ifdef IEC_NOLINK_NO_CALLYX
	if(g_link_comm.link_flag == false) return;
#endif
	
	//处理级联遥参
	IEC104_link_par(pt104);
	
	// 连接初始建立，所有状态复位
	if(pt104->LinkCountApp != pt104->LinkCountNet)
	{
		pt104->LinkCountApp = pt104->LinkCountNet;
		IEC104_Reset(pt104);
		
		IEC104_printf("IEC104(%d) state: 网络连接(%d => %d)!\r\n",pt104->chnl,pt104->state,IEC104_ST_STOPPED);
		pt104->state = IEC104_ST_STOPPED;
	}

	// 主动SOE处理，放在报文处理之前，使时间同步报文之前的SOE报文在时间报文应答前发送
	cnt = IEC104_SendEvent(pt104,tRunPara.bAutoCos); 

	// 级联遥控处理
	IEC104_lnk_yk(pt104);
#ifdef YPARA_LINK
	IEC104_lnk_msg_ypara(pt104);
#endif 
	
	if(pt104->state == IEC104_ST_STARTED)
	{
		g_bCommStatus=TRUE;
	}

#if 0
	if(cnt)
	{
		sbi_t cnt_remain;
		cnt_remain.n = g_soe_queue.head.n - g_soe_queue.tail_send[pt104->chnl].n;
		IEC104_printf("SendEvent:cnt_send=%d,cnt_remain=%d,head=%d,send=%d,ack=%d.\r\n",
			cnt,cnt_remain.n,g_soe_queue.head.n,g_soe_queue.tail_send[pt104->chnl].n,g_soe_queue.tail_ack[pt104->chnl].n);

	}
#endif

	// 处理接收的加密报文
	// 先处理加密报文，再处理普通报文，保证同一报文一个循环处理完
	if(pt104->bSM2Data)
	{
		// 检查是否是当前连接的报文
		if(IEC104_IsCurFrame(pt104) == 0)
		{
			return;
		}
		
		// 处理加密报文
		if(pRunSet->bTT_SM2)  
		{
		#ifdef ENCRYPT_SM2
			IEC104_SM2(pt104);
		#endif
		}
		pt104->bSM2Data=false;		
	}

	// printf("BBStep2: Deal the 104 msg. \n");
	// 处理接收的普通报文
	if(pt104->bRecvOk)  
	{
		BYTE *pd=pt104->arrRecvBuf;
		u16 addr;
		Byte2WORD Num;
		u8 cot;
		 
		// 检查是否是当前连接的报文
		if(IEC104_IsCurFrame(pt104) == 0)
		{
			return;
		}
		
		IEC104_UnPackFrame(pt104,pt104->arrRecvBuf,0);
		
		
		//检查0x68
		if(pd[0] != 0x68)
		{
			char log_info[128];
			// 不应该出现，因为接收时已检查过了，所以也不应该复位接收状态机
			IEC104_printf("IEC104(%d) recv error: state=%d,pd[0]=%d,ADDR0=%d!\r\n",pt104->chnl,pt104->state,pd[0],tRunPara.byAddr);
			
			sprintf(log_info,"IEC104(%d):recv error,state=%d,pd[0]=%d,ADDR0=%d",pt104->chnl+1,pt104->state,pd[0],tRunPara.byAddr);
			load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
			log_flag.com_err[pt104->chnl]  = true;
		}
		// 正常报文处理
		else
		{
			pd+=2;
			switch((*pd)&0x03)	//控制域判断
			{
			//I格式
			case 0x00:			
			case 0x02:
				// 如果不是STARTED状态，接收到I帧，应主动关闭连接
				if(pt104->state != IEC104_ST_STARTED)
				{
					char log_info[128];
					
					IEC104_printf("IEC104(%d) Reset(%d): 非STARTED状态接收到I帧!\r\n",pt104->chnl,pt104->state);
					IEC104_ActiveClose(pt104);

					sprintf(log_info,"IEC104(%d):The non-started state receives the I frame",pt104->chnl+1,pt104->state);
					load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
					log_flag.com_err[pt104->chnl]  = true;

					
					pt104->bRecvOk=false;
					return;
				}
				// 检查报文的发送序号，报文中的发送帧号必须等于pt104->RecvNum。
				pd[0] &= 0xfe;
				CopySwap(Num.tt,pd,2,false);

				 // 南瑞主站一次连接过程中会多次发送STARTDT，并清零发送接收序号，与标准不符，先屏蔽检查
				 // 序号检查投入才检查ACK。
				if(pRunSet->bTT_104ChkAck)
				{
					if(Num.ss != pt104->RecvNum.ss)
					{
						char log_info[128];
						IEC104_printf("IEC104(%d) Reset: 报文发送帧号错误(Num=%d,RecvNum=%d)!\r\n",pt104->chnl,Num.ss,pt104->RecvNum.ss);
						IEC104_ActiveClose(pt104);
						pt104->bRecvOk=false;

						sprintf(log_info,"IEC104(%d):The message sends the frame number error(Num=%d,RecvNum=%d)",pt104->chnl+1,Num.ss,pt104->RecvNum.ss);
						load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
						log_flag.com_err[pt104->chnl]  = true;
						
						return;
					}
				}
				
				pt104->RecvNum.ss=Num.ss+2;
				
				// 得到ACK序号，确认主站已接收的数据
				pd[2] &= 0xfe;
				CopySwap(pt104->AckNum.tt,&pd[2],2,false);
	
				if(Num.ss != pt104->RecvNum.ss) //序列号不检查,但接收到发送序列号错误，电科院一二次融合错误
				{
					pt104->AckNum.ss=pt104->SendNum.ss;
				}
				if(IEC104_Ack(pt104) != 0)
				{
					IEC104_ActiveClose(pt104);
					pt104->bRecvOk=false;
					return;
				}

				// 以下开始处理ASDU的内容
 				// 检查装置地址
				addr = pd[8] | (pd[9]<<8);
				if(addr != tRunPara.byAddr && addr != 0xFFFF) 
				{
					char log_info[128];
					// 公共地址不对，返回原报文，并将COT设置为46(未知应用服务数据单元公共地址).
					IEC104_Echo_I(pt104,IEC_COT_UN_CA|IEC_COT_PN,true);
					IEC104_printf("IEC104(%d) recv error: state=%d,addr=%d,ADDR0=%d!\r\n",pt104->chnl,pt104->state,addr,tRunPara.byAddr);

					sprintf(log_info,"IEC104(%d):Recv error,state=%d,addr=%d,ADDR0=%d",pt104->chnl+1,pt104->state,addr,tRunPara.byAddr);
					load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
					log_flag.com_err[pt104->chnl]  = true;
					if(soe_check(EV_COMM1_ERR+pt104->chnl)==false && get_104_link_status(pt104->chnl-CFG_UART_NUM_MAX))
					{
						soe_record_ev(EV_COMM1_ERR+pt104->chnl, 1, 0,0,0 ); //记录该端口通信状态
					}
					cls_104_link_status(pt104->chnl-CFG_UART_NUM_MAX);
					break;
				}

				// 检查确认完毕，开始正式处理报文
				ti = *(pd+4);
				cot = *(pd+6);
				switch(ti) 	//类型标示判断
				{
				case 100:			//总查询
					// 检查COT
					if(cot != IEC_COT_ACT)
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,true);
						break;
					}
					IEC104_CallAll(pt104,*(pd+13));
					lnk_call_all();
					//104网络通信异常
					if(soe_check(EV_COMM1_ERR+pt104->chnl))
					{
						//rt_printf("104 ok,com=%d\r\n",pt104->chnl-CFG_UART_NUM_MAX);						
						soe_record_ev(EV_COMM1_ERR+pt104->chnl, 0, 0,0,0 ); //记录该端口通信状态
					}
					set_104_link_status(pt104->chnl-CFG_UART_NUM_MAX);
					break;

				case 101:			//电度总查询
					// 检查COT
					if(cot != IEC_COT_ACT)
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);
						break;
					}
					IEC104_DDAll(pt104,*(pd+13));
					break;
					

				case 45:			//遥控命令单点
				case 46:			//遥控命令双点
					// 检查单双点是否和参数设置相符
					if((ti == 45 && tRunPara.YKtype == 1) || (ti == 46 && tRunPara.YKtype == 0) )
					{
						// 对不支持的类型标识，返回原报文，并将COT设置为44(未知类型标识).
						IEC104_Echo_I(pt104,IEC_COT_UN_TI|IEC_COT_PN,true);
						rt_printf("104遥控类型不符(ti=%d,YKtype=%d)\r\n",ti,tRunPara.YKtype);
						break; 
					}

					// 检查COT
					if(cot != IEC_COT_ACT && cot != IEC_COT_DEACT)
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);
						break;
					}
					IEC104_RmtCtrl(pt104,(pd+6),*(pd+4));		//从传送原因开始
				break;		
				case 103:
					// 检查COT					
					if(cot == IEC_COT_ACT||cot == IEC_COT_REQ)
					{
						IEC104_SetTime(pt104,(pd+13),cot);
					}
					else
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);						
					}
					break;
				case 105:
					// 检查COT
					if(cot != IEC_COT_ACT)
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);
						break;
					}

					#ifdef FUNC_RESET_EQU
						gb_ResetEqu = true;
					#endif
						
					switch(*(pd+13))//复位进程命令限定词 QRP
					{
						//进程的总复位
						case 1:
							IEC104_printf("IEC104(%d) Reset: 进程的总复位!\r\n",pt104->chnl);
							IEC104_Echo_I_Direct(pt104,IEC_COT_ACTCON);
							IEC104_ActiveClose(pt104);
							pt104->bRecvOk=false;
#ifdef IEC_RESET_PROCESS
							rt_printf("装置重启\r\n");				
							watchdog_reset_cpu(2);	//装置复位
#endif
							return;
						break;
						
						//复位事件缓冲区等待处理的带时标信息
						case 2:
							IEC104_printf("IEC104(%d) Reset: 复位事件缓冲区!\r\n",pt104->chnl);
							IEC104_Echo_I(pt104,IEC_COT_ACTCON,false);
							g_soe_queue.tail_send[pt104->chnl].n = g_soe_queue.head.n;
							g_soe_queue.tail_ack[pt104->chnl].n = g_soe_queue.tail_send[pt104->chnl].n;
						break;
					}
					break;

				case 122:	//召目录，选择文件，召唤文件，召唤节
						IEC104_File_Echo(pt104,(pd+4));  //响应文件召唤
					break;
				case 124:		//确认文件，确认节的应答
						IEC104_File_Sure(pt104,(pd+4)); //对文件操作确认
					break;		
 
 				case 176: //   本装置级联时的复归命令
						ResetHzLed(0);
						SignalReset(0,false);
						IEC104_Echo_I(pt104,IEC_COT_ACTCON,false);						
					break;				
#ifdef RCD_STRAN_S
				case RCD_TYPE: //  录波级联
						IEC104_Rcd_Ack(pt104);											
						break;
#endif

				case 200: //  切换定值区
					if(cot == IEC_COT_ACT)
					{
						IEC104_SectionWrite(pt104,&pd[13]);// Addbyte 链路地址和应用单元地址多了2个字节，传送原因多1个字节
					}
					else
					{

						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);						
					}						
					break;
				case 201: //  读取当前定值区号
					if(cot == IEC_COT_ACT)
					{
						IEC104_SectionRead(pt104);// Addbyte 链路地址和应用单元地址多了2个字节，传送原因多1个字节
					}
					else
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);						
					}						
					break;
				case 202: // 读参数

					if(cot == IEC_COT_ACT)
					{	
						int ret;
#ifdef YPARA_LINK
						ret=iec_setread_mananger((void*)pt104,&pd[10],pd[5],true);																													
#else
						ret=iec_setread((void*)pt104,&pd[10],pd[5],true);//vsq为需读参数的个数，0，代表读全部参数，参数为从定值区开始的数据						
#endif	
						if(ret!=0) // 读定值出错，否定应答
						{
							IEC104_Echo_I(pt104,IEC_COT_UN_OA,false);
						}
					}
					else
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);		
					}						
					break;
				case 203: //  修改参数
					if(cot == IEC_COT_ACT||cot==IEC_COT_DEACT)
					{
						int ret;						
						BYTE rcot=IEC_COT_UN_OA;
	#ifdef YPARA_LINK
						int link_ch;
						link_ch = get_ypara_linkch_write((void *)pt104,&pd[10],pd[5],true);
						if(link_ch == 0)
						{
							rt_printf("104------写本地定值\r\n");
							ret=iec_setwrite((void*)pt104,&pd[10],cot,pd[5],true);//vsq为需读参数的个数，0，代表固化参数，参数为从定值区开始的数据
							if(ret!=0) // 写定值出错，否定应答
							{
								if(cot==IEC_COT_DEACT)rcot = IEC_COT_DEACTCON|IEC_COT_PN;
								else rcot = IEC_COT_ACTCON|IEC_COT_PN;
							}
							else
							{
								if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON;
								else rcot=IEC_COT_ACTCON;							}	
							
							IEC104_Echo_I(pt104,rcot,false);	
						}
						else
						{
							IEC104_Rmt_msg_ypara_write(pt104,&pd[10],pd[5],link_ch);
			
							if((link_ch&0x1f) == 0x1f)		/*广播写直接返回确认*/
							{
								if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON;
								else rcot=IEC_COT_ACTCON;
								
								IEC104_Echo_I(pt104,rcot,false);																
							}							
						}											
#else						
						ret=iec_setwrite((void*)pt104,&pd[10],cot,pd[5],true);//vsq为需读参数的个数，0，代表固化参数，参数为从定值区开始的数据
						if(ret!=0) // 写定值出错，否定应答
						{
							if(cot==IEC_COT_DEACT)rcot = IEC_COT_DEACTCON|IEC_COT_PN;
							else rcot = IEC_COT_ACTCON|IEC_COT_PN;
						}
						else
						{
							if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON;
							else rcot=IEC_COT_ACTCON;
						}							
						IEC104_Echo_I(pt104,rcot,false);
#endif					
					}
					else
					{
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);
					}						
					break;		

				case FOS_PAR_READ_ONE://南网佛山局 读取单个参数
				case FOS_PAR_READ_MUL://南网 读取多个参数
				case FOS_PAR_SET_ONE: //南网佛山局 设置单个参数
				case FOS_PAR_SET_MUL://南网 设置多数参数						
				case FOS_PAR_READ_ONE_EXT://南网佛山局 读取单个参数
				case FOS_PAR_READ_MUL_EXT://南网 读取多个参数
				case FOS_PAR_SET_ONE_EXT: //南网佛山局 设置单个参数
				case FOS_PAR_SET_MUL_EXT://南网 设置多数参数
				
					// 检查COT
					if(cot != IEC_COT_ACT && cot != IEC_COT_DEACT)
					{
						rt_printf("COT检查未通过....\n\r");
						IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);			
					}
					else
					{
						rt_printf("\r\n进入读/写定值处理流程....\n\r");
						IEC104_ParCtrl(pt104,&pd[0]);
					}
					break;

					case 210: // 文件传输
					{
						pt104->tf.vsq=pd[5];
						if(cot == IEC_COT_ACT||cot == IEC_COT_REQ)
						{	int ret;
							ret=iec_file_app((void*)pt104,&pt104->arrRecvBuf[15],cot,pt104->arrRecvBuf[1]-14,true);// 从操作标识开始
							if(ret!=0) // 文件处理错误
							{
								BYTE rcot=IEC_COT_UN_OA;
								if(ret==-2)rcot=IEC_COT_UN_COT;
								IEC104_Echo_I(pt104,rcot,false);
							}							
						}
						else
						{
							IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);
						}
					}	
					break;	

					case 211: // 软件升级
					{
						if(cot == IEC_COT_ACT||cot == IEC_COT_DEACT)
						{	int ret;
							ret=iec_pro_update((void*)pt104,cot,pd[13],true);// 从操作标识开始
							if(ret!=0) // 文件处理错误
							{
								BYTE rcot=IEC_COT_UN_OA;
								IEC104_Echo_I(pt104,rcot,false);
							}							
						}
						else
						{
							IEC104_Echo_I(pt104,IEC_COT_UN_COT|IEC_COT_PN,false);
						}
					}	
					break;	


					
				default:
					// 对不支持的类型标识，返回原报文，并将COT设置为44(未知类型标识).
					IEC104_Echo_I(pt104,IEC_COT_UN_TI|IEC_COT_PN,false);
					break;
				}
				break;

				//S格式
				case 0x01:			
					// 得到ACK
					pd[2] &= 0xfe;
					CopySwap(Num.tt,&pd[2],2,false);
					
					pt104->AckNum.ss = Num.ss;
					
					// 如果是STOPPED状态，不允许接收S帧
					if(pt104->state == IEC104_ST_STOPPED)
					{
						char log_info[128];
							
						IEC104_printf("IEC104(%d) Reset: STOPPED状态接收到S帧(AckNum=%d)!\r\n",pt104->chnl,Num.ss);
						IEC104_ActiveClose(pt104);
						pt104->bRecvOk=false;

						sprintf(log_info,"IEC104(%d):The STOPPED state receives the S frame(AckNum=%d)",pt104->chnl+1,Num.ss);
						load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1);
						log_flag.com_err[pt104->chnl]  = true;

						
						return;
					}
					else 
					{
						// 如果不是STOPPED状态，需确认发送数据
						if(IEC104_Ack(pt104) != 0)
						{
							IEC104_ActiveClose(pt104);
							pt104->bRecvOk=false;
							return;
						}
						
						// 如果是PENDING状态，检查所有发送数据是否已确认，如果确认，进入STOPPED状态
						if(pt104->state == IEC104_ST_PENDING)
						{
							if(pt104->SendNum.ss == pt104->AckNum.ss)
							{
								IEC104_Echo_US(pt104,FRAME_U_STOP_ACK);
								
								IEC104_printf("IEC104(%d) state: S帧(%d => %d)!\r\n",pt104->chnl,pt104->state,IEC104_ST_STOPPED);
								pt104->state = IEC104_ST_STOPPED;
							}
						}
					}
					break;
					
				//U格式	
				case 0x03:	
					// 特殊命令，打开维护工具端口
					if(pd[1]== 0x55 && pd[2] == 0xaa && pd[3] == 0xff)
					{
						net_maintain_init();
					}
					//Test有效
					else if((*pd)&0x40)			
					{
						// 104标准规约要求，在三种状态下都应回应测试帧，
						// 但如果规约进入连接断开状态后,TCP连接没有真正断开(串口104有可能出现此现象)，
						// 此时不应应答测试帧，让主站由于收不到测试帧应答，主动断开TCP连接，否则，主站旧连接持续存在，
						// 规约层由于没有收到STARTED命令，不能发送数据，将导致数据不能主动上送。
						// 目前，由于在IEC104_IsCurFrame会拦截不符合要求的报文，所以此处条件可以删除
						// 170802:恢复IEC104_ST_STARTED条件，以免链路异常复位后主站重连不发STARTDT,通过测试帧保持连接。
						if(pt104->state == IEC104_ST_STARTED)
						{
							IEC104_Echo_US(pt104,FRAME_U_TEST_ACK);
						}
					}
					//StopDT有效
					else if((*pd)&0x10) 		
					{
						if(pt104->SendNum.ss == pt104->AckNum.ss)
						{
							IEC104_Echo_US(pt104,FRAME_U_STOP_ACK);
							
							IEC104_printf("IEC104(%d) state: StopDT(%d => %d)!\r\n",pt104->chnl,pt104->state,IEC104_ST_STOPPED);
							pt104->state = IEC104_ST_STOPPED;
						}
						else
						{
							IEC104_printf("IEC104(%d) state: StopDT(%d => %d)!\r\n",pt104->chnl,pt104->state,IEC104_ST_PENDING);
							pt104->state = IEC104_ST_PENDING;
						}
					}
					//StartDT有效
					else if((*pd)&0x04) 		
					{
						// 104标准规约要求，发送、接收序号在连接建立后设置为0，
						// 在STARTDT/STOPDT切换过程中不修改，发送和接收计数器继续运行。
						// 但使用串口104时，没有办法得到连接建立和断开状态，所以在此处建立连接。
						if(pt104->type == IEC104_TYPE_RS) 
						{
							IEC104_Reset(pt104);
							
							IEC104_printf("IEC104(%d) state: 串口连接(%d => %d)!\r\n",pt104->chnl,pt104->state,IEC104_ST_STOPPED);
							pt104->state = IEC104_ST_STOPPED;
						}
						
						IEC104_Echo_US(pt104,FRAME_U_SATRT_ACK);

						// 南瑞主站一次连接过程中会多次发送STARTDT，并清零发送接收序号，与标准不符
						//pt104->SendNum.ss=0;
						//pt104->RecvNum.ss=0;

						// 连接之后，首次进入IEC104_ST_STARTED，发送初始化结束帧和老的SOE
						IEC104_printf("IEC104(%d) state: StartDT(%d => %d)!\r\n",pt104->chnl,pt104->state,IEC104_ST_STARTED);
						if(pt104->state != IEC104_ST_STARTED)
						{
							pt104->state = IEC104_ST_STARTED;
							g_comm_link_status |= (1<<pt104->chnl);

							{
								char buf[64];
								char log_info[128];
								if(pt104->type == IEC104_TYPE_NET)
								{
									sprintf(buf,"104主站连接(%sIP=%d.%d.%d.%d)!\r\n", get_comm_name(pt104->chnl + 1), pt104->ip[0],pt104->ip[1],pt104->ip[2],pt104->ip[3]);
								}
								else
								{
									sprintf(buf,"104主站连接(%s)!\r\n",get_comm_name(pt104->chnl + 1));
								}
								
								log_str_time(LOG_OPERATE,buf,0,1);


								sprintf(log_info,"IEC104(%d):104 main station connection",pt104->chnl + 1);
								load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,1);

								if(log_flag.com_err[pt104->chnl] == true)
								{
									memset(log_info,0,sizeof(log_info));
									sprintf(log_info,"IEC104(%d):Communication process anomaly return",pt104->chnl+1);
									load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,0);
									log_flag.com_err[pt104->chnl] = false;
								}

							}


							// 在2006版104规约中，初始化结束帧在规约上是可选的，且明确要求在FRAME_U_SATRT_ACK后。
							// 2006版之前的规约没有明确此帧和FRAME_U_SATRT_ACK帧的先后关系，采用2006版标准。
							IEC104_InitOK(pt104);
							
							// "重新连接后，总召前，发送完所有老的SOE,不发送COS"
							 IEC104_SendEvent(pt104,0); 
						}

					}
					pt104->bClientStart=true;
					break;
				default:
					break;
			}

			// 清Test计数器	
			pt104->us0_test=ustimer_get_origin();			 
			pt104->bStartTest=false;		
		}

		pt104->bRecvOk=false;
	}

	#if 0  //湖南104为客户端模式，并且在连接到主站后，要首先发送启动帧
	if(tRunPara.b104Client&&(net_104_is_connect(pt104->cNetIndex)))
	{
		if(!pt104->bClientStart)
		{
			if(ustimer_get_duration(pt104->us0_client) > 2*USTIMER_SEC)
			{
				IEC104_Echo_US(pt104,FRAME_U_SATRT);
				pt104->us0_client= ustimer_get_origin();
			}			
		}
	}
	#endif
	// 如果连接没有建立，直接返回
	if(pt104->state == 0)  
	{
		return;
	}	
#ifdef YPARA_LINK_S
	if(tRunPara.b_ypar_slave)	IEC104_lnk_set_up(pt104);	
#endif
	
	// 如果TCP连接已断开，复位返回
	if(pt104->type != IEC104_TYPE_RS)
	{
		if(!net_104_is_connect(pt104->cNetIndex))
		{
			IEC104_printf("IEC104_App:连接已断开.\r\n");
			IEC104_Reset(pt104);
			if(soe_check(EV_COMM1_ERR+pt104->chnl)==false && get_104_link_status(pt104->chnl-CFG_UART_NUM_MAX))
			{
				soe_record_ev(EV_COMM1_ERR+pt104->chnl, 1, 0,0,0 ); //记录该端口通信状态
			}
			cls_104_link_status(pt104->chnl-CFG_UART_NUM_MAX);
			return;
		}
	}
	// 104  超过W值，应答 S帧
	if(pt104->RecvNum.ss>pt104->AckRecvNum)
	{
		if((pt104->RecvNum.ss-pt104->AckRecvNum)>=tRunPara.w104W)
		{
			IEC104_Echo_US(pt104,FRAME_S);
		}
	}
	//104发送处理
	IEC104_Send_Polling(pt104);

	// 处理加密遥控报文标志超时返回
	if(pRunSet->bTT_SM2)
	{
		if(ustimer_get_duration(pt104->us0_SM2Over)>60*USTIMER_SEC)
		{
			pt104->bRmtSM2 = false;
		}
	}
	
	// 根据遥测上送间隔查询遥测值
	cnt = rt_round(tRunPara.fYcTime*USTIMER_SEC);
	if(ustimer_get_duration(pt104->us0_yc) > cnt)
	{
		IEC104_SendYC(pt104);
		
		pt104->us0_yc += cnt;
	}

	// 定时任务
	IEC104_Timer(pt104);
	
	 // 1秒处理
	if(ustimer_get_duration(pt104->us0_sec) > USTIMER_SEC)
	{
		pt104->us0_sec += USTIMER_SEC;
		pt104->secs++;
	}

	return;
}
  
int IEC104_Ether_App(void)
{
	int net;
	
	for(net=0; net<CFG_ETH_MAX_LOGIC; net++)
	{
		IEC104_App(&g_t104[net]);
	}

	return 0;
}

 
void IEC104_Info_Printf_One(IEC104_DEF *p)
{
	iec_tbuf_t *pt ;
	
	rt_printf("\r\n通道属性:type=%d,chnl=%d,cNetIndex=%d\r\n",p->type,p->chnl,p->cNetIndex);
	if(p->state == 0)
	{
		return;
	}
	rt_printf("  连接计数:net=%d,app=%d,recv=%d\r\n",p->LinkCountNet,p->LinkCountApp,p->LinkCountRecv);
	rt_printf("  规约状态:state=%d,bSendChange=%d\r\n",p->state,p->bSendChange);
	rt_printf("  序号记录:SendNum=%d,RecvNum=%d,AckNum=%d\r\n",p->SendNum.ss,p->RecvNum.ss,p->AckNum.ss);
	rt_printf("帧接收处理:bRecvOk=%d,cTypeCounter=%d,cRecvLenth=%d,cRecvCnt=%d\r\n",p->bRecvOk,p->cTypeCounter,p->cRecvLenth,p->cRecvCnt);
	rt_printf("      计时:us0_yc=0x%x,us0_sec=0x%x,secs=%d\r\n",p->us0_yc,p->us0_sec,p->secs);
	rt_printf("测试帧发送:bStartTest=%d,us0_test_wait=%d,us0_test=%d\r\n",p->bStartTest,p->us0_test_wait,p->us0_test);
	pt= &p->TxBuf[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->TxBuf[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);

	return;
}
 
 
int IEC104_Info_Printf(void)
{
	int i;
	IEC104_DEF *p;

	// 网络104
	rt_printf("\r\n网络104:");
	for(i=0;i<CFG_ETH_MAX_LOGIC;i++)
	{
		p = &g_t104[i];
		IEC104_Info_Printf_One(p);
	}

	//串口104
	rt_printf("\r\n串口104:");
	for(i=0;i<CFG_UART_NUM_MAX;i++)	
	{
		if(tRunPara.tUartPara[i].wProtocol != PROTOCOL_104)
		{
			rt_printf("\r\n通道属性:非104通道(chnl=%d)\r\n",i);
			continue;
		}
		
		p = (IEC104_DEF *)g_tRsComm[i].ptBuf;
		IEC104_Info_Printf_One(p);
	}

	return 0;
} 

void IEC104_AddFrame(IEC104_DEF *pt104,BYTE *dat,int len,BYTE vsq,BYTE cot ,BYTE type) // 
{
	BYTE *pd=&pt104->arrSendBuf[6];
	*pd++=type;		
	*pd++=vsq;		
	*pd++=cot;		 //传送原因
	*pd++=0;		
	*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
	*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
	memcpy(pd,dat,len);
	IEC104_Send(pt104,FRAME_I_PRIOR_LOW,len+6);
}

void IEC104_AddFrame_dz(IEC104_DEF *pt104,BYTE *dat,int len,BYTE vsq,BYTE cot ,BYTE type,bool addone)
{
	BYTE *pd=&pt104->arrSendBuf[6];
	BYTE dat_tmp[256];
	int i,j=0,k=0;
	*pd++=type;		
	*pd++=vsq;		
	*pd++=cot;		 //传送原因
	*pd++=0;		
	*pd++=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
	*pd++=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
	if(addone)
	{	/*主站通过104遥调时，因管理机与级联间仍为101通讯，管理机对级联报文处理回给主站时，
		因级联给管理机的报文为2位地址位+4位数据位，管理机需自行增加1位地址位，即3位地址位+4位数据位以满足104报文要求*/
		for(i=0;i<(len+vsq);i++)
		{
			if((type==FOS_PAR_READ_MUL) || (type==FOS_PAR_READ_ONE))
			{
				if(2 == i%7)
				{
					dat_tmp[j++] = 0;
				}
				else
				{
					dat_tmp[j++]=dat[k++];
				}
			}
			else if((type==FOS_PAR_SET_MUL) || (type==FOS_PAR_SET_ONE))
			{
				if(2 == i%8)
				{
					dat_tmp[j++] = 0;
				}
				else
				{
					dat_tmp[j++]=dat[k++];
				}
			}
		}

		memcpy(pd,dat_tmp,len+vsq);
		len += vsq;
	}
	else
	{
		memcpy(pd,dat,len);
	}
	IEC104_Send(pt104,FRAME_I_PRIOR_LOW,len+6);
}

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


void IEC104_RmtSet_Echo(IEC104_DEF *pt104,BYTE *dat,BYTE cot,int len) // 
{

	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	int i;
	
	*pd++=dat[0];		//总召唤确认帧	  1   1
	*pd++=dat[1];		//可变结构限定词  2
	*pd++=cot;			//传送原因： 否定应答
	*pd++=0;			//传送原因：               4
	*pd++=dat[4];		//应用服务数据单元地址 5
	*pd++=dat[5];		//应用服务数据单元地址 6
	*pd++=dat[6];		//信息体地址L  7
	*pd++=dat[7];		//信息体地址H  8
	*pd++=dat[8];		//信息体地址H  9
	for(i=0;i<len;i++)
	{
		*pd++=dat[9+i];		//信息值
	}
        
	IEC104_Send(pt104,FRAME_I,9+len); //应答
}

void IEC104_setion_chg_msg(IEC104_DEF *pt104,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 = pt104->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 IEC104_SectionWrite(IEC104_DEF *pt104,u8 *ps)			//确认帧
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	BYTE cot;	// 定值区为0，肯定应答，其他否定应答
	int num=0;
	WORD section;
	if(pRunSet->wEquTypeManager) IEC104_setion_chg_msg(pt104,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[num++]=200;		//总召唤确认帧	  1   1
	pd[num++]=1;		//可变结构限定词  2
	pd[num++]=cot;			//传送原因： 否定应答
	pd[num++]=0;			//传送原因：      
	pd[num++]=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
	pd[num++]=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
	pd[num++]=0;		//信息体地址L  7
	pd[num++]=0;		//信息体地址H  8
	pd[num++]=0;		//信息体地址H  9
	pd[num++]=ps[0];	//当前定值区
	pd[num++]=ps[1];	//当前定值区
	IEC104_Send(pt104,FRAME_I,num); //应答
	
}

void IEC104_SectionRead(IEC104_DEF *pt104)			// 读定值区
{

	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	BYTE cot = IEC_COT_ACTCON;	// 定值区为0，肯定应答，其他否定应答
	int num=0;
	pd[num++]=201;		//总召唤确认帧	  1   1
	pd[num++]=1;		//可变结构限定词  2
	pd[num++]=cot;			//传送原因： 否定应答
	pd[num++]=0;			//传送原因：      
	pd[num++]=(BYTE)tRunPara.byAddr;			//应用服务数据单元地址 
	pd[num++]=(BYTE)(tRunPara.byAddr>>8);			//应用服务数据单元地址 6
	pd[num++]=0;		//信息体地址L  7
	pd[num++]=0;		//信息体地址H  8
	pd[num++]=0;		//信息体地址H  9
	pd[num++]=(u8)m_runsection;	//当前定值区
	pd[num++]=(u8)(m_runsection>>8);	//当前定值区
	pd[num++]=0;		//最小定值区
	pd[num++]=0;		//最小定值区
	pd[num++]=SEC_NUMBER-1;		//最大定值区1
	pd[num++]=0;		//最大定值区2
	
	IEC104_Send(pt104,FRAME_I,num); //应答
	
}


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






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

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

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
 
static void IEC104_Direct_Echo(IEC104_DEF *pt104,DWORD InfoAddr) 
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	BYTE vsq=0;
	int i;
	int filenum;
	struct dir_file_struct *pdir,*pdirfree;
	// 根据信息体地址，判断读读哪个目录，获取目录信息

	*pd++=126;		//总召唤确认帧	  1   1
	*pd++=vsq;		//可变结构限定词  2
	*pd++=5;		//传送原因：               3
	*pd++=0;		//传送原因：               4
	
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 5
	*pd++=(BYTE)(tRunPara.byAddr>>8);	//应用服务数据单元地址 6
	
	*pd++=(BYTE)InfoAddr;		 //信息体地址L  7
	*pd++=(BYTE)(InfoAddr>>8);	  //信息体地址H  8
	*pd++=(BYTE)(InfoAddr>>16);	 //信息体地址H  9

	pdir=hf_get_dir_file(IEC_Getdir(InfoAddr),&filenum);
	if(pdir==NULL)
	{
		IEC104_Send(pt104,FRAME_I,9); //无文件应答
		return;
	}
	pdirfree=pdir;
	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<(filenum-1))// 目录超出一帧上送范围，分帧，VSQ置后续标准位
		{
			pt104->arrSendBuf[7]=vsq|0x80;
			IEC104_Send(pt104,FRAME_I,9+vsq*13);  //有文件应答
			vsq=0;
			pd=&pt104->arrSendBuf[15];	
		}
		pdir++;		
		
	}
	rt_free(pdirfree);	
	if(vsq>0)
	{
		pt104->arrSendBuf[7]=vsq;
		if(vsq>1)
		{
			pt104->arrSendBuf[7]|=0x80;

		}
		IEC104_Send(pt104,FRAME_I,9+vsq*13);  //有文件应答
	}
	else
	{
		IEC104_Send(pt104,FRAME_I,9); //无文件应答

	}
}

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

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


static void IEC104_EchoFileReady(IEC104_DEF *pt104,DWORD InfoAddr,WORD filename)
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	int filelenth,sectionnum;
	// 根据信息体地址，判断读读哪个目录，获取目录信息

	filelenth=hf_get_file_inf(IEC_Getdir(InfoAddr),filename,&sectionnum);
	*pd++=120;		//总召唤确认帧	  1   1
	*pd++=1;		//可变结构限定词  2
	*pd++=13;		//传送原因：13               3
	*pd++=0;		//传送原因：               4
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 5
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址 6
	
	*pd++=(BYTE)InfoAddr;		 //信息体地址L  7
	*pd++=(BYTE)(InfoAddr>>8);	  //信息体地址H  8
	*pd++=(BYTE)(InfoAddr>>16);	 //信息体地址H  9
        /*文件名称*/
        *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否定定认可*/
	 }
	pt104->tfile.filelenth=filelenth;
	pt104->tfile.setctionnum=sectionnum;
	pt104->tfile.filename=filename;	 
	pt104->tfile.InfoAddr=InfoAddr;
	pt104->tfile.filechecksum=0;	
	IEC104_Send(pt104,FRAME_I,15); //应答
}

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

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


static void IEC104_EchoSectionReady(IEC104_DEF *pt104,DWORD InfoAddr,WORD filename,BYTE section)
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	int setctionlenth;
	char *pdat=NULL;


	IEC104_FreeFile(pt104);	

	*pd++=121;		//总召唤确认帧	  1   1
	*pd++=1;		//可变结构限定词  2
	*pd++=13;		//传送原因：13               3
	*pd++=0;		//传送原因：               4
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 5
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址 6
	*pd++=(BYTE)InfoAddr;		 //信息体地址L  7
	*pd++=(BYTE)(InfoAddr>>8);	  //信息体地址H  8
	*pd++=(BYTE)(InfoAddr>>16);	 //信息体地址H  9
        /*文件名称*/
        *pd++ = (BYTE)(filename);  // 10
        *pd++ = (BYTE)(filename>>8); // 11
	if(pt104->tfile.InfoAddr==InfoAddr&&pt104->tfile.filename==filename&&section<=pt104->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
		pt104->tfile.pdat=pdat;			//保存节内容地址，后续需要释放
		pt104->tfile.sectionlenth=setctionlenth;
		pt104->tfile.transsection=section;
		pt104->tfile.bFiletransing=true;	   
		pt104->tfile.bSegtransing=false;
		pt104->tfile.us0_file_trans=ustimer_get_origin();
		pt104->tfile.transBytes=0;
		pt104->tfile.sectionchecksum=0;
	 }
	IEC104_Send(pt104,FRAME_I,16); //应答

}

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

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


static void  IEC104_SegmentSend(IEC104_DEF *pt104,DWORD InfoAddr,WORD filename,BYTE section)
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	BYTE sectionchecksum=0;  //节校验和
	BYTE segmentbytes,cot;
	int i;

	cot=13;
	segmentbytes=0;
	if(pt104->tfile.bFiletransing
		&&pt104->tfile.InfoAddr==InfoAddr
		&&pt104->tfile.filename==filename
		&&section==pt104->tfile.transsection)  //文件名相同 节相同
	{
		if(pt104->tfile.transBytes+FILE_SEGMENT_BYTES>=pt104->tfile.sectionlenth)
		{
			segmentbytes=pt104->tfile.sectionlenth%FILE_SEGMENT_BYTES;  // 最后的段的字节数
		}
		else
		{
			segmentbytes=FILE_SEGMENT_BYTES;
		}

		pt104->tfile.sectionchecksum=sectionchecksum;
		pt104->tfile.us0_file_trans=ustimer_get_origin();	   	
		pt104->tfile.bSegtransing=true;
	}
	else
	{
		cot|=0x40;
		IEC104_FreeFile(pt104);	

	}
	*pd++=125;		//总召唤确认帧	  1   1
	*pd++=1;		//可变结构限定词  2
	*pd++=cot;		//传送原因：13               3
	*pd++=0;		//传送原因：               4
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 5
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址 6
	*pd++=(BYTE)InfoAddr;		 //信息体地址L  7
	*pd++=(BYTE)(InfoAddr>>8);	  //信息体地址H  8
	*pd++=(BYTE)(InfoAddr>>16);	 //信息体地址H  9
        /*文件名称*/
        *pd++ = (BYTE)(filename);  // 10
        *pd++ = (BYTE)(filename>>8); // 11
	*pd++=section;  //节名  12
	*pd++=segmentbytes;  //段的长度 13
	for(i=0;i<segmentbytes;i++)
	{
		*pd=pt104->tfile.pdat[pt104->tfile.transBytes++];
		sectionchecksum+=*pd++;
	}
	IEC104_Send(pt104,FRAME_I,13+segmentbytes); //应答
}




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

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


static void  IEC104_EchoLastSection(IEC104_DEF *pt104,WORD filename,BYTE section,BYTE LSQ)
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	*pd++=123;		//总召唤确认帧	  1   1
	*pd++=1;		//可变结构限定词  2
	*pd++=13;		//传送原因：13               3
	*pd++=0;		//传送原因：               4
	*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 5
	*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址 6
	*pd++=(BYTE)pt104->tfile.InfoAddr;		 //信息体地址L  7
	*pd++=(BYTE)(pt104->tfile.InfoAddr>>8);	  //信息体地址H  8
	*pd++=(BYTE)(pt104->tfile.InfoAddr>>16);	 //信息体地址H  9
        /*文件名称*/
        *pd++ = (BYTE)(filename);  // 10
        *pd++ = (BYTE)(filename>>8); // 11
	*pd++=section;  //节名  12

	switch(LSQ)
	{
		case 1:
			*pd++=1;						//LSQ=1：不带停止激活文件传输13
			*pd++=pt104->tfile.filechecksum;			//文件校验和 14
			
			break;
		case 3:
			*pd++=3;						//LSQ=3：不带停止激活的节传输
			*pd++=pt104->tfile.sectionchecksum;			//节校验和			
			break;
	}	

	IEC104_Send(pt104,FRAME_I,14); //应答
}


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

更新信息：
更新日志1：
日期：	  
修改者：	  
修改内容：  
修改原因：
***************************************************************************/
bool dddd=false;

void  IEC104_SegmentContinue(IEC104_DEF *pt104)
{
	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	BYTE sectionchecksum=0;  //节校验和
	BYTE segmentbytes;
	int i;

	if(dddd)
	{
		IEC104_Direct_Echo(pt104,0x0cea);
		dddd=false;
	}

	
	if(!pt104->tfile.bSegtransing)return;

	if(IEC104_BufFree(&pt104->TxBuf[1]) < (IEC_TBUF_NUM>>2))
	{
		return;
	}	
		if(pt104->tfile.transBytes<pt104->tfile.sectionlenth)  // 段还未传输完毕
		{
			if(pt104->tfile.transBytes+FILE_SEGMENT_BYTES>=pt104->tfile.sectionlenth)
			{
				segmentbytes=pt104->tfile.sectionlenth%FILE_SEGMENT_BYTES;  // 最后的段的字节数
			}
			else 
			{
				segmentbytes=FILE_SEGMENT_BYTES;
			}
			*pd++=125;		//总召唤确认帧	  1   1
			*pd++=1;		//可变结构限定词  2
			*pd++=13;		//传送原因：13               3
			*pd++=0;		//传送原因：               4
			*pd++=(BYTE)tRunPara.byAddr;		//应用服务数据单元地址 5
			*pd++=(BYTE)(tRunPara.byAddr>>8);		//应用服务数据单元地址 6
			*pd++=(BYTE)pt104->tfile.InfoAddr;		 //信息体地址L  7
			*pd++=(BYTE)(pt104->tfile.InfoAddr>>8);	  //信息体地址H  8
			*pd++=(BYTE)(pt104->tfile.InfoAddr>>16);	 //信息体地址H  9
		        /*文件名称*/
		        *pd++ = (BYTE)(pt104->tfile.filename);  // 10
		        *pd++ = (BYTE)(pt104->tfile.filename>>8); // 11
			*pd++=pt104->tfile.transsection;  //节名  12
			
			*pd++=segmentbytes;  //段的长度 13

			for(i=0;i<segmentbytes;i++)
			{
				*pd=pt104->tfile.pdat[pt104->tfile.transBytes++];
				sectionchecksum+=*pd++;
			}
			pt104->tfile.sectionchecksum+=sectionchecksum;
			pt104->tfile.us0_file_trans=ustimer_get_origin();	   	
			IEC104_Send(pt104,FRAME_I,13+segmentbytes); //应答
			
		}
		else
		{
			IEC104_EchoLastSection(pt104,pt104->tfile.filename,pt104->tfile.transsection,3);
			IEC104_FreeFile(pt104);	

		}

}


void IEC104_File_Respone(IEC104_DEF *pt104,BYTE *dat,BYTE cot,BYTE SCAFQ) // 
{

	BYTE *pd=&pt104->arrSendBuf[6];  //从第7个字节开始
	
	*pd++=dat[0];		//总召唤确认帧	  1   1
	*pd++=dat[1];		//可变结构限定词  2
	*pd++=cot;		//传送原因： 否定应答
	*pd++=0;			//传送原因：               4
	*pd++=dat[4];		//应用服务数据单元地址 5
	*pd++=dat[5];		//应用服务数据单元地址 6
	*pd++=dat[6];		//信息体地址L  7
	*pd++=dat[7];		//信息体地址H  8
	*pd++=dat[8];		//信息体地址H  9
        
       *pd++=dat[9];  // 10 
       *pd++=dat[10]; // 11
	*pd++=dat[11];  //节名  12
	*pd++=SCAFQ;  //限定词
	IEC104_Send(pt104,FRAME_I,13); //应答

}


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

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

void IEC104_File_Echo(IEC104_DEF *pt104,BYTE *dat) //dat 从报文的帧类型开始
{
	BYTE COT,SCQ;  //传送原因
	WORD filename,section; //文件名 节名
	DWORD InfoAddr;       //信息体地址
	
	COT=dat[2];
	InfoAddr=dat[6]+(dat[7]<<8)+(dat[8]<<16); 
	filename=dat[9]+(dat[10]<<8); //文件名
	section=dat[11]; //节名
	SCQ = dat[12]; //选择和召唤限定词


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


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


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

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


void IEC104_File_Sure(IEC104_DEF *pt104,BYTE *dat) //dat 从报文的帧类型开始
{

	BYTE COT,AFQ;  //传送原因
	WORD filename,section; //文件名 节名
	DWORD InfoAddr;       //信息体地址
	
	COT=dat[2];
	InfoAddr=dat[6]+(dat[7]<<8)+(dat[8]<<16); 
	filename=dat[9]+(dat[10]<<8); //文件名
	section=dat[11]; //节名
	AFQ = dat[12]; //选择和召唤限定词

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

			IEC104_FreeFile(pt104);		
			if(pt104->tfile.transsection<pt104->tfile.setctionnum-1)//不是最后的节
			{		
				pt104->tfile.filechecksum+=pt104->tfile.sectionchecksum;	//文件校验和
				section+=1;
				IEC104_EchoSectionReady(pt104,InfoAddr,filename,section);//下一节准备好						
			}
			else if(section>=pt104->tfile.setctionnum-1) //最后的节
			{
				IEC104_EchoLastSection(pt104,filename,section,1);		//LSQ = 3;      最后的段
			}
			break;
		case 4:						//节传输的否定认可
			IEC104_FreeFile(pt104);	
			IEC104_EchoSectionReady(pt104,InfoAddr,filename,section);//节准备好报文
			break;
		default:
			break;
	}                            
}

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


