/************************************************************************* 
* 版权所有：	
* 文件版本： V1.00
* 文件名称：SOE.c
* 生成日期： 2008年9月1日
* 作	者： xxxxxx
* 功	能： 事件记录处理
* 更新信息：
	2015-11-02  
		增强了SOE处理时资源竞争保护，此次增强基于以下假设
		1、除了优先级别低于主循环的地方外，可以在其它各个优先级别调用SOE记录函数。
		2、处理SOE必须在主循环中，也就是说优先级别最低的地方。
*/
/*------------------------------- 头文件 --------------------------------------
*/
#include "head.h"


/*------------------------------- 宏定义 --------------------------------------
*/
#define RCDTYPE_EVENT	1
#define RCDTYPE_YX		2

#define SOE_SAVE_BUF_NUM 	64


/*-------------------------------类型结构--------------------------------------
*/
/*------------------------------ 全局变量 -------------------------------------
*/
u32 		g_ev_words[EV_NUMBER_WORDS];  	// 所有事件的状态保存
SOE_QUEUE 	g_soe_queue;  					// 事件队列
SOE_INF 	g_soe_info;       				// 系统事件的信息结构
int			g_soe_test;

static EVENT_SAVE g_soe_sbuf[SOE_SAVE_BUF_NUM]; // SOE保存加速BUF
#ifdef ADD_DEBUG_RECORD_AUTO_SHOW_FUN
const u16 ExecuteEventIdTbl[]={EV_GL1TZ,EV_GL2TZ,EV_GL3TZ,
                               EV_LX1TZ,EV_LX2TZ,
                            #ifdef DISP_SET_LX3
	                           EV_LX3TZ,			
                            #endif
                               EV_GLJS,EV_LXJS,EV_FA_GL_TZ,	
                            #if defined GD_AREA_CHAOZHOU || defined GD_AREA_SHANTOU  || defined GD_AREA_ECZD_2020 || defined GD_AREA_MAIN_2020
	                           EV_FA_GL_TZ2,
                            #endif 
                               EV_FA_LX_TZ,EV_AR1,EV_AR2,EV_AR3,EV_ARBS,EV_U0TZ,EV_XBBS,EV_BS_ALL,
       	                       EV_BS_FZMORE,EV_TQHZ,EV_GGDY_TZ,EV_GDDY_TZ,EV_GDY_TZ,EV_DDY_TZ,EV_LOWPL_TZ,EV_GPL_TZ,EV_DPL_TZ, 
       	                       EV_GOOSE_QC_QD,EV_GOOSE_GL_QD,EV_GOOSE_HZ_QD,EV_GOOSE_GL_TZ, 
                            #if defined GD_AREA_ZHONGSHAN || defined GD_AREA_ZHONGSHAN_2020 || defined GD_AREA_MAIN_2020
	                           EV_GOOSE_GL2_TZ, 		// 过流2段跳闸
                            #endif
                            #ifdef GD_AREA_ZHONGSHAN_2020
	                           EV_GOOSE_HZ_GL1,EV_GOOSE_HZ_GL2,EV_GOOSE_HZ_LX,EV_BSTZ,	
                            #endif                            
	                           EV_GOOSE_LX_TZ,EV_GOOSE_SDLOST_TZ,EV_GOOSE_ERRLOST_TZ,EV_GOOSE_EXT_TZ,EV_BSTZ,EV_FA_FZ,
		                       EV_BS_U0,EV_HHGZ,EV_HHGZ_P,EV_HHGZ_L,EV_BSTZ_FA,EV_FA_HZ,EV_FA_LL_HZ,EV_FA_FZ,	                           
	                        #ifdef XDL_ZT	
	                           EV_LX_XDL_TZ,		// 零序小电流跳闸	
	                        #endif
	                           EV_GOOSE_ERR,
	                           EV_SYS_GOOSE_ERR,
	                           EV_PT1ERR,EV_PT2ERR,
	                           EV_BS_UF_P,EV_BS_UF_L,
	                           
	                           
};    //TODO: add param
#define  EXECUTE_EVENT_NUM   (sizeof(ExecuteEventIdTbl)/sizeof(u16))
#endif



/*------------------------------ 函数声明 -------------------------------------
*/

/*------------------------------ 外部函数 -------------------------------------
外部函数供其它实体文件引用，必须仔细检查传入参数的合法性.
*/
// 遥信取SOE BUF，因为遥信处理在最高优先级，所以可以不用屏蔽中断
// 如果采用屏蔽中断的方式，在10个遥信同时动作的情况下，一次遥信处理会多消耗38us。
EVENT_SAVE *soe_get_buffer_yx(void)
{
	u16 i,head;
	head = g_soe_queue.head.n++;

	if(g_soe_queue.head.n == g_soe_queue.tail_eep.n)
	{
		g_soe_queue.tail_eep.n++;
		g_soe_queue.stat_eep_over++;
	}

	// 缓冲区满了，调整tail_send和tail_ack
	for(i=0;i<COMM_CHANNEL_NUM;i++) 
	{
		if(g_soe_queue.head.n==g_soe_queue.tail_send[i].n)
		{
			g_soe_queue.tail_send[i].n++;
			g_soe_queue.stat_send_over[i]++;
		}
		
		if(g_soe_queue.head.n==g_soe_queue.tail_ack[i].n)
		{
			g_soe_queue.tail_ack[i].n++;
			g_soe_queue.stat_ack_over[i]++;
		}
	}

	// 保存标志清零，防止不该有的事件确认
	g_soe_queue.soe[head].es_flag = 0;

	// 唤醒主循环
	mainloop_wakeup();

	return &g_soe_queue.soe[head];
}

 EVENT_SAVE *soe_get_buffer(u16 *extarg)
{
	u16 i,head;
	uint32_t  flags;
	
	rt_irq_save(flags);
	head = g_soe_queue.head.n++;
	rt_irq_restore(flags);	
	
	rt_irq_save(flags);
	if(g_soe_queue.head.n == g_soe_queue.tail_eep.n)
	{
		g_soe_queue.tail_eep.n++;
		g_soe_queue.stat_eep_over++;
	}
	rt_irq_restore(flags);	

	// 缓冲区满了，调整tail_send和tail_ack
	for(i=0;i<COMM_CHANNEL_NUM;i++) 
	{
		rt_irq_save(flags);
		if(g_soe_queue.head.n==g_soe_queue.tail_send[i].n)
		{
			g_soe_queue.tail_send[i].n++;
			g_soe_queue.stat_send_over[i]++;
		}
		
		if(g_soe_queue.head.n==g_soe_queue.tail_ack[i].n)
		{
			g_soe_queue.tail_ack[i].n++;
			g_soe_queue.stat_ack_over[i]++;
		}
		rt_irq_restore(flags);

	}

	// 保存标志清零，防止不该有的事件确认
	g_soe_queue.soe[head].es_flag = 0;

	// 唤醒主循环
	mainloop_wakeup();

	if(extarg!=NULL)*extarg=head;
	return &g_soe_queue.soe[head];
}


void soe_init(void)
{	
	u32  i,addr;
	u16 index;
	EVENT_SAVE tEvt;

	// 清零事件状态和事件队列
	// 以下1行需屏蔽
	// 原因1:定值初始化中会对开出软压板定值及对应事件状态初始化，此处会把该初始化清0
	// 原因2:手动清除事件和操作时，会把当前事件的标志清0，会对事件的管理造成混乱
	//memset(&g_ev_words,0,sizeof(g_ev_words));
	memset(&g_soe_queue,0,sizeof(g_soe_queue));
	
	// 事件保存信息初始化
	g_soe_info.wEvIndex = -1;
	g_soe_info.wEvNumber= 0;
	g_soe_info.wEvID    = 0;
	g_soe_info.wEvAck 	= 0;
	g_soe_info.wOpIndex = -1;
	g_soe_info.wOpNumber= 0;
	g_soe_info.wOpID    = 0;

    g_soe_info.wVipIndex = -1;
    g_soe_info.wVipNumber= 0;
    g_soe_info.wVipID    = 0;

#ifdef ADD_DEBUG_RECORD_FUN
    g_soe_info.wDebugIndex = -1;
    g_soe_info.wDebugNumber= 0;
    g_soe_info.wDebugID    = 0;
#endif
    // 统计事件记录信息
    for(i=0; i<MAX_EVENT_NUMBER; i++)
    {
        addr=EEP_SOE_ADDR+i*(sizeof(EVENT_SAVE));
        e2prom_read(addr,(u8 *)&tEvt,sizeof(EVENT_SAVE));
        if(tEvt.es_flag!=SOE_ID_MASK)
        {
            break;
        }
        if((s16)(tEvt.es_id - g_soe_info.wEvID) > 0)
        {
            g_soe_info.wEvIndex=i;
            g_soe_info.wEvID=tEvt.es_id;
        }
        g_soe_info.wEvNumber++;
    }
    g_soe_info.wEvID++;

	// 统计操作记录信息
	for(i=0;i<MAX_OPERATE_NUMBER;i++)
	{
		addr=EEP_OPERATE_ADDR+i*(sizeof(EVENT_SAVE));
		e2prom_read(addr,(u8 *)&tEvt,sizeof(EVENT_SAVE)); 
		if(tEvt.es_flag!=SOE_ID_MASK)
		{
			break;
		}
		if((s16)(tEvt.es_id - g_soe_info.wOpID) > 0)
		{
			g_soe_info.wOpIndex=i;
			g_soe_info.wOpID=tEvt.es_id;
		}
		g_soe_info.wOpNumber++;
	}
	g_soe_info.wOpID++;
	// 统计关键记录信息
	for(i=0;i<MAX_VIP_NUMBER;i++)
	{
		addr=EEP_VIP_ADDR+i*(sizeof(EVENT_SAVE));
		e2prom_read(addr,(u8 *)&tEvt,sizeof(EVENT_SAVE)); 
		if(tEvt.es_flag!=SOE_ID_MASK)
		{
			break;
		}
		if((s16)(tEvt.es_id - g_soe_info.wVipID) > 0)
		{
			g_soe_info.wVipIndex=i;
			g_soe_info.wVipID=tEvt.es_id;
		}
		g_soe_info.wVipNumber++;
	}
	g_soe_info.wVipID++;
//#ifdef ADD_DEBUG_RECORD_FUN
    for(i=0; i<MAX_DEBUG_RECORD_NUMBER; i++)
    {
        addr=EEP_DEBUG_RECORD_ADDR+i*(sizeof(EVENT_SAVE));
        e2prom_read(addr,(u8 *)&tEvt,sizeof(EVENT_SAVE));
        if(tEvt.es_flag!=SOE_ID_MASK)
        {
            break;
        }
        //if((s16)(tEvt.es_id - g_soe_info.wVipID) > 0)   //可考虑去除此判断
        {
            g_soe_info.wDebugIndex=i;
            //g_soe_info.wVipID=tEvt.es_id;
        }
        g_soe_info.wDebugNumber++;
    }
//#endif

	// 如果SOE上电续传没有投入，直接返回
	if(pRunSet->bTT_SoeResumePower == 0)
	{
		return;
	}

	// 将后台没有确认的事件导入事件队列
	index = g_soe_info.wEvIndex+1;
	for(i=0;i<MAX_EVENT_NUMBER;i++) 
	{
		// 调整索引
		if(index == MAX_EVENT_NUMBER)
		{
			index = 0;
		}

		// 取出事件
		addr=EEP_SOE_ADDR+index*(sizeof(EVENT_SAVE));
		e2prom_read(addr,(u8 *)&tEvt,sizeof(EVENT_SAVE)); 

		// 如果是没有确认的事件，放入事件队列
	//	rt_printf("soe_init:index=%04d,flag=%02x,ack=%02x.\r\n",index,tEvt.es_flag,tEvt.es_ack);
		if((tEvt.es_flag == SOE_ID_MASK) && (tEvt.es_ack == SOE_ID_ACK_NONE))
		{
			EVENT_SAVE *ev=soe_get_buffer(NULL);
			*ev = tEvt ;
			ev->es_id = addr-EEP_SOE_BASE;
			
			g_soe_info.wEvAck++;
		}
		
		index++;
	}

	// 保证事件队列中没有确认的事件不会重新保存
	g_soe_queue.tail_eep.n = g_soe_queue.head.n;

}

int soe_ev_set(u16 code,u8 v)
{
	if(code >= EV_NUMBER)
	{
		return -1;
	}
	
	if(code < EV_PUB_START)
	{
		g_sw[code/EV_SW_NUM].di[code%EV_SW_NUM+SW_DI_NUM] = v ? SW_DI_TYPE_ON : SW_DI_TYPE_OFF;


	}
	else
	{
		g_sw_pub.di[code-EV_PUB_START+PUB_DI_NUM] = v ? SW_DI_TYPE_ON : SW_DI_TYPE_OFF;
	}
	
	if(v)
	{
		g_ev_words[code/32]|=(1<<(code%32));  // 状态更新
	}
	else
	{
		g_ev_words[code/32]&=~(DWORD)(1<<(code%32)); //状态更新
	}

	return 0;
}


u16 soe_lp2cp(u8 ev_type,u16 ev_code,s32 is_mix)
{
	int index;
	u16 cp = 0;
	u8 slot,no;

	switch(ev_type&SOE_TYPE_MASK)
	{
	// 事件通讯点从逻辑单点数组中找
	case SOE_TYPE_EV:
		if(ev_code < EV_PUB_END)
		{
			// index = ev_code + PUB_DI_NUM+(SW_DI_NUM*SWITCH_NUM_MAX);
			if(ev_code < EV_PUB_START)
			{
				int sw;
				sw = ev_code/EV_SW_NUM;
				index = ev_code%EV_SW_NUM;
				index = PUB_DI_NUM_ALL + sw*SW_DI_NUM_ALL + SW_DI_NUM + index;
			}
			else
			{
				index = ev_code -EV_PUB_START + PUB_DI_NUM;
			}
			cp = g_lp[index].cp;
		}
	break;
	// 遥信事件通讯点从逻辑单点数组中找
	case SOE_TYPE_YX:
		slot = ev_code>>8;
		no = ev_code&0xff;
		index = plc_yx2index(g_di_st[slot][no].owner,g_di_st[slot][no].type);
		if(index >= 0)
		{
			cp = g_lp[index].cp;
		}
	break;
	// 级联事件通讯点
	case SOE_TYPE_LNK:
		if(is_mix==0)cp = ev_code;  //若是级联点是合并点，不上送，由PLC逻辑上送
	break;
	// 双点事件通讯点从转发点表中的双点部分找
	case SOE_TYPE_DPI:
		if(ev_code < g_table_head->di_db_num)
		{
			cp = g_di_db_table[ev_code].cp;
		}
	break;
	// PLC事件通讯点从PLC结构数组中找
	case SOE_TYPE_PLC:
		if(ev_code < g_plc_num)
		{
			cp = g_plc[ev_code].cp;
		}
	break;
	// 测试事件通讯点直接传送
	case SOE_TYPE_TST:
		cp = ev_code;
	break;
	// 操作事件不送后台
	case SOE_TYPE_OPT:
		cp = 0;
	break;

	}

	return cp;
}

void soe_ext_arg(u16 index,u32 sw)
{
	EVENT_EXT_ARG *pArg;
	int ui_begin;
	if(sw>0&&sw<=SWITCH_NUM_MAX)
	{
		sw-=1;
	}
	else
	{
		sw=0;
	}
	pArg=&g_soe_queue.tExtArg[index];  //附加参数	
	ui_begin = UI_SW_INDEX_BEGIN(sw);	
	pArg->arg[0] = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IA].m2[0]), 256, g_ui[ui_begin + SW_AC_IA].m2_factor_k);
	pArg->arg[1] = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IB].m2[0]), 256, g_ui[ui_begin + SW_AC_IB].m2_factor_k);
	pArg->arg[2] = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IC].m2[0]), 256, g_ui[ui_begin + SW_AC_IC].m2_factor_k);
	pArg->arg[3] = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_I0].m2[0]), 256, g_ui[ui_begin + SW_AC_I0].m2_factor_k);

	if(pRunSet->tSwSet[sw].bTT_Power_v2)
	{
		pArg->arg[4] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UA2].m2[0]), 256, g_ui[PUB_AC_UA2].m2_factor_k);
		pArg->arg[5] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UB2].m2[0]), 256, g_ui[PUB_AC_UB2].m2_factor_k);
		pArg->arg[6] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UC2].m2[0]), 256, g_ui[PUB_AC_UC2].m2_factor_k);
		pArg->arg[7] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U02].m2[0]), 256, g_ui[PUB_AC_U02].m2_factor_k);				
		pArg->arg[8] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UAB2].m2[0]), 256, g_ui[PUB_AC_UAB2].m2_factor_k);
		pArg->arg[9] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UBC2].m2[0]), 256, g_ui[PUB_AC_UBC2].m2_factor_k);
		pArg->arg[10] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UCA2].m2[0]), 256, g_ui[PUB_AC_UCA2].m2_factor_k);
	}
	else
	{
	pArg->arg[4] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UA1].m2[0]), 256, g_ui[PUB_AC_UA1].m2_factor_k);
	pArg->arg[5] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UB1].m2[0]), 256, g_ui[PUB_AC_UB1].m2_factor_k);
	pArg->arg[6] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UC1].m2[0]), 256, g_ui[PUB_AC_UC1].m2_factor_k);
	pArg->arg[7] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);				
	pArg->arg[8] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UAB1].m2[0]), 256, g_ui[PUB_AC_UAB1].m2_factor_k);
	pArg->arg[9] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UBC1].m2[0]), 256, g_ui[PUB_AC_UBC1].m2_factor_k);
	pArg->arg[10] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UCA1].m2[0]), 256, g_ui[PUB_AC_UCA1].m2_factor_k);

	}
	pArg->arg[11] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_US1].m2[0]), 256, g_ui[PUB_AC_US1].m2_factor_k);				
	pArg->arg[12] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_US2].m2[0]), 256, g_ui[PUB_AC_US2].m2_factor_k);	

	#ifdef GD_AREA_ECZD_MAOMING
		pArg->arg[13] = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UAB2].m2[0]), 256, g_ui[PUB_AC_UAB2].m2_factor_k);
		pArg->arg[14] = g_sw_pub.ac_in[PUB_AC_IN_UZ1];
	#endif
	
	pArg->type[0]=SW_AC_IA;
	pArg->type[1]=SW_AC_IB;
	pArg->type[2]=SW_AC_IC;
	pArg->type[3]=SW_AC_I0;
	if(pRunSet->tSwSet[sw].bTT_Power_v2)
	{
		pArg->type[4]=PUB_AC_UA2|0x80;
		pArg->type[5]=PUB_AC_UB2|0x80;
		pArg->type[6]=PUB_AC_UC2|0x80;
		pArg->type[7]=PUB_AC_U02|0x80;
		pArg->type[8]=PUB_AC_UAB2|0x80;
		pArg->type[9]=PUB_AC_UBC2|0x80;
		pArg->type[10]=PUB_AC_UCA2|0x80;	
	}
	else
	{
	pArg->type[4]=PUB_AC_UA1|0x80;
	pArg->type[5]=PUB_AC_UB1|0x80;
	pArg->type[6]=PUB_AC_UC1|0x80;
	pArg->type[7]=PUB_AC_U01|0x80;
	pArg->type[8]=PUB_AC_UAB1|0x80;
	pArg->type[9]=PUB_AC_UBC1|0x80;
	pArg->type[10]=PUB_AC_UCA1|0x80;	
	}	
	pArg->type[11]=PUB_AC_US1|0x80;
	pArg->type[12]=PUB_AC_US2|0x80;

	#ifdef GD_AREA_ECZD_MAOMING
		pArg->type[13]=PUB_AC_UAB2|0x80;
		pArg->type[14]=PUB_AC_IN_UZ1|0x80;
	#endif

	pArg->sw=sw;
	
}
 
#ifdef SOE_PARA_ALL_SAVE
void soe_record_allArg(s32 *arg,int sw)
{

	int ui_begin;
	if(sw>0&&sw<=SWITCH_NUM_MAX)
	{
		sw-=1;
	}
	else
	{
		sw=0;
	}
	ui_begin = UI_SW_INDEX_BEGIN(sw);	
	*arg++ = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UAB1].m2[0]), 256, g_ui[PUB_AC_UAB1].m2_factor_k);
	*arg++ = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UBC1].m2[0]), 256, g_ui[PUB_AC_UBC1].m2_factor_k);
	*arg++ = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_UCA1].m2[0]), 256, g_ui[PUB_AC_UCA1].m2_factor_k);
	*arg++ = _Mul_Div_U(sqrt_32fix(g_ui[PUB_AC_U01].m2[0]), 256, g_ui[PUB_AC_U01].m2_factor_k);				

	*arg++ = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IA].m2[0]), 256, g_ui[ui_begin + SW_AC_IA].m2_factor_k);
	*arg++= _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IB].m2[0]), 256, g_ui[ui_begin + SW_AC_IB].m2_factor_k);
	*arg++ = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_IC].m2[0]), 256, g_ui[ui_begin + SW_AC_IC].m2_factor_k);
	*arg++ = _Mul_Div_U(sqrt_32fix(g_ui[ui_begin + SW_AC_I0].m2[0]), 256, g_ui[ui_begin + SW_AC_I0].m2_factor_k);

}
#endif
 void soe_record_ev(u16 code,u8 value,long v1,long v2,long v3 )
{	
	u16 extarg;
	EVENT_STRUCT *ev;
	u32 sw;
	bool allarg=false;
	if(code<EV_PUB_START)
	{
		sw=(code/EV_SW_NUM)+1;
	}
	else
	{
		sw=0;
	}	
	
	if(code<EV_PUB_START) // add by sunxi 增加故障记录标示
	{
	#ifdef SOE_RECORD_DELAY
		if(!gb_wake_soe)	//首次上电时，对前一次上电产生的闭锁事件不做延时处理，避免逻辑运行出错
		{
			if(tAllSOE[code%EV_SW_NUM].soe_def&SOE_TYPE_DELAY_FHZ && (value==0))
			{	/*清SOE事件应在置SOE事件完成上送后再清，如果当前SOE事件未置则一直等待，
				避免因分合闸延时上送SOE导致先清了SOE事件再送置SOE事件导致上送COS出错（正确是先置再清）*/
				if(g_soe_queue.fzsoe_wait_flag)
				{
					int i;
					g_soe_queue.fzsoe_delay_flag = true;
					g_soe_queue.fzsoe_wait_flag = false;
					for(i=0;i<DELAY_SOE_MAX;i++)
					{		
						if(g_soe_queue.delay_fzsoe_tab[i].value==0) break;
						g_soe_queue.delay_fzsoe_count = i;
						soe_record_ev( g_soe_queue.delay_fzsoe_tab[i].code,g_soe_queue.delay_fzsoe_tab[i].value, g_soe_queue.delay_fzsoe_tab[i].v1,g_soe_queue.delay_fzsoe_tab[i].v2,g_soe_queue.delay_fzsoe_tab[i].v3); 
					}
					/*置SOE后调用一次plc_scan，避免置->清SOE间隔过短导致正常plc_san扫描时无法扫描到置SOE*/
					plc_scan(1);
					memset((char *)&g_soe_queue.delay_fzsoe_tab,0,sizeof(DELAY_SOE_DATA)*DELAY_SOE_MAX);//clear delay_soe_tab[] buffer
					g_soe_queue.delay_fzsoe_count = 0;
					g_soe_queue.fzsoe_delay_flag = false;
				}	
				if(g_soe_queue.hzsoe_wait_flag)
				{
					int i;
					g_soe_queue.hzsoe_delay_flag = true;
					g_soe_queue.hzsoe_wait_flag = false;
					for(i=0;i<DELAY_SOE_MAX;i++)
					{		
						if(g_soe_queue.delay_hzsoe_tab[i].value==0) break;
						g_soe_queue.delay_hzsoe_count = i;	
						soe_record_ev( g_soe_queue.delay_hzsoe_tab[i].code,g_soe_queue.delay_hzsoe_tab[i].value, g_soe_queue.delay_hzsoe_tab[i].v1,g_soe_queue.delay_hzsoe_tab[i].v2,g_soe_queue.delay_hzsoe_tab[i].v3); 
					}
					memset((char *)&g_soe_queue.delay_hzsoe_tab,0,sizeof(DELAY_SOE_DATA)*DELAY_SOE_MAX);//clear delay_soe_tab[] buffer
					g_soe_queue.delay_hzsoe_count = 0;
					g_soe_queue.hzsoe_delay_flag = false;
				}	
				
			}
			else if(tAllSOE[code%EV_SW_NUM].soe_def&SOE_TYPE_DELAY_FHZ && value )//开关延时保存分/合闸动作SOE && 动作 
			{						
				u8 i=0;
				bool soe_fh=((code%EV_SW_NUM)==EV_AR1)||((code%EV_SW_NUM)==EV_AR2)||((code%EV_SW_NUM)==EV_AR3)||
					        ((code%EV_SW_NUM)==EV_FA_HZ)||((code%EV_SW_NUM)==EV_FA_LL_HZ);//0-fz soe;1-hz soe
				//延时soe入QUEUE
				if((g_soe_queue.fzsoe_delay_flag==0) && (g_tRelay[0].run_stu.sw==2) && (!soe_fh))//非分闸延时SOE QUEUE转录SOE && 合位 && 分闸SOE  		fzsoe_tab[]
				{					
					for(i=0;i<DELAY_SOE_MAX;i++)//大于最大缓存，直接保存SOE
					{
						if(g_soe_queue.delay_fzsoe_tab[i].code==code)//当前SOE已保存
						{										
							return;//此次不保存数据
						}
						else if(g_soe_queue.delay_fzsoe_tab[i].value==0)
						{
							clk_time_get(&g_soe_queue.delay_fzsoe_tab[i].ts);
							g_soe_queue.delay_fzsoe_tab[i].value = value;						
							g_soe_queue.delay_fzsoe_tab[i].code=code;
							g_soe_queue.delay_fzsoe_tab[i].v1=v1;	
							g_soe_queue.delay_fzsoe_tab[i].v2=v2;
							g_soe_queue.delay_fzsoe_tab[i].v3=v3;
							g_soe_queue.fzsoe_wait_flag = true;
							return;//此次不保存数据
						}
					}				
				}
				else if((g_soe_queue.hzsoe_delay_flag==0) && (g_tRelay[0].run_stu.sw==1) && soe_fh)////非合闸延时SOE QUEUE转录SOE && 分位 && 闸SOE hzsoe_tab[]
				{					
					for(i=0;i<DELAY_SOE_MAX;i++)//大于最大缓存，直接保存SOE
					{
						if(g_soe_queue.delay_hzsoe_tab[i].code==code)//当前SOE已保存
						{									
							return;//此次不保存数据
						}
						else if(g_soe_queue.delay_hzsoe_tab[i].value==0)
						{
							clk_time_get(&g_soe_queue.delay_hzsoe_tab[i].ts);
							g_soe_queue.delay_hzsoe_tab[i].value = value;						
							g_soe_queue.delay_hzsoe_tab[i].code=code;
							g_soe_queue.delay_hzsoe_tab[i].v1=v1;	
							g_soe_queue.delay_hzsoe_tab[i].v2=v2;
							g_soe_queue.delay_hzsoe_tab[i].v3=v3;
							g_soe_queue.hzsoe_wait_flag = true;
							return;//此次不保存数据
						}
					}				
				}			
			}
		}
	#endif
		ev = &soe_get_buffer(&extarg)->es_ev;
		ev->ev_type=SOE_TYPE_EV;
		if(tAllSOE[code%EV_SW_NUM].soe_def&0x80)  // 需上送故障记录
		{
			ev->ev_type|=SOE_TYPE_FAULT;
			soe_ext_arg(extarg,sw);
		}
		if(tAllSOE[code%EV_SW_NUM].soe_def&0x40)  // 置关键事件标志
		{
			ev->ev_type|=SOE_TYPE_VIP;
		}
		if(tAllSOE[code%EV_SW_NUM].soe_def&0x10)  // 全数据保存
		{
			allarg=true;
		}
	}	
	else
	{
	#ifdef SOE_RECORD_DELAY	
		if(!gb_wake_soe)	//首次上电时，对前一次上电产生的闭锁事件不做延时处理，避免逻辑运行出错
		{
			if(tAllSOE[code-EV_PUB_START+EV_SW_NUM].soe_def&SOE_TYPE_DELAY_FHZ && (value==0))
			{
				if(g_soe_queue.fzsoe_wait_flag)
				{
					int i;
					g_soe_queue.fzsoe_delay_flag = true;
					g_soe_queue.fzsoe_wait_flag = false;
					for(i=0;i<DELAY_SOE_MAX;i++)
					{		
						if(g_soe_queue.delay_fzsoe_tab[i].value==0) break;
						g_soe_queue.delay_fzsoe_count = i;
						soe_record_ev( g_soe_queue.delay_fzsoe_tab[i].code,g_soe_queue.delay_fzsoe_tab[i].value, g_soe_queue.delay_fzsoe_tab[i].v1,g_soe_queue.delay_fzsoe_tab[i].v2,g_soe_queue.delay_fzsoe_tab[i].v3); 
					}
					memset((char *)&g_soe_queue.delay_fzsoe_tab,0,sizeof(DELAY_SOE_DATA)*DELAY_SOE_MAX);//clear delay_soe_tab[] buffer
					g_soe_queue.delay_fzsoe_count = 0;
					g_soe_queue.fzsoe_delay_flag = false;
				}	
				if(g_soe_queue.hzsoe_wait_flag)
				{
					int i;
					g_soe_queue.hzsoe_delay_flag = true;
					g_soe_queue.hzsoe_wait_flag = false;
					for(i=0;i<DELAY_SOE_MAX;i++)
					{		
						g_soe_queue.delay_hzsoe_count = i;	
						if(g_soe_queue.delay_hzsoe_tab[i].value==0) break;
						soe_record_ev( g_soe_queue.delay_hzsoe_tab[i].code,g_soe_queue.delay_hzsoe_tab[i].value, g_soe_queue.delay_hzsoe_tab[i].v1,g_soe_queue.delay_hzsoe_tab[i].v2,g_soe_queue.delay_hzsoe_tab[i].v3); 
					}
					memset((char *)&g_soe_queue.delay_hzsoe_tab,0,sizeof(DELAY_SOE_DATA)*DELAY_SOE_MAX);//clear delay_soe_tab[] buffer
					g_soe_queue.delay_hzsoe_count = 0;
					g_soe_queue.hzsoe_delay_flag = false;
				}	
				
			}
			else if(tAllSOE[code-EV_PUB_START+EV_SW_NUM].soe_def&SOE_TYPE_DELAY_FHZ && value)//开关延时保存分/合闸动作SOE && 动作 
			{						
				u8 i=0;
				bool soe_fh=(((code-EV_PUB_START+EV_SW_NUM)%EV_SW_NUM)==EV_AR1)||(((code-EV_PUB_START+EV_SW_NUM)%EV_SW_NUM)==EV_AR2)||(((code-EV_PUB_START+EV_SW_NUM)%EV_SW_NUM)==EV_AR3)||
					        (((code-EV_PUB_START+EV_SW_NUM)%EV_SW_NUM)==EV_FA_HZ)||(((code-EV_PUB_START+EV_SW_NUM)%EV_SW_NUM)==EV_FA_LL_HZ);//0-fz soe;1-hz soe
				//延时soe入QUEUE					
				if((g_soe_queue.fzsoe_delay_flag==0) && (g_tRelay[sw].run_stu.sw==2) && (!soe_fh))//非分闸延时SOE QUEUE转录SOE && 合位 && 分闸SOE  		fzsoe_tab[]
				{					
					for(i=0;i<DELAY_SOE_MAX;i++)//大于最大缓存，直接保存SOE
					{
						if(g_soe_queue.delay_fzsoe_tab[i].code==code)//当前SOE已保存
						{
							return;//此次不保存数据
						}
						else if(g_soe_queue.delay_fzsoe_tab[i].value==0)
						{
							clk_time_get(&g_soe_queue.delay_fzsoe_tab[i].ts);
							g_soe_queue.delay_fzsoe_tab[i].value = value;						
							g_soe_queue.delay_fzsoe_tab[i].code=code;
							g_soe_queue.delay_fzsoe_tab[i].v1=v1;	
							g_soe_queue.delay_fzsoe_tab[i].v2=v2;
							g_soe_queue.delay_fzsoe_tab[i].v3=v3;
							g_soe_queue.fzsoe_wait_flag = true;
							return;//此次不保存数据
						}
					}				
				}
				else if((g_soe_queue.hzsoe_delay_flag==0) && (g_tRelay[sw].run_stu.sw==1) && soe_fh)////非合闸延时SOE QUEUE转录SOE && 分位 && 合闸SOE hzsoe_tab[]
				{			
					for(i=0;i<DELAY_SOE_MAX;i++)//大于最大缓存，直接保存SOE
					{
						if(g_soe_queue.delay_hzsoe_tab[i].code==code)//当前SOE已保存
						{
							return;//此次不保存数据
						}
						else if(g_soe_queue.delay_hzsoe_tab[i].value==0)
						{
							clk_time_get(&g_soe_queue.delay_hzsoe_tab[i].ts);
							g_soe_queue.delay_hzsoe_tab[i].value = value;						
							g_soe_queue.delay_hzsoe_tab[i].code=code;
							g_soe_queue.delay_hzsoe_tab[i].v1=v1;	
							g_soe_queue.delay_hzsoe_tab[i].v2=v2;
							g_soe_queue.delay_hzsoe_tab[i].v3=v3;
							g_soe_queue.hzsoe_wait_flag = true;
							return;//此次不保存数据
						}
					}				
				}
			}
		}
	#endif
		ev = &soe_get_buffer(&extarg)->es_ev;
		ev->ev_type=SOE_TYPE_EV;
		if(tAllSOE[code-EV_PUB_START+EV_SW_NUM].soe_def&0x80)  // 需上送故障记录
		{
			ev->ev_type|=SOE_TYPE_FAULT;
			soe_ext_arg(extarg,sw);
		}
		if(tAllSOE[code-EV_PUB_START+EV_SW_NUM].soe_def&0x40)  // 需上送故障记录
		{
			ev->ev_type|=SOE_TYPE_VIP;
		}		
		if(tAllSOE[code-EV_PUB_START+EV_SW_NUM].soe_def&0x10)  // 需上送故障记录
		{
			allarg=true;
		}	
	}	
	if(allarg)  //保存全参数
	{
#ifdef SOE_PARA_ALL_SAVE
		soe_record_allArg(&ev->allarg[0],sw);
#endif
	}
	soe_ev_set(code,value);
	ev->ev_value = value;
	ev->ev_code=code;
	ev->ev_arg[0]=v1;	
	ev->ev_arg[1]=v2;
	ev->ev_arg[2]=v3;
#ifdef SOE_RECORD_DELAY	
    //延时SOE QUEUE中SOE时标获得
    //转录SOE && QUEUE非空 && code对应 && 动作
	if(!gb_wake_soe)
	{
		if(g_soe_queue.fzsoe_delay_flag && !g_soe_queue.fzsoe_wait_flag && g_soe_queue.delay_fzsoe_tab[g_soe_queue.delay_fzsoe_count].code==code && value )
		{
			memcpy(&ev->ts,&g_soe_queue.delay_fzsoe_tab[g_soe_queue.delay_fzsoe_count].ts,sizeof(struct timespec));
		}
		else if(g_soe_queue.hzsoe_delay_flag && !g_soe_queue.hzsoe_wait_flag && g_soe_queue.delay_hzsoe_tab[g_soe_queue.delay_hzsoe_count].code==code && value )
		{
			memcpy(&ev->ts,&g_soe_queue.delay_hzsoe_tab[g_soe_queue.delay_hzsoe_count].ts,sizeof(struct timespec));
		}
		else //其它非延时SOE 时标获得
		{
			clk_time_get(&ev->ts);	
		}
	}
	else //其它非延时SOE 时标获得
	{
		clk_time_get(&ev->ts);	
	}
#else
	clk_time_get(&ev->ts);	
#endif
	g_soe_queue.stat_cnt_type[SOE_TYPE_EV]++;
	
 }

int  soe_record_imp(u16 code,u8 val)
{

	int i;
	u8 slot=(u8)(code>>8);	//板卡号
	u8 dino=(u8)(code);		//遥信号
	
	for(i=0; i< g_equ_config->di_num; i++)
	{
		if((g_equ_config_di[i].slot==slot)&&(g_equ_config_di[i].index==dino))
		{
			break;
		}
	}
	if(i<g_equ_config->di_num)
	{
		u8 sw=g_equ_config_di[i].owner;
		u8 type=g_equ_config_di[i].type;
		if(type==0||sw==0||sw>SWITCH_NUM_MAX)return 0;
		type-=1;
		sw-=1;
		if(type>=SW_DI_IMP1&&type<=SW_DI_IMP2) //是脉冲计数
		{
			if(val==1)g_sw[sw].ac_in[SW_AC_IN_IMP1+type-SW_DI_IMP1]++;
			return 1;
		}
	}	
	return 0;	
}


void soe_record_yx(u16 code,u8 value,struct timespec * ts)
{
	EVENT_STRUCT *ev;
	if(soe_record_imp(code ,value)) return;  //是脉冲计数值，直接返回，不保存
	ev = &soe_get_buffer_yx()->es_ev;	
	ev->ev_type=SOE_TYPE_YX;
	ev->ev_value = value;
	ev->ev_code=code;
	ev->ev_arg[0]=0;	
	ev->ev_arg[1]=0;
	ev->ev_arg[2]=0;

	ev->ts=*ts;

	// 双点触发
	plc_db_soe(ev);
	g_soe_queue.stat_cnt_type[SOE_TYPE_YX]++;
	//关键遥信变位处理 
	{		
		u8 type;
		u8 slot=(u8)(code>>8);	//板卡号
		u8 no=(u8)(code);		//遥信号
		type=equ_di_type(slot,no);
		if(type>0)
		{			
			type--;
		
		//佛山局要求开关分合位为关键记录
#if defined  GD_AREA_FOS || defined GD_AREA_FOS_2021 || defined FOS_2021
		if(type==SW_DI_KRHW ||type==SW_DI_KRTW)	// 跳位
		{
			ev->ev_type|=SOE_TYPE_VIP;
		}
#endif
				
		}
	}
	
}
 

void soe_record_lnk(u16 code,u8 value,struct timespec * ts, s32 v1,bool bmix,u32 cp_s)
{
	EVENT_STRUCT *ev = &soe_get_buffer(NULL)->es_ev;
	//rt_printf("\r\n soe_record_lnk :code=%04x v=%d v1=%d bmix=%d",code,value,v1,bmix);
	ev->ev_type=SOE_TYPE_LNK;
	ev->ev_value=value;
	ev->ev_code=code;
	ev->ev_arg[0]=v1;	
	ev->ev_arg[1]=bmix;
	ev->ev_arg[2]=cp_s;
	
	ev->ts=*ts;
	
	g_soe_queue.stat_cnt_type[SOE_TYPE_LNK]++;
}


void soe_record_dpi(u16 code,u8 value,struct timespec * ts)
{
	EVENT_STRUCT *ev = &soe_get_buffer(NULL)->es_ev;
	
	ev->ev_type = SOE_TYPE_DPI;
	ev->ev_value=value;
	ev->ev_code=code;
	ev->ev_arg[0]=0;	
	ev->ev_arg[1]=0;
	ev->ev_arg[2]=0;
	
	ev->ts=*ts;
	
	g_soe_queue.stat_cnt_type[SOE_TYPE_DPI]++;
}


void soe_record_plc(u16 code,u8 value, u32 flag)
{
	u16 extarg;
	EVENT_STRUCT *ev = &soe_get_buffer(&extarg)->es_ev;
	
	ev->ev_type = SOE_TYPE_PLC;
	if(flag & PLC_FLAG_FAULT)
	{
		ev->ev_type|=SOE_TYPE_FAULT;
		soe_ext_arg(extarg,(flag>>16));
	}
	ev->ev_value =value;
	ev->ev_code=code;
	ev->ev_arg[0]=0;	
	ev->ev_arg[1]=0;
	ev->ev_arg[2]=0;
	
	clk_time_get(&ev->ts); 
	
	g_soe_queue.stat_cnt_type[SOE_TYPE_PLC]++;
}

void soe_record_tst(u16 code,u8 value)
{
	EVENT_STRUCT *ev = &soe_get_buffer(NULL)->es_ev;
	
	ev->ev_type = SOE_TYPE_TST;
	ev->ev_value =value;
	ev->ev_code=code;
	ev->ev_arg[0]=0;	
	ev->ev_arg[1]=0;
	ev->ev_arg[2]=0;
	
	clk_time_get(&ev->ts); 
	
	g_soe_queue.stat_cnt_type[SOE_TYPE_TST]++;
}

void soe_record_opt(u16 code,long v1)
{
	EVENT_STRUCT *ev = &soe_get_buffer(NULL)->es_ev;
	
	ev->ev_type=SOE_TYPE_OPT;	
	#ifdef GD_AREA_FOS_2020
	if(tAllSOE[code-EV_SW_NUM*(SWITCH_NUM_MAX-1)].soe_def&0x40)  // 置关键事件标志
	{
		ev->ev_type|=SOE_TYPE_VIP;
	}
	#endif
	ev->ev_value=1;
	ev->ev_code=code;
	ev->ev_arg[0]=v1;	
	ev->ev_arg[1]=0;
	ev->ev_arg[2]=0;
	
	clk_time_get(&ev->ts);
	
	g_soe_queue.stat_cnt_type[SOE_TYPE_OPT]++;
}

#ifdef FUN_SET_CHANGE_SOE
void soe_record_opt3(u16 code,long v1,long v2,long v3)
{
	EVENT_STRUCT *ev = &soe_get_buffer(NULL)->es_ev;
	
	ev->ev_type=SOE_TYPE_OPT;	
	#ifdef GD_AREA_FOS_2020
	if(tAllSOE[code-EV_SW_NUM*(SWITCH_NUM_MAX-1)].soe_def&0x40)  // 置关键事件标志
	{
		ev->ev_type|=SOE_TYPE_VIP;
	}
	#endif
	ev->ev_value=1;
	ev->ev_code=code;
	ev->ev_arg[0]=v1;	
	ev->ev_arg[1]=v2;
	ev->ev_arg[2]=v3;
	
	clk_time_get(&ev->ts);
	
	g_soe_queue.stat_cnt_type[SOE_TYPE_OPT]++;
}
#endif

int soe_get(u8 ch,EVENT_STRUCT *ev)
{
	u16 tail_send;
	sbi_t tail_send_new;
	uint32_t  flags;
	int ok;

	// 检查参数
	if(ch >= COMM_CHANNEL_NUM || ev == NULL)
	{
		return -1;
	}

	// 检查是否有数据
	if(g_soe_queue.tail_send[ch].n == g_soe_queue.head.n)
	{
		return -2;
	}
	
	// 为了防止取出不正确或不完整的数据(在SOE池已满且事件正在产生，此时104主站连接进来时最容易产生)
	// 需检查tail_send是否被中断修改。
	while(1)
	{
		// 获取事件，调整尾指针
		tail_send = g_soe_queue.tail_send[ch].n;
		*ev = g_soe_queue.soe[tail_send].es_ev;
		tail_send_new.n = tail_send+1;

		// 在关中断的情况下，检测修改尾指针，避免数据错误。
		ok = 0;
		rt_irq_save(flags);
		if(tail_send == g_soe_queue.tail_send[ch].n)
		{
			g_soe_queue.tail_send[ch].n = tail_send_new.n;
			ok=1;

		}
		rt_irq_restore(flags);

		if(ok)
		{
			break;
		}

		rt_printf_time("soe_get(%d): old =%d,new=%d!\r\n",ch,tail_send ,g_soe_queue.tail_send[ch].n);
	}

	return tail_send_new.n;
}

// 注意:ack是已确认事件的下一个(还不能确认)
void soe_ack(u8 chnl,u16 ack)
{
	u32 es_save_addr=0,es_save_cnt = 0;
	sbi_t num_all,num_send,num_ack;
	EVENT_SAVE *es,tEvt;
	u8 soe_type;
	u32  addr;
	
	// 检查ack是否在合法的范围内，ack的位置必须在tail_ack和tail_send之间
	num_send.n = g_soe_queue.tail_send[chnl].n - g_soe_queue.tail_ack[chnl].n;
	num_ack.n = ack - g_soe_queue.tail_ack[chnl].n;
	
	if(num_ack.n > num_send.n)
	{
		num_all.n = g_soe_queue.head.n - g_soe_queue.tail_ack[chnl].n;
		rt_printf_time("soe_ack error:num_send=%d,num_ack=%d,head=%d,tail_send=%d,tail_ack=%d,ack=%d.\r\n", 
			num_send.n,num_ack.n,g_soe_queue.head.n,g_soe_queue.tail_send[chnl].n,g_soe_queue.tail_ack[chnl].n, ack);
			
		return;
	}
 
	// 循环确认事件
	for(;g_soe_queue.tail_ack[chnl].n != ack; g_soe_queue.tail_ack[chnl].n++)
	{
		es = &g_soe_queue.soe[g_soe_queue.tail_ack[chnl].n]; 

		// 检查是否是不需要确认的事件
		soe_type = es->es_ev.ev_type & SOE_TYPE_MASK;
		if(soe_type >= SOE_TYPE_ACK)
		{
			continue;
		}

		// 没有保存过的事件不能更新
		if(es->es_flag != SOE_ID_MASK)
		{
			rt_printf_time("soe_ack_ev:没有保存过的事件不能更新,es_flag=%02x.\r\n",es->es_flag);
			continue;

		}

		// 已确认过的事件不再确认
		if(es->es_ack == SOE_ID_ACK_OK)
		{
			continue;
		}
		es->es_ack = SOE_ID_ACK_OK;

		// 读出已保存的事件
		addr = es->es_id+EEP_SOE_BASE;
		e2prom_read(addr,(u8 *)&tEvt,sizeof(EVENT_SAVE)); 
		es->es_id = tEvt.es_id;

		// 两份内容除了ack外，应完全一样
		tEvt.es_ack = es->es_ack;
		if(memcmp(es,&tEvt,sizeof(tEvt)))
		{
			rt_printf_time("soe_update_ev:memcmp error!\r\n");
			continue;
		}

		// 处理保存信息
		if(es_save_cnt == 0)
		{
			es_save_addr = addr;
		}
		// 如果地址不连续或BUF已满，先保存一段
		else if((addr != es_save_addr+es_save_cnt*sizeof(EVENT_SAVE)) || es_save_cnt == SOE_SAVE_BUF_NUM)
		{
			e2prom_write(es_save_addr,(u8 *)&g_soe_sbuf,sizeof(EVENT_SAVE)*es_save_cnt); 
			es_save_cnt = 0;
			es_save_addr = addr;
		}
		
		// 写入确认事件
		memcpy(&g_soe_sbuf[es_save_cnt++],&tEvt,sizeof(EVENT_SAVE));
		
		g_soe_info.wEvAck--;
 	}

	if(es_save_cnt)
	{
		e2prom_write(es_save_addr,(u8 *)&g_soe_sbuf,sizeof(EVENT_SAVE)*es_save_cnt); 
	}

//	rt_printf("SOE通讯BUF:head=%d,tail_send=%d,tail_ack=%d\r\n",g_soe_queue.head.n,g_soe_queue.tail_send[chnl].n,g_soe_queue.tail_ack[chnl].n);

	return;
}
 

void soe_log(EVENT_STRUCT * er)
{
	static char log[512];

	u8 soe_type;
	char *buf;

	struct rtc_time_t tm;
	timespec_to_rtc(er->ts,&tm,0);

	buf = log;
	sprintf(buf,"20%02hu-%02hu-%02hu %02hu:%02hu:%02hu.%06u ",tm.year,tm.month,tm.day,tm.hour,tm.min,tm.ms/1000,er->ts.tv_nsec/1000);
	buf += strlen(buf);
	

	
	soe_type = er->ev_type&SOE_TYPE_MASK;
	if(soe_type==SOE_TYPE_EV||soe_type==SOE_TYPE_OPT)   // 不是遥信变位
	{
		int i;
		int ev_sw=0;
		int len=0;
		u16 code;
		const SOE_DEF * psoe;
		
		if(er->ev_code>=EV_NUMBER)
		{
			sprintf(buf,"错误代码:%d",er->ev_code);
			log_str_ext(LOG_SOE,log);
			return;
		}		
		
		if (er->ev_code < EV_SW_NUM)
		{
			code = er->ev_code;
			ev_sw = 1;
		}
		else if ((er->ev_code >= EV_SW_NUM)&& (er->ev_code < EV_PUB_START))
		{
			code = er->ev_code % EV_SW_NUM;
			ev_sw = (er->ev_code / EV_SW_NUM) + 1;
		}
		else
		{

#ifdef FUN_SET_CHANGE_SOE
			if(er->ev_code==EV_SET_CHANGE_PARA)return;  //定值修改操作记录，不做日志，已经有修改内容记录
#endif
			code = er->ev_code - EV_SW_NUM*(SWITCH_NUM_MAX-1);
		}
		
		#if SWITCH_NUM_MAX != 1
		{
			if ((ev_sw > 0) && (ev_sw <= SWITCH_NUM_MAX))
			{
				sprintf(buf,"开关%02d ",ev_sw);
				len = strlen(buf);
			}
		}
		#endif

		psoe=&tAllSOE[code];
		if(soe_ev_is_on(er)) // 置1 ,产生，显示参数
		{
		#ifdef XDL_ZT
			if(code == EV_LX_XDL_QD)
			{
				if(er->ev_arg[2] == 1)
					strcpy(&buf[len],"小电流接地：启动[界内动作]");
				else if(er->ev_arg[2] == 0)
					strcpy(&buf[len],"小电流接地：启动[界外动作]");
				else if(er->ev_arg[2] == -1)
					strcpy(&buf[len],"小电流接地：启动[线路无流]");
				else if(er->ev_arg[2] == -2)
					strcpy(&buf[len],"小电流接地：启动[电压欠幅]");
				else if(er->ev_arg[2] == -3)
					strcpy(&buf[len],"小电流接地：启动[电压无向]");
				else if(er->ev_arg[2] == -4)
					strcpy(&buf[len],"小电流接地：启动[无首半波]");
				else if(er->ev_arg[2] == -11)
					strcpy(&buf[len],"小电流接地：启动[无电压通道]");
				else if(er->ev_arg[2] == -12)
					strcpy(&buf[len],"小电流接地：启动[功能禁止]");
				else if(er->ev_arg[2] == -13)
					strcpy(&buf[len],"小电流接地：启动[无电流通道]");
				else if(er->ev_arg[2] == -14)
					strcpy(&buf[len],"小电流接地：启动[处理超时]");
				else if(er->ev_arg[2] == -15)
					strcpy(&buf[len],"小电流接地：启动[开关序号异常]");
			}
			else
			{
				strcpy(&buf[len],psoe->strName);
			}
		#else
			strcpy(&buf[len],psoe->strName);
		#endif
			buf += strlen(buf);			
			for(i=0;i<3;i++)
			{
				u8 index=(psoe->Attrib[i]&0x1f);
				if(index == 0)
				{
					continue;
				}
				switch((psoe->Attrib[i]>>5)&0x07)
				{
					case 0:  // 浮点数
						sprintf(buf," %s=%f%s",ev_para[index].strName,(float)er->ev_arg[i]/Q16_BASE,g_unit[ev_para[index].byNo].name);
						break;
					case 1: // 整型值
						sprintf(buf," %s=%d%s",ev_para[index].strName,er->ev_arg[i],g_unit[ev_para[index].byNo].name);
						break;
					case 2: // 遥控操作
						if(i == 0)
						{
							u8 sw=(er->ev_arg[0]>>8)&0xff; //开关号
							u8 no=(er->ev_arg[0]>>0)&0xff;	//序号
							char *ykowner;
							ykowner=get_yk_owner((er->ev_arg[0]>>16)&0xff);
							if(sw==0)
							{
								sprintf(buf,"--%s:公共开出%02d",ykowner,no);
							}
							else
							{
								sprintf(buf,"--%s:开关%02d开出%02d",ykowner,sw,no);
							}
						}						
						break;	
					case 3: // 压板操作
					{
						strcpy(buf,"投入");
					}						
					break;	
					default:
						sprintf(buf," 参数错误");		
					break;
				}
				buf += strlen(buf);
			}
		}
		else  // 置 0，显示返回值
		{
			strcpy(&buf[len],psoe->strName);
			buf += strlen(buf); 
			strcpy(buf,ev_back[(psoe->soe_def&0x03)]);
		}


		if(soe_type==SOE_TYPE_EV)
		{
	
			log_str_ext(LOG_SOE,log);

			if(soe_ev_is_on(er)) // 置1 ,产生，显示参数
			{
				if(er->ev_code == EV_TWYL)	
				{
					load_hs_log_rcd(TYPE_SW_YX,false,&tm,"Tripping position but current occured",1);
				}
				else  if(er->ev_code == EV_BOARDCOMM)
				{
					load_hs_log_rcd(TYPE_HW_ERR,false,&tm,"The subboard communication is abnormal occured",1);
				}
				else if(er->ev_code == EV_HMIERR)	
				{
					load_hs_log_rcd(TYPE_HW_ERR,false,&tm,"Panel communication error occured",1);
				}
				else if(er->ev_code == EV_TZFAIL)
				{
					load_hs_log_rcd(TYPE_CTL_LOOP,false,&tm,"Tripping failure occured",1);
				}
				
				
			}
			else 
			{
				if(er->ev_code == EV_TWYL)
				{
					load_hs_log_rcd(TYPE_SW_YX,false,&tm,"Tripping position but current disappeared",0);
				}
				else  if(er->ev_code == EV_BOARDCOMM)
				{
					load_hs_log_rcd(TYPE_HW_ERR,false,&tm,"The subboard communication is abnormal disappeared",0);
				}
				else if(er->ev_code == EV_HMIERR)
				{
					load_hs_log_rcd(TYPE_HW_ERR,false,&tm,"Panel communication error disappeared",0);
				}
				else if(er->ev_code == EV_TZFAIL)		
				{
					load_hs_log_rcd(TYPE_CTL_LOOP,false,&tm,"Tripping failure disappeared",0);
				}
	
				
			}


		}
		else
		{
			log_str(LOG_OPERATE,log);
		
			if(er->ev_code == EV_SET_OK)
			{
				load_hs_log_rcd(TYPE_DEV_PARM,false,&tm,"The fixed value is determined successfully",1);
			}
			else if(er->ev_code == EV_CSTSET_OK)
			{
				load_hs_log_rcd(TYPE_DEV_PARM,false,&tm,"The setting of the internal fixed value is determined successfully",1);
			}
			else if(er->ev_code == EV_EQUPARA_OK)
			{
				load_hs_log_rcd(TYPE_DEV_PARM,false,&tm,"Device parameter setting successfully",1);
			}
			else if(er->ev_code == EV_SET_FAIL)
			{
				load_hs_log_rcd(TYPE_DEV_PARM,false,&tm,"The fixed value is set to fail",1);
			}
			else if(er->ev_code == EV_CSTSET_FAIL)
			{
				load_hs_log_rcd(TYPE_DEV_PARM,false,&tm,"Internal setting failed",1);
			}
			else if(er->ev_code == EV_EQUPARA_FAIL)
			{
				load_hs_log_rcd(TYPE_DEV_PARM,false,&tm,"Device parameter setting failed",1);
			}
			

			if(er->ev_code == EV_BAT_AUTOACTIVE)
			{
				load_hs_log_rcd(TYPE_STANDBY_POW,false,&tm,"Automatic battery activation",0);
			}
			else if(er->ev_code == EV_BAT_HANDACTIVE)
			{
				load_hs_log_rcd(TYPE_STANDBY_POW,false,&tm,"Battery activation",0);
			}
			else if(er->ev_code == EV_BAT_HANDACTOFF)
			{
				load_hs_log_rcd(TYPE_STANDBY_POW,false,&tm,"Battery activation exit",0);
			}
			else if(er->ev_code == EV_BAT_HANDOFF)
			{
				load_hs_log_rcd(TYPE_STANDBY_POW,false,&tm,"Manual exit of battery",0);
			}
			//else if(er->ev_code == EV_BATERR)
			//{
			//	load_hs_log_rcd(TYPE_STANDBY_POW,false,&tm,"Abnormal battery alarm",0);
			//}
			
		}
	 	
	}
	// 遥信变位记录
	else if(soe_type==SOE_TYPE_YX)
	{
		u8 slot=(u8)(er->ev_code>>8);	//板卡号
		u8 dino=(u8)(er->ev_code);		//遥信号
		char * name;

		name = equ_di_name(slot,dino);
		if(name)
		{
			sprintf(buf,"板卡%02d开入%02d[%s]变位",slot,dino+1,name); 
		}
		else
		{
			sprintf(buf,"板卡%02d开入%02d[]变位",slot,dino+1); 
		}
	
		buf += strlen(buf);
		
		if(soe_ev_is_on(er)) // 置1 ,产生，显示参数
		{
			sprintf(buf," 0->1");
		}
		else
		{
			sprintf(buf," 1->0");
		}

		log_str_ext(LOG_YX,log);
 	}
 	// 级联SOE记录
	else if(soe_type==SOE_TYPE_LNK)
	{
		char name[TRANS_SZ_BUF_LEN];
		LINK_TABLE * lt;

		lt = (LINK_TABLE *)er->ev_arg[0];

		name[0] = 0;
		if(lt->is_mix==1)
		{
			sprintf(name,"合并点%d",lt->tbl_index+1);
		}
		else
		{
			tbl_getname(RES_TYPE_YX,lt->tbl_index,name);
		}
		sprintf(buf,"通道%d地址%02d 点号0x%04x(级联点号0x%04x  %s)变位",(u8)((lt->cp_s>>21)&0x0f),(u8)((lt->cp_s>>16)&0x1f),lt->cp,(lt->cp_s)&0xffff,name); 
		buf += strlen(buf);
		
		if(soe_ev_is_on(er)) // 置1 ,产生，显示参数
		{
			sprintf(buf," 0->1");
		}
		else
		{
			sprintf(buf," 1->0");
		}

		log_str_ext(LOG_SOE,log);
 	}
 	// 双点变位记录
 	else if(soe_type==SOE_TYPE_DPI)
	{
		sprintf(buf,"双点%02d变位[值:%d,通讯点:0x%04x]",er->ev_code,er->ev_value,g_di_db_table[er->ev_code].cp); 
		log_str_ext(LOG_SOE,log);
 	}
 	// PLC变位记录
 	else if(soe_type==SOE_TYPE_PLC)
	{
		sprintf(buf,"PLC%02d变位[值:%d,通讯点:0x%04x]",er->ev_code,er->ev_value,g_plc[er->ev_code].cp); 
		log_str_ext(LOG_SOE,log);
 	}

 	else
	{
		sprintf(buf,"未知类型:%d",er->ev_code);
		log_str_ext(LOG_SOE,log);
	}		


}

#ifdef ADD_DEBUG_RECORD_AUTO_SHOW_FUN
   //TODO: add execute event judge
u8  JudgeExecuteEventFun(EVENT_SAVE es)
{
   u8 res=FALSE;
   u16 count;
   //rt_printf("EXECUTE_EVENT_NUM=%d\r\n",EXECUTE_EVENT_NUM);
   for(count=0;count<EXECUTE_EVENT_NUM;count++)
     if(ExecuteEventIdTbl[count]==es.es_ev.ev_code)
       {
         res=TRUE;
         break;
       }

   return res;
   
}
#endif


void soe_save(void)
{
	u32 es_save_addr=0,es_save_cnt = 0;
	
	u32 addr,ok,head;
	sbi_t tail_send,tail_send_new;
	EVENT_SAVE es,*esp;
	uint32_t  flags;
	u8 soe_type;
	u64 us0;

	// 循环保存所有记录
	us0 = bsp_ustimer_get_origin();
	head = g_soe_queue.head.n;
	while(g_soe_queue.tail_eep.n != head && es_save_cnt < SOE_SAVE_BUF_NUM) 
	{
		// 为了防止取出不正确或不完整的数据(在SOE池已满且事件正在产生)
		// 需检查tail_send是否被中断修改。
		while(1)
		{
			// 获取事件，调整尾指针
			tail_send.n = g_soe_queue.tail_eep.n;
			es=g_soe_queue.soe[tail_send.n];
			esp = &g_soe_queue.soe[tail_send.n];
			
			// 在关中断的情况下，检测修改尾指针，避免数据错误。
			ok = 0;
			tail_send_new.n = tail_send.n+1;
			rt_irq_save(flags);
			if(tail_send.n == g_soe_queue.tail_eep.n)
			{
				g_soe_queue.tail_eep.n = tail_send_new.n;
				ok=1;

			}
			rt_irq_restore(flags);

			if(ok)
			{
				break;
			}

			rt_printf("soe_save: old =%d,new=%d!\r\n",tail_send.n ,g_soe_queue.tail_eep.n);
		}

		soe_type = es.es_ev.ev_type & SOE_TYPE_MASK;
		if(soe_type != SOE_TYPE_TST)
		{
			// 输出到日志记录文本文件
			soe_log(&es.es_ev);	

			//输出历史SOE记录文件
			refresh_soe_file(&es.es_ev);

			// 初始化事件保存信息
			addr = 0;
			es.es_flag=SOE_ID_MASK;            // 记录标志
			es.es_ack= pRunSet->bTT_SoeResumePower ? SOE_ID_ACK_NONE : SOE_ID_ACK_OK; 
			
			// 记录事件
			if(soe_type< SOE_TYPE_ACK)
			{
				// 刷新显示
				if(!g_bVipSoe_show)
				{
					g_soe_info.bDpNew=true;
					g_soe_info.wDpAutoShow=tail_send.n;
				}				
				// 调整事件指针
				g_soe_info.wEvIndex++;
				if(g_soe_info.wEvIndex>=MAX_EVENT_NUMBER)
				{
					g_soe_info.wEvIndex=0;

					// 如果事件回绕，先保存一段
					if(es_save_cnt)
					{
						e2prom_write(es_save_addr,(u8 *)&g_soe_sbuf,sizeof(EVENT_SAVE)*es_save_cnt); 
						//rt_printf("soe_save:cnt=%d,head=%d,tail=%d.\r\n",es_save_cnt,head,g_soe_queue.tail_eep.n);
						es_save_cnt = 0;
					}
				}

				// 计算地址
				addr=EEP_SOE_ADDR+g_soe_info.wEvIndex*(sizeof(EVENT_SAVE));
				if(es_save_cnt == 0)
				{
					es_save_addr=addr;
				}

				// 放入保存buf中
				es.es_id=g_soe_info.wEvID++;
				memcpy(&g_soe_sbuf[es_save_cnt],&es,sizeof(EVENT_SAVE));

				// 保存计数加一
				es_save_cnt++;
				if(pRunSet->bTT_SoeResumePower)
				{
					g_soe_info.wEvAck++;
				}
				
				// 调整事件记录数
				if(g_soe_info.wEvNumber < MAX_EVENT_NUMBER)
				{
					g_soe_info.wEvNumber++;
				}	

				//保存到关键事件缓冲区
                if(es.es_ev.ev_type&SOE_TYPE_VIP)
                {

#ifdef ADD_DEBUG_RECORD_AUTO_SHOW_FUN
                    u8 IsExeEvent=FALSE;

                    IsExeEvent=JudgeExecuteEventFun(es);
                    if(IsExeEvent==TRUE)  
#endif
                    // 只有关键记录才刷新显示
					if(g_bVipSoe_show)
					{
						g_soe_info.bDpNew=true;
						g_soe_info.wDpAutoShow=tail_send.n;
						#ifdef GD_AREA_FOS_2020
							g_soe_info.bDpType=false;
						#endif
					}				
					es.es_id=g_soe_info.wVipID++;					
					g_soe_info.wVipIndex++;
					if(g_soe_info.wVipIndex>=MAX_VIP_NUMBER)
					{
						g_soe_info.wVipIndex=0;
					}
					
					addr=EEP_VIP_ADDR+g_soe_info.wVipIndex*(sizeof(EVENT_SAVE));
					e2prom_write(addr,(u8 *)&es,sizeof(EVENT_SAVE)); 
					
					if(g_soe_info.wVipNumber < MAX_VIP_NUMBER)
					{
						g_soe_info.wVipNumber++;
					}
#ifdef ADD_DEBUG_RECORD_FUN
#ifdef ADD_DEBUG_RECORD_AUTO_SHOW_FUN
                   if((es.es_ev.ev_value==TRUE)&&(IsExeEvent==TRUE))  //当为被选中的动作事件时才存入动作记录中
#else
                   if(es.es_ev.ev_value==TRUE)
#endif
                    {
                        g_soe_info.wDebugIndex++;
                        if(g_soe_info.wDebugIndex>=MAX_DEBUG_RECORD_NUMBER)
                        {
                            g_soe_info.wDebugIndex=0;
                        }

                        addr=EEP_DEBUG_RECORD_ADDR+g_soe_info.wDebugIndex*(sizeof(EVENT_SAVE));
                        e2prom_write(addr,(u8 *)&es,sizeof(EVENT_SAVE));

                        if(g_soe_info.wDebugNumber < MAX_DEBUG_RECORD_NUMBER)
                        {
                            g_soe_info.wDebugNumber++;
                        }
                    }
#endif
                }
            }
			// 记录操作
			else if(soe_type==SOE_TYPE_OPT)  
			{
			#ifdef GD_AREA_FOS_2020
			    if(es.es_ev.ev_type&SOE_TYPE_VIP)
		    	{
					if(g_bVipSoe_show)
					{
						g_soe_info.bDpNew=true;
						g_soe_info.wDpAutoShow=tail_send.n;					
						g_soe_info.bDpType=true;						
					}
		    	}
			#endif
				es.es_id=g_soe_info.wOpID++;
				
				g_soe_info.wOpIndex++;
				if(g_soe_info.wOpIndex>=MAX_OPERATE_NUMBER)
				{
					g_soe_info.wOpIndex=0;
				}
				
				addr=EEP_OPERATE_ADDR+g_soe_info.wOpIndex*(sizeof(EVENT_SAVE));
				e2prom_write(addr,(u8 *)&es,sizeof(EVENT_SAVE)); 
				
				if(g_soe_info.wOpNumber < MAX_OPERATE_NUMBER)
				{
					g_soe_info.wOpNumber++;
				}	
			}

			// 事件内容保存完后，将事件队列的事件保存信息完善，
			// 如果队列溢出，没有办法保证事件确认的正确性。
			esp->es_flag = es.es_flag;
			esp->es_ack= es.es_ack;
			esp->es_id = addr; //事件队列中的ID用来保存事件的地址，供事件确认用
		}
 	}

	if(es_save_cnt)
	{
		e2prom_write(es_save_addr,(u8 *)&g_soe_sbuf,sizeof(EVENT_SAVE)*es_save_cnt); 
		//rt_printf("soe_save:cnt=%d,head=%d,tail=%d.\r\n",es_save_cnt,head,g_soe_queue.tail_eep.n);
	}

	log_str_ext_flush(LOG_YX);
	log_str_ext_flush(LOG_SOE);

	rt_stat_other_in(5,bsp_ustimer_get_duration(us0)/USTIMER_US);
}


void soe_clear_ev(void)
{
	u32 i,addr;
	u8 tempflag[4];
#ifdef BATTERY_WITH_COMM
	bool b_bat_comm=false;
#endif
	tempflag[0]=0;
	tempflag[1]=0;
	tempflag[2]=0;
	tempflag[3]=0;
	for(i=0;i<MAX_EVENT_NUMBER;i++)
	{		
		addr=EEP_SOE_ADDR+i*(sizeof(EVENT_SAVE));
		e2prom_write(addr,tempflag,4); 
	}

	for(i=0;i<MAX_VIP_NUMBER;i++)
	{		
		addr=EEP_VIP_ADDR+i*(sizeof(EVENT_SAVE));
		e2prom_write(addr,tempflag,4); 
	}
   
#ifdef ADD_DEBUG_RECORD_FUN
for(i=0; i<MAX_DEBUG_RECORD_NUMBER; i++)
{
	addr=EEP_DEBUG_RECORD_ADDR+i*(sizeof(EVENT_SAVE));
	e2prom_write(addr,tempflag,4);
}
#endif

	soe_init();
	tRunPara.sysData.wSwTzCount = 0;
	tRunPara.sysData.wSwHzCount = 0;
	tRunPara.sysData.bWriteFlag = TRUE;//无需等待定时扫描循环，立即保存数据

#ifdef BATTERY_WITH_COMM
	for(i=0;i<CFG_UART_NUM_MAX;i++)
	{
		if(tRunPara.tUartPara[i].wProtocol == PROTOCOL_POWER_MOD)
		{
			b_bat_comm = true;
		}
	}

	if(b_bat_comm)
	{
		ClearBattActInfo();
	}
#endif
}


void soe_clear_opt(void)
{
	u32 i,addr;
	u8 tempflag[4];
	tempflag[0]=0;
	tempflag[1]=0;
	tempflag[2]=0;
	tempflag[3]=0;
	for(i=0;i<MAX_OPERATE_NUMBER;i++)
	{		
		addr=EEP_OPERATE_ADDR+i*(sizeof(EVENT_SAVE));
		e2prom_write(addr,tempflag,4); 
	}
	
	soe_init();	
}


bool soe_check(u32 code)
{
	DWORD dv=g_ev_words[code/32];
	if(dv&(1<<(code%32)))
	{
		return true;
	}
	return false;
}


int soe_stat_reset(void)
{
	int i;

	g_soe_queue.stat_eep_over = 0;
	
	for(i=0;i<COMM_CHANNEL_NUM;i++)
	{
		g_soe_queue.stat_send_over[i] = 0;
		g_soe_queue.stat_ack_over[i] = 0;
	}
	
	for(i=0;i<SOE_TYPE_NUM;i++)
	{
		g_soe_queue.stat_cnt_type[i] = 0;
	}

	return 0;
}


int soe_info_printf(void)
{
	int i;
	
	rt_printf("事件记录:Num=%d,Index=%d,id=%d,ack=%d.\r\n",g_soe_info.wEvNumber,g_soe_info.wEvIndex,g_soe_info.wEvID,g_soe_info.wEvAck);
	rt_printf("操作记录:Num=%d,Index=%d,id=%d.\r\n\r\n",g_soe_info.wOpNumber,g_soe_info.wOpIndex,g_soe_info.wOpID);

	for(i=0;i<SOE_TYPE_NUM;i++)
	{
		rt_printf("事件统计[%02d]:%d.\r\n", i,g_soe_queue.stat_cnt_type[i]);
	}

	rt_printf("\r\n");
		rt_printf("保存队列  :head=%d,eep=%d,eep_over=%d.\r\n",
			g_soe_queue.head.n,g_soe_queue.tail_eep.n,g_soe_queue.stat_eep_over);
	for(i=0;i<COMM_CHANNEL_NUM;i++)
	{
		rt_printf("通讯队列[%02d]:head=%d,send=%d,ack=%d,send_over=%d,ack_over=%d.\r\n",
			i,g_soe_queue.head.n,g_soe_queue.tail_send[i].n,g_soe_queue.tail_ack[i].n,g_soe_queue.stat_send_over[i],g_soe_queue.stat_ack_over[i]);
	}

	return 0;
}

 
static u32 cnt_5ms;
static u32 cnt_act;
static u32 is_on;
void soe_test(void)
{
	if(g_soe_test)
	{

		cnt_5ms++;
		if(cnt_5ms == 10)
		{
			if(is_on == 0)
			{
				sw_do_pub(PUB_DO_08,SW_DO_TYPE_ON);
			}
			else
			{
				sw_do_pub(PUB_DO_08,SW_DO_TYPE_OFF);
				cnt_act++;
				if(cnt_act >= 100)
				{
					cnt_act = 0;
					
					cnt_5ms =0;
					g_soe_test = 0;
					rt_printf("soe_test完成。\r\n");
				}
			}

			is_on = !is_on;
			cnt_5ms = 0;
		}
	}
}

 
