/******************************************************************** 版权所有: 文件版本: V1.00 文件名称: IEC101.c 生成日期: 2010年04月26日 作 者:hhw 使用范围: 功 能: IEC101规约处理 更新信息: 更新日志1 修改者: 修改日期: 修改内容: 修改原因: *********************************************************************/ #include "head.h" u32 IEC101_SendEvent(IEC101_DEF *pt,int is_send_cos); void IEC101_Info_Printf_One(IEC101_DEF *p); extern int gprs_vs_write(const unsigned char * buf, int count); GPRS_INF_DEF tGprsInf; static void IEC101_State(IEC101_DEF *pt101,int state) { if(pt101->st101 != state) { char buf[64]; char log_info[128]; rt_printf_time("IEC101(%d) state: %d => %d!\r\n", pt101->chnl,pt101->st101,state); pt101->st101 = state; if(state == IEC101_ST_DATA) { sprintf(buf,"101主站连接(%s)!\r\n",get_comm_name(pt101->chnl + 1)); log_str_time(LOG_OPERATE,buf,0,1); sprintf(log_info,"IEC101(%d):101 main station connection",pt101->chnl+1); load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,1); if(log_flag.com_err[pt101->chnl] == true) { memset(log_info,0,sizeof(log_info)); sprintf(log_info,"IEC101(%d):Communication process anomaly return",pt101->chnl+1); load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,0); log_flag.com_err[pt101->chnl] = false; } // SOE发送缓冲区复位,发送指针回到ACK的位置 if(pRunSet->bTT_SoeResumeComm) { #ifdef SOE_RELINK_DISCOS if(!g_soe_queue.bSoeRelink[pt101->chnl]) { //g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_send[pt101->chnl].n; g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_eep.n; } g_soe_queue.bSoeRelink[pt101->chnl] = true; #endif //pt101->bSoeRelink = true; g_soe_queue.tail_send[pt101->chnl].n=g_soe_queue.tail_ack[pt101->chnl].n; } } else if(state == IEC101_ST_NONE) { sprintf(buf,"101主站断开(%s)!\r\n",get_comm_name(pt101->chnl + 1)); log_str_time(LOG_OPERATE,buf,0,0); sprintf(log_info,"IEC101(%d):101 main station disconnection",pt101->chnl + 1); load_hs_log_rcd(TYPE_CHNL_LINK,true,NULL,log_info,0); } } return; } static void IEC101_ResetTxBuf(IEC101_DEF *pt101) { pt101->btx_buf_m = false; pt101->btx_buf_s = false; memset(&pt101->tx_buf,0,sizeof(pt101->tx_buf)); } static void IEC101_Reset(IEC101_DEF *pt101) { // 通道属性、连接计数不允许复位 // 规约状态复位 g_comm_link_status &= ~(1<chnl); IEC101_State(pt101,IEC101_ST_NONE); pt101->bSendChange=false; // 计时 pt101->us0_sec = ustimer_get_origin(); pt101->secs = 0; pt101->us0_sendpiece=ustimer_get_origin(); // 对时 pt101->us0_frame = 0; pt101->ms_delay = 0; // 1级、2级数据发送 pt101->bFirstFCV = 1; pt101->tb_index = -1; pt101->FCBSave=0; // 遥测发送处理 pt101->bycsend = FALSE; pt101->ycno=0; pt101->us0_yc = ustimer_get_origin(); memset(pt101->yc_save,0,sizeof(pt101->yc_save)); // 其它状态复位 pt101->bRmtSM2=false; pt101->dValidTime=0; pt101->bMSend=false; pt101->bDIR=true; pt101->bResetProcess = false; pt101->tm.us0_callall=dTCounter; // 不能用微妙定时器,最多支持13分钟 pt101->tm.bAppEcho=false; //pt101->bSoeRelink = false; pt101->bTimeSyn = false; #if 0 // SOE发送缓冲区复位,发送指针回到ACK的位置 if(pRunSet->bTT_SoeResumeComm) { #ifdef SOE_RELINK_DISCOS if(!g_soe_queue.bSoeRelink[pt101->chnl]) { //g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_send[pt101->chnl].n; g_soe_queue.relink_tail_bak[pt101->chnl].n = g_soe_queue.tail_eep.n; } g_soe_queue.bSoeRelink[pt101->chnl] = true; #endif //pt101->bSoeRelink = true; g_soe_queue.tail_send[pt101->chnl].n=g_soe_queue.tail_ack[pt101->chnl].n; } #endif // 遥控信息复位 ResetRctrl(MASTER_101+pt101->chnl); // 发送缓冲区复位 IEC101_ResetTxBuf(pt101); RS_Recv_Enable(pt101->chnl); #ifdef RCD_STRAN_S rt_printf_time("-> Link Reset, Clear File Cache\r\n"); iec_freefile(&pt101->tf); pt101->tf.bTransing=false; pt101->tf.bdatTraned=false; #endif } void IEC101_Init(IEC101_DEF *pt,u8 chnl) { // 接收帧间隔为重发间隔时间的一半 g_tRsComm[chnl].us_recv_timeout = pRunSet->dT101Resend*USTIMER_SEC/2; memset(pt,0,sizeof(IEC101_DEF)); pt->type=IEC101_CHN; pt->chnl=chnl; pt->bMaintainComm=false; if(tRunPara.tUartPara[chnl].wProtocol==PROTOCOL_MAINTAIN) { pt->bMaintainComm=true; } // 初始化接收buf结构 pt->rx_buf.chn = pt->chnl; pt->rx_buf.buf = pt->recvbuf; rt_fifo_init(&pt->recv_fifo,pt->recv_fifo_buf,IEC101_FIFO_SIZE); #ifdef RCD_STRAN_S queue_init(&pt->rcd_qt.queue,NEWRCD_COUNT,&pt->rcd_qt.data,NEWRCD_SIZE); #endif IEC101_Reset(pt); } /************************************************************************** 函数名称:IEC101_Send 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:启动485口的数据发送 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_SSend(IEC101_DEF *pt) { u8 *sb = g_tRsComm[pt->chnl].sendbuf; // 如果链路层没有数据,直接返回 if(pt->btx_buf_s == false) { return; } // 有数据,复制到真正的发送BUF并清标志 memcpy(sb,pt->tx_buf_s,pt->tx_buf_s[0]+1); pt->btx_buf_s = false; // 置发送标志 RS_Send_Enable(pt->chnl); g_tRsComm[pt->chnl].nSendCnt=1; // 发送缓冲区的第一个字节为发送长度 pt->bSend=true; if(!g_bCommLed)g_bCommLed=true; if(UART_CHANNEL[pt->chnl]==CFG_UART_GPRS_IN ) { //gprs_vs_write(&sb[1], sb[0]); pt->bSend=false; s_stat_tx((int)pt->chnl, sb[0]); } #ifdef FUN_FUXI_ESAM if(UART_CHANNEL[pt->chnl]==CFG_UART_CHIP_S1 ) { write_chip_s1(&sb[1], sb[0]); pt->bSend=false; } #endif if(g_print_101 && (g_print_port & (1<chnl))) { rt_printf("TX_S101_%d:",pt->chnl); print_msg("",&sb[1],sb[0]); } //统计发送帧数 s_stat_tx_frame((int)pt->chnl); } /************************************************************************** 函数名称:IEC101_MSend 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:平衡101,作为主站发送 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_MSend(IEC101_DEF *pt) { u8 *sb = g_tRsComm[pt->chnl].sendbuf; // 如果链路层没有数据,直接返回 if(pt->btx_buf_m == false) { return; } // 有数据,复制到真正的发送BUF并清标志 memcpy(sb,pt->tx_buf_m,pt->tx_buf_m[0]+1); pt->btx_buf_m = false; // 置发送标志 RS_Send_Enable(pt->chnl); g_tRsComm[pt->chnl].nSendCnt=1; pt->bSend=true; if(!g_bCommLed)g_bCommLed=true; if(UART_CHANNEL[pt->chnl]==CFG_UART_GPRS_IN ) { //gprs_vs_write(&sb[1], sb[0]); pt->bSend=false; s_stat_tx((int)pt->chnl, sb[0]); } #ifdef FUN_FUXI_ESAM if(UART_CHANNEL[pt->chnl]==CFG_UART_CHIP_S1 ) { write_chip_s1(&sb[1], sb[0]); pt->bSend=false; } #endif if(g_print_101 && (g_print_port & (1<chnl))) { rt_printf("TX_M101_%d:",pt->chnl); print_msg("",&sb[1],sb[0]); } //统计发送帧数 s_stat_tx_frame((int)pt->chnl); } static void IEC101_ext_send(IEC101_DEF *pt, u8 *buf,u16 len) { #ifdef FUNC_ENCRY_IN_ONE_SERIAL if((tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) && pt->chnl==1) {//投入纬德规约时,串口通道1/3/4的待发数据统一由串口通道0发送处理 u8 *sb = g_tRsComm[0].extsendbuf_1; memcpy(sb,buf,len); g_tRsComm[0].extsendcnt_1=0; g_tRsComm[0].extsendlen_1=len; g_tRsComm[0].bextsend_1=true; } else if((tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) && pt->chnl==3) {//投入纬德规约时,串口通道1/3/4的待发数据统一由串口通道0发送处理 u8 *sb = g_tRsComm[0].extsendbuf_2; memcpy(sb,buf,len); g_tRsComm[0].extsendcnt_2=0; g_tRsComm[0].extsendlen_2=len; g_tRsComm[0].bextsend_2=true; } else if((tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) && pt->chnl==4) {//投入纬德规约时,串口通道1/3/4的待发数据统一由串口通道0发送处理 u8 *sb = g_tRsComm[0].extsendbuf_3; memcpy(sb,buf,len); g_tRsComm[0].extsendcnt_3=0; g_tRsComm[0].extsendlen_3=len; g_tRsComm[0].bextsend_3=true; } else #endif { u8 *sb = g_tRsComm[pt->chnl].extsendbuf; memcpy(sb,buf,len); g_tRsComm[pt->chnl].extsendcnt=0; g_tRsComm[pt->chnl].extsendlen=len; g_tRsComm[pt->chnl].bextsend=true; } } static void IEC101_Send_Polling(IEC101_DEF *pt) { #ifdef FUNC_ENCRY_IN_ONE_SERIAL int i=0,j=0,crc_sum=0; #endif u8 *buf; #ifdef FUNC_ENCRY_IN_ONE_SERIAL memset(&g_tRsComm[pt->chnl].enc_sendbuf,0,sizeof(&g_tRsComm[pt->chnl].enc_sendbuf)); #endif // 如果串口忙,不能发送数据 if(g_tRsComm[pt->chnl].bextsend #ifdef FUNC_ENCRY_IN_ONE_SERIAL || g_tRsComm[0].bextsend_1 || g_tRsComm[0].bextsend_2 || g_tRsComm[0].bextsend_3 #endif ) { pt->us0_sendpiece=ustimer_get_origin(); return; } if(pt->bSend)// 有数据要发送了 { int len=g_tRsComm[pt->chnl].sendbuf[0]; #ifdef FUNC_ENCRY_IN_ONE_SERIAL if(tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) {//纬德规约内容组包 g_tRsComm[pt->chnl].enc_sendbuf[i++] = 0xAA; g_tRsComm[pt->chnl].enc_sendbuf[i++] = (u8)(len); //数据报文长度 g_tRsComm[pt->chnl].enc_sendbuf[i++] = (u8)((len)>>8); g_tRsComm[pt->chnl].enc_sendbuf[i++] = pt->enc_tunnel; memcpy(&g_tRsComm[pt->chnl].enc_sendbuf[i],&g_tRsComm[pt->chnl].sendbuf[1],len); i = i+len; for(j=1;jchnl].enc_sendbuf[j]; } g_tRsComm[pt->chnl].enc_sendbuf[i++] = (u8)crc_sum; g_tRsComm[pt->chnl].enc_sendbuf[i++] = 0x55; buf = &g_tRsComm[pt->chnl].enc_sendbuf[0]; len = i; } else #endif { buf = &g_tRsComm[pt->chnl].sendbuf[1]; } if(pRunSet->bTT_ESAM&&(!pt->bMaintainComm)) { u8 ti,cot,se; u8 *out; iec_GetMsgInfo(buf,len,&ti,&cot,&se,true); len = sec_encrpt_iec(ti,cot,se,buf,len,&out); if(len > 0) { IEC101_ext_send(pt,out,len); } } else { IEC101_ext_send(pt,buf,len); } pt->bSend=false; return; } if(ustimer_get_duration(pt->us0_sendpiece) < USTIMER_MS*5) // 发送帧之间间隔5ms { return; } // 如果有从动站数据,发送 if(pt->btx_buf_s) { IEC101_SSend(pt); pt->us0_sendpiece=ustimer_get_origin(); } // 如果有启动站数据,发送 else if(pt->btx_buf_m) { IEC101_MSend(pt); pt->us0_sendpiece=ustimer_get_origin(); } else if(pt->bEchosecmd5) { u8 md5buf[24]; u16 len; len=sec_md5_echo(md5buf); IEC101_ext_send(pt,md5buf,len); pt->bEchosecmd5=false; } } /************************************************************************** 函数名称:IEC101_GetOneBuf 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:将数据添加到一级数据缓冲区 输入参数:pt 各通道101结构定义首地址,ps源数据地址 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static int IEC101_BufFreeNum(iec_tbuf_t *pt) { return (pt->tb_tail_ack - pt->tb_head - 1)&(IEC_TBUF_NUM-1); } static u16 IEC101_GetOneBuf(iec_tbuf_t *pt) { u16 head; head = pt->tb_head; pt->tb_head++; // 系统设计时,保证缓冲不会满,以下情况不应出现 if(pt->tb_head==pt->tb_tail_ack || pt->tb_head==pt->tb_tail_send) { rt_printf_time("IEC101: IEC101_GetOneBuf error:head=%d,send=%d,ack=%d.\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack); } return head; } /************************************************************************** 函数名称:IEC101_FrameEcho 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:应答短帧报文 输入参数:pt 各通道101结构定义首地址,ZoneByte短政控制域 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_FrameEcho(IEC101_DEF *pt,unsigned char ZoneByte) { u8 *pd=pt->tx_buf_s; // 如果此时正在发送数据,说明逻辑不对 if(pt->btx_buf_s) { rt_printf("IEC101_FrameEcho(%d) err:链路层BUF已有数据\r\n",pt->chnl); } // 更新ACD或DIR位 if(tRunPara.tUartPara[pt->chnl].b101PH) { // 平衡101,DIR置1 if(!pt->bDIR) { IEC101_SETDIR(ZoneByte); } } else { // 目前所有数据都当成一级数据,等新标准正式颁布后再分一、二级数据。 // 非平衡101, 如果还有一级数据,设置ACD位 if((pt->tx_buf[0].tb_head != pt->tx_buf[0].tb_tail_send) || (pt->tx_buf[1].tb_head != pt->tx_buf[1].tb_tail_send) || (pt->tx_buf[2].tb_head != pt->tx_buf[2].tb_tail_send)) { IEC101_SETACD(ZoneByte); } } // 形成报文 if(!tRunPara.b101Addr2Byte) { pd[0]=5; pd[1]=0x10; pd[2]=ZoneByte; pd[3]=(u8)tRunPara.byAddr; pd[4]=pd[2]+pd[3]; pd[5]=0x16; } else { pd[0]=6; pd[1]=0x10; pd[2]=ZoneByte; pd[3]=(u8)tRunPara.byAddr; pd[4]=(u8)(tRunPara.byAddr>>8); pd[5]=pd[2]+pd[3]+pd[4]; pd[6]=0x16; } } /************************************************************************** 函数名称:IEC101_GetClassOne 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:从一级数据缓冲区里读取数据 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_UpdateCOS(u8 *pd) { u8 sq,num,i,ti; u16 cp; u8 addr=0; //传送原因双字节 u8 SIQ; if(tRunPara.b101Cot2Byte) addr++; if(tRunPara.b101App2Byte) addr++; ti=pd[0]; sq = pd[1]&0x80; num = pd[1]&0x7f; cp = pd[4+addr] | (pd[5+addr]<<8); pd +=(6+addr); for(i=0;ichnl].b101PH ? pt101->btx_buf_m : pt101->btx_buf_s) { RT_PRINTF_POSITION(); return false; } // 运行到这,如果有数据就无条件发送 for(i=0;i<3;i++) { pt = &pt101->tx_buf[i]; if(pt->tb_head == pt->tb_tail_send) { continue; } // 更新发送尾指针,保留老指针备用。 // 由于系统设计时保证发送BUF队列不会溢出,所以指针先加也是没有问题的。 tb_send = pt->tb_tail_send; pt->tb_tail_send++; //拷贝数据并设置控制域 len = pt->tb_data[tb_send][0]; if(tRunPara.tUartPara[pt101->chnl].b101PH) { u8 zone; // 获取数据 pd = pt101->tx_buf_m; memcpy(pd,pt->tb_data[tb_send],len + 1); // 平衡101,将控制域修改,原来一级数据缓冲区中为8,现在需要变为3 zone=IEC101_CALL_DATA; // 合纵FTU现场,测试长帧,功能码需回2,不能是3 (原来为3,不合理) if(pd[7+tRunPara.b101Addr2Byte]==104) //长帧测试帧 { zone=IEC101_CALL_TESTLINK; pt101->bTestFrameL = true; } //梅州FTU,101主站总召前,通讯续传不回确认帧 if((pd[7+tRunPara.b101Addr2Byte]==0x1e)&&(pt101->bSendChange == 0)) //SOE { pt101->bSoeResume = true; } IEC101_SETFCV(zone); //FCV位有效 IEC101_SETPRM(zone); if(!pt101->bDIR)IEC101_SETDIR(zone); if(pt101->bMFCB) { IEC101_SETFCB(zone); } else { IEC101_CLRFCB(zone); } pd[5]=zone; } else { // 获取数据 pd = pt101->tx_buf_s; memcpy(pd,pt->tb_data[tb_send],len + 1); // 目前所有数据都当成一级数据,等新标准正式颁布后再分一、二级数据。 // 非平衡101,如果还有一级数据,设置ACD位 if((pt101->tx_buf[0].tb_head != pt101->tx_buf[0].tb_tail_send) || (pt101->tx_buf[1].tb_head != pt101->tx_buf[1].tb_tail_send) || (pt101->tx_buf[2].tb_head != pt101->tx_buf[2].tb_tail_send)) { IEC101_SETACD(pd[5]); } } // 更新低优先级的COS frm_attr = pt->tb_info[tb_send].frm_attr; if((frm_attr&(FRAME_I_PRIOR_LOW|FRAME_I_COS)) == (FRAME_I_PRIOR_LOW|FRAME_I_COS)) { IEC101_UpdateCOS(pd + 7 + tRunPara.b101Addr2Byte); } // 如果是延时获取帧,校验和计算前计算延时 if(frm_attr & FRAME_I_106) { u16 ms; u8 * info_addr; info_addr = pd + 1 + IEC101_InfoAddr(); ms = info_addr[2] + (info_addr[3] << 8); ms += ustimer_get_duration(pt101->us0_frame)/USTIMER_MS; info_addr[0] = 0; info_addr[1] = 0; info_addr[2] = ms; info_addr[3] = ms>>8; } // 计算校验和 cs=0; len = pd[2]; pd=&pd[5]; for(cnt=0;cnttb_index=i; // 设置安全报文发送标志 if(frm_attr & FRAME_I_ENCRYPT) { pt101->bMSend_encrypt = true; } return true; } return false; } static void IEC101_AckOneBuf(IEC101_DEF *pt101) //调整一级数据缓冲区,已发送数据清除 { iec_tbuf_t *pt; char log_info[128]={0}; // 如果最后发送的遥测帧,确认遥测帧 if (pt101->bycsend) { pt101->bycsend = FALSE; return; } // 如果没有发送报文,不需要确认 if(pt101->tb_index < 0) { return; } // 如果没有需要确认的帧,返回,但这不是正常流程 pt = &pt101->tx_buf[pt101->tb_index]; pt101->tb_index = -1; if(pt->tb_tail_ack == pt->tb_tail_send) { rt_printf_time("IEC101_AckOneBuf error!\r\n"); IEC101_Info_Printf_One(pt101); sprintf(log_info,"IEC101(%d):IEC101_AckOneBuf error!",pt101->chnl+1); load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1); log_flag.com_err[pt101->chnl] = true; return; } // 如果是SOE报文,需确认SOE缓冲池中SOE. if(pt->tb_info[pt->tb_tail_ack].frm_attr&FRAME_I_SOE) { soe_ack(pt101->chnl,pt->tb_info[pt->tb_tail_ack].soe_tail_send); } pt->tb_tail_ack++; } /************************************************************************** 函数名称: IEC101_Asdu_Yc 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:通用分类数据的链路直接响应 输入参数:pt 缓冲区地址,pdat报文数据,len报文长度 vsq,inf,cot ,rii,IEC101规约要求的内容 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_Asdu_Yc(IEC101_DEF *pt,u8 *pdat,u8 len,u8 vsq, u8 cot) { u8 *pd,*pdata; u8 cnt,checksum,sum; u8 AddByte=0; sum=0; if(tRunPara.tUartPara[pt->chnl].b101PH) { pdata=pt->tx_buf_m; } else { pdata=pt->tx_buf_s; } if(tRunPara.b101Addr2Byte) AddByte=1; pd=&pdata[7+AddByte]; if(tRunPara.bYcBDH) //标度化值 { pd[sum++]=11; //类型标示 } else if(tRunPara.bYcFloat) //浮点值 { pd[sum++]=13; //类型标示浮点数 } else { pd[sum++]=9; //类型标示 } pd[sum++]=vsq; //VSQ pd[sum++]=cot; //传送原因 if(tRunPara.b101Cot2Byte) pd[sum++]=0; //传送原因 pd[sum++]=(u8)tRunPara.byAddr; //应用服务单元地址 if(tRunPara.b101App2Byte) pd[sum++]=(u8)(tRunPara.byAddr>>8); //应用服务单元地址 for(cnt=0;cntchnl].b101PH) { pdata[5]=IEC101_CALL_DATA; IEC101_SETFCV(pdata[5]); //FCV位有效 IEC101_SETPRM(pdata[5]); if(!pt->bDIR)IEC101_SETDIR(pdata[5]); if(pt->bMFCB) { IEC101_SETFCB(pdata[5]); } else { IEC101_CLRFCB(pdata[5]); } } else { pdata[5]=IEC101_ECHO_DATA; //类型标示 } pdata[6]=(u8)tRunPara.byAddr; //链路地址 if(tRunPara.b101Addr2Byte) pdata[7]=(u8)(tRunPara.byAddr>>8); //链路地址 checksum=0; pd=&pdata[5]; for(cnt=0;cntbSendChange) || pt->bRmtSM2 || pt->bResetProcess || (!tRunPara.bAutoSendYc)) { return false; } //平衡101,主动发送不能小于发送间隔时间 if(tRunPara.tUartPara[pt->chnl].b101PH) { if((ustimer_get_duration(pt->us0_yc) < (DWORD)rt_round(tRunPara.fYcTime*USTIMER_SEC))|| byc_filter)// { return false; } } #if defined IEC_JXYB_DEAL || defined NO_SEND_YC if(g_run_stu.bjx) return false; #endif // 得到一遥测帧 pd=g_arrIECBuf; vsq=0; num=0; for(i=pt->ycno;iac_num;i++) { u8 QDS=0; //品质描述, 默认为零 u8 no=g_ac_table[i].indexno; long lmeaval; bool bsend=false; if(g_ac_table[i].dead_value==0||g_ac_table[i].normal==0) continue; //死区或额定为零,不主动上送 if(no>0) { DWORD dwMax=0; DWORD dwDeadVal=0; float rate; float f_65536=65536.0; u8 link_qds=0; no-=1; rate=(float)g_ac_table[i].rate/65536.0; //dwMax=(DWORD)g_ac_table[i].normal; dwMax = (tRunPara.bDeadType==true) ? (DWORD)g_ac_table[i].normal:_AbsL(pt->yc_save[i]); if(dwMax==0) //防止额定或者前一次保存值为0 dwMax=1; dwDeadVal=_Mul_Div_U(g_ac_table[i].dead_value,dwMax,6553600); if(dwDeadVal==0)dwDeadVal=1; if(dwMax==0)f_65536=1.0; lmeaval=GetRmtMeaVal(g_ac_table[i].owner,no,&link_qds); ff.ff=(float)lmeaval/f_65536*rate; //转为浮点数 #ifdef VOLT_ADAPTIVE_FACTOR if(pRunSet->bTT_AdaptiveFactor) { if(0 == g_ac_table[i].owner) { if((no <= PUB_AC_UCA1) && (no >= PUB_AC_UA1)) { ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt1_two; } else if((no <= PUB_AC_UCA2) && (no >= PUB_AC_UA2)) { ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt2_two; } } } #endif ycvalue=(int)rt_round(ff.ff); // 溢出处理 if(tRunPara.bYcFloat) { if(_AbsL(lmeaval) > (dwMax*10)) // 大于额定值的10倍*65536 { if(dwMax>0)QDS|=0x01; //遥测溢出 } } else { if (ycvalue >= 0) { if (ycvalue > 32767) { ycvalue = 32767; QDS|=0x01; //遥测溢出 } } else { if (ycvalue < (-32768)) { ycvalue = -32768; QDS|=0x01; //遥测溢出 } } } #ifdef FUNC_SEND_FAULT_CURRENT if(((no==PUB_AC_YC9) || (no==PUB_AC_YC10) || (no==PUB_AC_YC11) || (no==PUB_AC_YC12))) { bsend = true; } #endif // 如果大于死区值,上送 #ifndef IEC_JXYB_DEAL if(bsend || ((iyc_save[i])>=dwDeadVal))) #else if(bsend || ((iyc_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(iyc_save[i]=lmeaval; pd[num++]=(u8)cp; //信息体地址L pd[num++]=(u8)(cp>>8); //信息体地址H if(tRunPara.bYcFloat) //浮点值上送 { num += CopySwap(&pd[num],ff.tt,sizeof(ff.tt),true); } else { pd[num++]=(u8)(ycvalue); //无符号整数 pd[num++]=(u8)(ycvalue>>8); //无符号整数 } #if !defined GD_AREA_SHANTOU //&& !defined GD_AREA_FOS_2020 if(g_run_stu.bjx) { QDS|=0x80;//带品质描述,有效 <1>:=无效 } #endif if(pRunSet->bTT_QDS) { QDS|=link_qds; } #ifdef IEC_QDS_HAVE QDS|=link_qds; #endif pd[num++]=QDS; vsq++; } if(num>MAX_101_FRAME_LENTH) { bContinue=true; break; } } } // 更新下一帧的遥测序号 pt->ycno = bContinue ? (i+1) : 0; // 发送遥测帧 if(num>0) { IEC101_Asdu_Yc(pt,g_arrIECBuf,num,vsq,3); pt->us0_yc = ustimer_get_origin(); return true; } return false; } static void IEC101_LinkInf(IEC101_DEF *pt) { //68 bc bc 68 28 01 f0 08 14 01 01 40 00 00 00 00 00 00 00 00 00 00 00 00 00 遥测帧 // vsq cot appaddr u8 *pd,*pdata; u8 checksum,sum; int i; WORD cp; u8 num; WORD count=0; pdata=pt->tx_buf_s; sum=0; pd=&pdata[5]; pd[sum++]=IEC101_ECHO_DATA; //类型标示 pd[sum++]=(u8)tRunPara.byAddr; //链路地址 pd[sum++]=250; //类型标示 pd[sum++]=8; //长度 pd[sum++]=14; //传送原因 pd[sum++]=1; //KOD pd[sum++]=0; //统计遥测遥信个数 cp=g_di_table[0].cp; num=0; for(i=0;idi_num;i++) // 遥信个数统计 { if(g_di_table[i].cp==(cp+num)) { num++; } else { pd[sum++]=1; //遥信类型 pd[sum++]=num; pd[sum++]=(u8)cp; //遥信个数 pd[sum++]=(u8)(cp>>8); //遥信个数 count++; cp=g_di_table[i].cp; num=1; } } if(num) { pd[sum++]=1; //遥信类型 pd[sum++]=num; pd[sum++]=(u8)cp; //遥信点号 pd[sum++]=(u8)(cp>>8); //遥信点号 count++; } cp=g_ac_table[0].cp; num=0; for(i=0;iac_num;i++) // 遥测个数统计 { if(g_ac_table[i].cp==(cp+num)) { num++; } else { pd[sum++]=2; //遥测类型 pd[sum++]=num; pd[sum++]=(u8)cp; //遥测个数 pd[sum++]=(u8)(cp>>8); //遥测个数 count++; cp=g_ac_table[i].cp; num=1; } } if(num) { pd[sum++]=2; //遥测类型 pd[sum++]=num; pd[sum++]=(u8)cp; //遥测个数 pd[sum++]=(u8)(cp>>8); //遥测个数 count++; } pdata[0]=sum+6; //报文长度 +2=控制域、地址 +6=报文头(4字节)校验和、结束符 pdata[1]=0x68; //报文头 pdata[2]=sum; //数据长度 pdata[3]=sum; pdata[4]=0x68; pdata[11]=count; checksum=0; pd=&pdata[5]; for(i=0;itx_buf[1]; if(attr&FRAME_I_PRIOR_HIGH)ptb=&pt->tx_buf[0]; else if(attr&FRAME_I_PRIOR_LOW)ptb=&pt->tx_buf[2]; head = IEC101_GetOneBuf(ptb); // 保存数据 ptb->tb_info[head].frm_attr = attr; ptb->tb_info[head].soe_tail_send = g_soe_queue.tail_send[pt->chnl].n; L = IEC101_InfoAddr() + len - 4; // 报文生成 pd=ptb->tb_data[head]; *pd++=L+6; //报文长度 +2=控制域、地址 +6=报文头(4字节)校验和、结束符 *pd++=0x68; //报文头 *pd++=L; //数据长度 *pd++=L; *pd++=0x68; *pd++=IEC101_ECHO_DATA; //非平衡功能码,平衡通道发送时再修改 *pd++=(u8)tRunPara.byAddr; //链路地址1 if(tRunPara.b101Addr2Byte) { *pd++=(u8)(tRunPara.byAddr>>8); //链路地址2 } *pd++=type; //类型标示 *pd++=vsq; //VSQ *pd++=cot; //传送原因1 if(tRunPara.b101Cot2Byte) { *pd++=0; //传送原因2 } *pd++=(u8)tRunPara.byAddr; //应用服务单元地址1 if(tRunPara.b101App2Byte) { *pd++=(u8)(tRunPara.byAddr>>8); //应用服务单元地址2 } for(cnt=0;cntus0_frame)/USTIMER_MS; // rt_printf("时间设置:T2=%dms,T3=%dms.\r\n",pt101->ms_delay,ms); ms += pt101->ms_delay; // 修正时间 ct.ms += ms; if(ct.ms>=60000) { ct.ms-=60000; sys_time_change(&ct); } // 设置时间 sys_time_set_rmt(&ct); } } clk_time_get(&ts); timespec_to_rtc(ts,&ct,1); // 得到星期 rtc_to_timespec(&ct, &ts); week = WEEK_DAY(ts.tv_sec); //组织报文,以最新时间返回 pd=g_arrIECBuf; num=0; #if defined GD_AREA_ZHONGSHAN || defined GD_AREA_ZHONGSHAN_2020 pd[num++]=0; //信息序号=0时钟同步 pd[num++]=0; //信息序号=0时钟同步 if(scot==IEC_COT_ACT) { pd[num++]=ps[0]; //MsL pd[num++]=ps[1]; //MsH pd[num++]=ps[2]; //Min pd[num++]=ps[3]; //hour pd[num++]=ps[4]; //Day pd[num++]=ps[5]; //month pd[num++]=ps[6]; //year } else { pd[num++]=(u8)ct.ms; //MsL pd[num++]=(u8)(ct.ms>>8); //MsH pd[num++]=ct.min; //Min pd[num++]=ct.hour; //hour pd[num++]=ct.day; //Day pd[num++]=ct.month; //month pd[num++]=ct.year; //year } #else pd[num++]=0; //信息序号=0时钟同步 pd[num++]=0; //信息序号=0时钟同步 pd[num++]=(u8)ct.ms; //MsL pd[num++]=(u8)(ct.ms>>8); //MsH pd[num++]=ct.min; //Min pd[num++]=ct.hour; //hour pd[num++]=((week<<5) | ct.day); //Day pd[num++]=ct.month; //month pd[num++]=ct.year; //year #endif if(scot == IEC_COT_REQ) { cot=0x05; } else { cot = check_time ? 0x07:0x47; //对时内容非法,即将传送原因的P/N位置为1 } IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,cot,103,FRAME_I);// } static void IEC101_TestFrame(IEC101_DEF *pt101,u8 *ps) //测试确认帧 { u8 *pd; int num; //组织报文 pd=g_arrIECBuf; num=0; pd[num++]=0; //信息体地址 pd[num++]=0; // pd[num++]=ps[0];//pd[num++]=0xAA; //测试码 pd[num++]=ps[1];//pd[num++]=0x55; //测试码 IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,7,104,FRAME_I|FRAME_I_PRIOR_LOW);// } /************************************************************************** 函数名称:IEC101_InitOK 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:子站初始化完毕 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_InitOK(IEC101_DEF *pt101) { u8 *pd; int num; u8 cot; //组织报文 pd=g_arrIECBuf; num=0; pd[num++] = 0; pd[num++] = 0; if(pt101->bInitCot) { pd[num++]=2; //远方复位 } else { pd[num++]=0; //当地电源合上 } pt101->bInitCot=true; cot = pRunSet->bTT_101Cot03 ? 3 : 4; IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,cot,70,FRAME_I);//70 初始化结束帧 } // 复位进程 static void IEC101_ResetProcess(IEC101_DEF *pt101,u8 qrp) { u8 *pd; int num; //组织报文 pd=g_arrIECBuf; num=0; pd[num++] = 0; pd[num++] = 0; pd[num++] = qrp; IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,7,105,FRAME_I); } /************************************************************************** 函数名称:IEC101_CallAll 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:通用范围总查询响应 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_CallAll(IEC101_DEF *pt,u8 QOI) //确认帧 { u8 *pd,*pdata; u8 vsq;//通用分类数据集数目 u16 cp=0,attr; int i,num,begin,end; pdata=g_arrIECBuf; pt->ycno=0; attr = pt->bSendChange ? FRAME_I_PRIOR_LOW : FRAME_I;//| FRAME_I_PRIOR_HIGH //总召唤确认帧 num=0; pd=pdata; pd[num++] = 0; pd[num++] = 0; pd[num++] = QOI; IEC101_Asdu_Add(pt,pdata,num,1,7,100, FRAME_I); // 遥测帧 if((QOI==IEC_QOI_CALL_ALL_GROUP)|((QOI >= IEC_QOI_CALL_YC_BEGIN && QOI <= IEC_QOI_CALL_YC_END))) { int ycvalue; u8 ti; union{ float ff; u8 tt[4]; }ff; long lmeaval; // 类型标识 if(tRunPara.bYcBDH) { ti=11; // 标度化值 } else if(tRunPara.bYcFloat) { ti=13; // 浮点值 } else { ti=9; // 归一化值 } // 确认起止点号 if(QOI == IEC_QOI_CALL_ALL_GROUP || pRunSet->dGroupYcSize == 0) { begin = 0; end = g_table_head->ac_num; } else if(pRunSet->dGroupYcSize) { begin = (QOI - IEC_QOI_CALL_YC_BEGIN) * pRunSet->dGroupYcSize; end = begin + pRunSet->dGroupYcSize; if(end > g_table_head->ac_num) { end = g_table_head->ac_num; } } // 得到第一点的通讯点号,必须检查g_ac_table,否则有可能系统崩溃 if(g_ac_table) { cp=g_ac_table[begin].cp; } // 初始数据 pd=pdata; vsq=0; num=0; // 如果点号连续,保存第一点点号 if(pRunSet->bDIContinue) { pd[num++]=(u8)cp; pd[num++]=(u8)(cp>>8); } // 发送begin到end之间的所有遥测值 for(i=begin;i0) { DWORD dwMax=0; float rate; float f_65536=65536.0; no-=1; rate=(float)g_ac_table[i].rate/65536.0; //dwMax=(DWORD)g_ac_table[i].normal; dwMax = (tRunPara.bDeadType==true) ? (DWORD)g_ac_table[i].normal:_AbsL(pt->yc_save[i]); if(dwMax==0) //防止额定或者前一次保存值为0 dwMax=1; lmeaval=GetRmtMeaVal(g_ac_table[i].owner,no,&link_qds); if(dwMax==0)f_65536=1.0; ff.ff=((float)lmeaval)/f_65536*rate; //转为浮点数 #ifdef VOLT_ADAPTIVE_FACTOR if(pRunSet->bTT_AdaptiveFactor) { if(0 == g_ac_table[i].owner) { if((no <= PUB_AC_UCA1) && (no >= PUB_AC_UA1)) { ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt1_two; } else if((no <= PUB_AC_UCA2) && (no >= PUB_AC_UA2)) { ff.ff=((float)lmeaval)/f_65536*10/pRunSet->pt2_two; } } } #endif ycvalue=(int)rt_round(ff.ff); if(tRunPara.bYcFloat) { if(_AbsL(lmeaval) > (dwMax*10)) // 大于额定值的10倍*65536 { if(dwMax>0)QDS|=0x01; //遥测溢出 } } else { if (ycvalue >= 0) { if (ycvalue > 32767) { ycvalue = 32767; QDS|=0x01; //遥测溢出 } } else { if (ycvalue < (-32768)) { ycvalue = -32768; QDS|=0x01; //遥测溢出 } } } } else //备用点号,数值上送0 { lmeaval=0; ycvalue=0; ff.ff=0; if(!pRunSet->bDIContinue) continue; // 点号不连续上送,可不送备用点 } //如果在测试模式下,置无效状态 if(g_run_stu.bToolRmtTest||g_run_stu.bHmiRmtTest) { if(pRunSet->bTT_RmtTest)QDS|=0x80;//带品质描述,有效 <1>:=无效 } #if !defined GD_AREA_SHANTOU && !defined GD_AREA_FOS_2020 && !defined FUNC_JX_YC_SIQ00 if(g_run_stu.bjx) { QDS|=0x80;//带品质描述,有效 <1>:=无效 } #endif if(pRunSet->bTT_QDS) { QDS|=link_qds; } #ifdef IEC_QDS_HAVE QDS|=link_qds; #endif //保存遥测值,供主动遥测上送比较用 if(iyc_save[i]=lmeaval; // 如果点号不连续,上送每个点的点号 if(!pRunSet->bDIContinue) { cp=g_ac_table[i].cp; pd[num++]=(u8)cp; pd[num++]=(u8)(cp>>8); } // 上送值 if(tRunPara.bYcFloat) { //浮点值上送 num += CopySwap(&pd[num],ff.tt,sizeof(ff.tt),true); } else { // 整型 pd[num++]=(u8)(ycvalue); pd[num++]=(u8)(ycvalue>>8); } pd[num++]=QDS; vsq++; // 发送一帧 if(num>MAX_101_FRAME_LENTH&&(iac_num-1)) { u8 vsq1=vsq; if(pRunSet->bDIContinue) { vsq1|=0x80; } IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti,attr); num=0; pd=pdata; if(pRunSet->bDIContinue) { pd[num++]=(u8)(cp+vsq); pd[num++]=(u8)((cp+vsq)>>8); cp+=vsq; } vsq=0; } } if(num>2) { u8 vsq1=vsq; if(pRunSet->bDIContinue) { vsq1|=0x80; } IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti,attr); } pt->us0_yc = ustimer_get_origin(); } // 事件帧 if((QOI == IEC_QOI_CALL_ALL_GROUP) || (QOI >= IEC_QOI_CALL_YX_BEGIN && QOI <= IEC_QOI_CALL_YX_END)) { u8 ti; u8 yxtype=0; // 确定类型标识,单点(1)或双点(3) ti = tRunPara.bDPI ? 3:1; // 确定起止点号 if(QOI == IEC_QOI_CALL_ALL_GROUP || pRunSet->dGroupYxSize == 0) { begin = 0; end = g_table_head->di_num; } else { begin = (QOI - IEC_QOI_CALL_YX_BEGIN) * pRunSet->dGroupYxSize; end = begin + pRunSet->dGroupYxSize; if(end > g_table_head->di_num) { end = g_table_head->di_num; } } // 初始数据 pd=pdata; vsq=0; num=0; // 得到第一点的通讯点号,必须检查g_di_table,否则有可能系统崩溃 if(g_di_table) { cp=g_di_table[begin].cp; yxtype=tbl_get_dpi(begin); } // 如果点号连续,保存第一点点号 if(pRunSet->bDIContinue) { pd[num++]=(u8)cp; pd[num++]=(u8)(cp>>8); } // 发送begin到end之间的所有遥信值 for(i=begin;i2) { u8 vsq2=vsq; u8 ti2=yxtype ? 3:1; yxtype=type; if(pRunSet->bDIContinue) { vsq2|=0x80; } IEC101_Asdu_Add(pt,pdata,num,(vsq2),QOI,ti2, attr| FRAME_I_COS);//cot=9总查询 num=0; pd=pdata; if(pRunSet->bDIContinue) { pd[num++]=(u8)(cp+vsq); pd[num++]=(u8)((cp+vsq)>>8); cp+=vsq; } vsq=0; yxtype=type; } SIQ = tbl_get_yx(i,type); ti=type ? 3:1; } else { SIQ = tbl_get_yx(i,tRunPara.bDPI); } #if !defined GD_AREA_SHANTOU && !defined GD_AREA_FOS_2020 && !defined FUNC_JX_YX_BEFORE if(g_run_stu.bjx) { SIQ|=0x80;//带品质描述,有效 <1>:=无效 } #endif if(g_run_stu.bToolRmtTest||g_run_stu.bHmiRmtTest) //测试模式,遥信置为无效 { if(pRunSet->bTT_RmtTest)SIQ|=0x80; } // 点号 if(!pRunSet->bDIContinue) { cp=g_di_table[i].cp; pd[num++]=(u8)cp; pd[num++]=(u8)(cp>>8); } pd[num++]=SIQ; vsq++; if(((vsq>120)||(num>MAX_101_FRAME_LENTH))&&(idi_num-1)) // ngd不能超过128,因个数最高位为连续标示位 { u8 vsq1=vsq; if(pRunSet->bDIContinue) { vsq1|=0x80; } IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti, attr| FRAME_I_COS);//cot=9总查询 num=0; pd=pdata; if(pRunSet->bDIContinue) { pd[num++]=(u8)(cp+vsq); pd[num++]=(u8)((cp+vsq)>>8); cp+=vsq; } vsq=0; } } if(num>2) { u8 vsq1=vsq; if(pRunSet->bDIContinue) { vsq1|=0x80; } IEC101_Asdu_Add(pt,pdata,num,(vsq1),QOI,ti, attr| FRAME_I_COS);//cot=9总查询 } } // 总召唤结束帧 num=0; pd=pdata; pd[num++] = 0; pd[num++] = 0; pd[num++] = QOI; IEC101_Asdu_Add(pt,pdata,num,1,10,100, attr); // 不允许通讯续传的情况下,第一次总召先清SOE队列,保证上传事件是总召后产生的,后续总召不清SOE队列 if((pRunSet->bTT_SoeResumeComm == 0) &&(!pt->bSendChange)) { g_soe_queue.tail_send[pt->chnl].n=g_soe_queue.head.n; g_soe_queue.tail_ack[pt->chnl].n = g_soe_queue.tail_send[pt->chnl].n; } #ifdef FUN_SOE_DELAY_TIME if(pt->bSendChange == false) { pt->DTSendChange = dTCounter; pt->bfirst_call = true; } #endif // 置总召标志 pt->bSendChange=true; } static void IEC101_DDAll(IEC101_DEF *pt,u8 QCC) //确认帧 { u8 *pd,*pdata; u8 vsq,cot;//通用分类数据集数目 u16 cp; int i,num; pdata=g_arrIECBuf; num=0; //总召唤确认帧 pd=pdata; pd[num++] = 0; pd[num++] = 0; pd[num++] = QCC; IEC101_Asdu_Add(pt,pdata,num,1,7,101, FRAME_I); // 电度值 if((QCC==1)||(QCC==5)) { // 初始数据 pd=pdata; vsq=0; num=0; cp=g_dd_table[0].cp; if(QCC==5)cot=37; // 总召 else cot=QCC+37; // 召某一组 pd[num++]=(u8)cp; pd[num++]=(u8)(cp>>8); for(i=0;idd_num;i++) { int sw,no; float rate; union{ float ff; u8 tt[4]; }ff; sw=g_dd_table[i].owner; no=g_dd_table[i].indexno; if(no>0) { rate=(float)g_dd_table[i].rate/65536.0; ff.ff=GetRmtDdVal(sw,no-1)*rate; } else { ff.ff=0; } num += CopySwap(&pd[num],ff.tt,sizeof(ff.tt),true); pd[num++]=0; vsq++; // 发送一帧 if((num>MAX_101_FRAME_LENTH)||(i==g_table_head->dd_num-1)) { IEC101_Asdu_Add(pt,pdata,num,(vsq|0x80),cot,206,FRAME_I_PRIOR_LOW); cp+=vsq; pd=pdata; num=0; vsq=0; pd[num++]=(u8)cp; pd[num++]=(u8)(cp>>8); } } } if(QCC==0x45)//主站扩展瞬时冻结电度命令 { //rand_file_info.b_frz=true; } // 总召唤结束帧 num=0; pd=pdata; pd[num++] = 0; pd[num++] = 0; pd[num++] = QCC; IEC101_Asdu_Add(pt,pdata,num,1,10,101, FRAME_I_PRIOR_LOW); } /************************************************************************** 函数名称:IEC101_RmtCtrl 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:遥控处理 输入参数: 点号开始21 0b 00 82 (10-13) 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_Echo_YK(IEC101_DEF *pt,u16 cp,u8 ti,u8 cot,u8 v) { u8 *pd,*pdata; int num; pdata=g_arrIECBuf; pd = pdata; num=0; pd[num++]=(u8)cp; pd[num++]=(u8)(cp>>8); pd[num++]=v; IEC101_Asdu_Add(pt,pdata,num,1,cot,ti,FRAME_I|FRAME_I_PRIOR_HIGH);//cot=9总查询 pt->bRmtSM2 = false; //遥控执行完成,清除SM2标志,开放事件上传 } void IEC101_Echo_PAR(IEC101_DEF *pt,u16 cp,u8 ti,u8 cot,BYTE *buf,u8 len,u8 vsq) { u8 *pd,*pdata; int num; pdata=g_arrIECBuf; pd = pdata; num=0; //pd[num++]=(u8)cp; //pd[num++]=(u8)(cp>>8); memcpy(&pd[num],buf,len); num+=len; //pd[num++]=v; IEC101_Asdu_Add(pt,pdata,num,vsq,cot,ti,FRAME_I);//cot=9总查询 } int get_yk_info_cp(u8 cp) { int i; for(i=0;ido_num;i++) { //rt_printf("cp=%X link_ch=%x cp=%x \r\n",cp,(g_do_table[i].link_ch&0x0f),g_do_table[i].link_cp); if((g_do_table[i].link_ch&0x01f) == cp) { return (u8)i; } } return -1; } u8 get_par_link_101_to_104(BYTE *out,BYTE *in,BYTE parNum,u8 ti) { int size=0,i=0,j=0; int no=0; if((ti==FOS_PAR_SET_MUL) || (ti==FOS_PAR_SET_ONE)) size = (parNum*(sizeof(float)+2+1)); //写定值时需多一位QOS位 else if(ti==FOS_PAR_READ_MUL || ti==FOS_PAR_READ_ONE) size = (parNum*(sizeof(float)+2)); rt_printf("size=%d,parNum=%d\n\r",size,parNum); for(no=0;nolink_cp = (Addr/pRunSet->dYC_num)+1; //转发通信地址 #else parTab->link_cp = (Addr/FOS_PAR_SW_NUM)+1; //转发通信地址 #endif //通过遥控的级联地址去判断遥参是否合法 ykCp = get_yk_info_cp(parTab->link_cp); if(ykCp<0) { rt_printf("定值参数未找到Yk对应的级联地址=%X link_ch=%x \r\n",parAddr,parTab->link_cp); return false; } memcpy(parTab,&g_do_table[ykCp],sizeof(g_do_table[0])); //将遥控配置的转发信息赋到遥参 #if 0 parTab->cp = Addr%FOS_PAR_SW_NUM + FOS_PAR_STATR_ADDR; //转发点号 #endif parTab->link_cp = parTab->cp = parAddr; //转发通信地址 #ifdef YC_QUANTITY rt_printf("\n parAddr=%x link_cp=%x cp=%x link_ch=0X%X ykCp=%d linkAddr=%x\r\n",parAddr,parTab->link_cp,parTab->link_ch,parTab->cp,ykCp, Addr%pRunSet->dYC_num); #else rt_printf("\n parAddr=%x link_cp=%x cp=%x link_ch=0X%X ykCp=%d linkAddr=%x\r\n",parAddr,parTab->link_cp,parTab->link_ch,parTab->cp,ykCp, Addr%FOS_PAR_SW_NUM); #endif return true; } #ifdef FUNC_MORE_PRESET YT_BUF PresetData101[256]; YT_BUF SetData101[256]; int WrongCount101[256]; int g_CountPreset101=0, g_CountSet101=0; bool gb_101_yt=false; #endif static void IEC101_ParCtrl(IEC101_DEF *pt,u8 *pdat) // ps从传送原因开始 { u8 cot,ykcot,ykvalue; WORD parNum=1; WORD ykno=0; WORD yksave=0; WORD parAddr=0; u8 AppByte=0; u8 CotByte=0; u8 *ps=&pdat[2]; u8 ti=pdat[0]; //类型标示 struct do_table parTab; BYTE buf[256]; BYTE len=0; BYTE addrStart=0; WORD sw=1; static DWORD ctrlParTime=0; //系统定时器 static bool bSetParaDisable=false; if(tRunPara.b101App2Byte) //应用单元双字节地址 { AppByte=1; } if(tRunPara.b101Cot2Byte) //双字节传送原因 { CotByte=1; } ykcot=(ps[0]&0x01f); parNum=(pdat[1]&0xff);//遥参数量,支持遥调大于30个定值 ykno=ps[2+AppByte+CotByte]+(WORD)ps[3+AppByte+CotByte]*256; addrStart = 2+AppByte+CotByte; yksave=ykno; cot=IEC_COT_UN_OA | IEC_COT_PN; parAddr = (ps[addrStart+1]<<8)+ps[addrStart];//遥参通信地址 len = get_par_link_data(&buf[0],&ps[addrStart],parNum,ti);//2+(parNum*4);//2:点号 4:参数值 ykvalue=ps[AppByte+CotByte+2-1+7];// rt_printf("ti=%d parAddr=%x parNum= %d recDataLen=%d addrStart=%d ykvalue=%x ykcot=%02x\r\n",ti,parAddr,parNum,len,addrStart,ykvalue,ykcot); if(pRunSet->wEquTypeManager)// 通信管理机模式,转发 { #ifdef YC_QUANTITY if(!get_par_ch_info(parAddr,&parTab)||parNum>pRunSet->dYC_num) #else if(!get_par_ch_info(parAddr,&parTab)||parNum>FOS_PAR_SW_NUM) #endif { cot = IEC_COT_ACTCON | IEC_COT_PN; // 否定应答 //refresh_co_file(true,ti,yktype,ykno,cot,yk_cmd); return ; } //parTab.vsq = parNum; if(lnk_master_par(&parTab ,ti,ps[0],len,&buf[0],parNum) == 0) { #ifdef FUNC_MORE_PREREAD /*级联遥参方式:0开关,1队列*/ pt->lnk_par_send_flag |= (1<lnk_par_send_flag,g_link_par_cnt); g_link_par_cnt = (g_link_par_cnt+1)%SWITCH_NUM_EXT_PUB; #else pt->lnk_par_send_flag |= (1<us0_lnk_par = ustimer_get_origin(); } } else if (pRunSet->wEquTypeDFTU)// 测控装置遥参交互 { WORD parCount; WORD addLenParLen; #ifdef FUNC_MORE_PRESET int n; #endif cot=IEC_COT_ACTCON; //ykvalue = (ti==FOS_PAR_SET_ONE) ? (ykvalue|0x80) :ykvalue; #ifndef FUNC_MORE_PRESET bfirst_sel = false; #endif #ifdef FUNC_MORE_PRESET if((dTCounter-ctrlParTime) > tRunPara.dYKTime) {//超时清零预置/执行/出错数据存储区 memset(SetData101,0,sizeof(SetData101)); g_CountSet101 = 0; memset(PresetData101,0,sizeof(PresetData101)); g_CountPreset101 = 0; memset(WrongCount101,0,sizeof(WrongCount101)); rt_printf("超时清除101报文缓存区\r\n"); } #endif if(ti==FOS_PAR_SET_ONE || ti==FOS_PAR_SET_MUL) { if(ykvalue&0x80) { #ifdef FUNC_MORE_PRESET memcpy(&PresetData101[g_CountPreset101++].yc_data,&ps[addrStart],parNum*7); rt_printf_time("101预置报文[第%d帧]:",g_CountPreset101); for(n=0;ndYC_num); #else parAddr = (ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]-((tRunPara.byAddr-1)*FOS_PAR_SW_NUM); #endif //parAddr = (ps[addrStart+1]<<8)+ps[addrStart];//遥参通信地址 #ifdef YC_QUANTITY if( ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) >= FOS_PAR_STATR_ADDR && ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) < (FOS_PAR_STATR_ADDR+pRunSet->dYC_num)) #else if( ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) >= FOS_PAR_STATR_ADDR && ((ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]) < (FOS_PAR_STATR_ADDR+FOS_PAR_SW_NUM)) #endif { parAddr = (ps[addrStart+1+addLenParLen]<<8)+ps[addrStart+addLenParLen]; } rt_printf("parAddr=%x addr2=%X %x addrStart=%x\r\n",parAddr,(ps[addrStart+1]<<8)+ps[addrStart],addLenParLen,addrStart); if(ti==FOS_PAR_READ_ONE || ti==FOS_PAR_READ_MUL) { rt_printf("读取遥参激活 parAddr=%x\r\n",parAddr); if(!readRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen]))//暂默认为0开关 cot = IEC_COT_ACTCON | IEC_COT_PN; // 否定应答// memcpy(&ps[addrStart+2],"1234",sizeof(float));//2:参数点表地址 } else { #ifdef FUNC_FIT_QOS if(ykcot == IEC_COT_DEACT) { cot = IEC_COT_DEACTCON; // 预置取消确认 bSetParaDisable = true; //取消预置时置1 rt_printf("QOS(101)=%02x,取消遥参预置...\r\n",ykvalue); break; } #endif if(ykvalue&0x80) { ctrlParTime = dTCounter; //系统时间戳 if(!selectRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen])) { cot = IEC_COT_ACTCON | IEC_COT_PN; #ifdef FUNC_MORE_PRESET WrongCount101[g_CountPreset101-1] = g_CountPreset101; #else bSetParaDisable = true; //预置报文出错时置1,禁止固化定值 #endif break; } else { bSetParaDisable = false; //重新预置时清0 } } else { #ifdef FUNC_MORE_PRESET int s_stop; s_stop = WrongCount101[g_CountSet101-1]; #endif #if defined FUNC_MORE_PRESET if(ykcot == IEC_COT_DEACT) { cot = IEC_COT_DEACTCON; // 预置取消确认 bSetParaDisable = true; //取消预置时置1 rt_printf("取消遥参预置...\r\n"); break; } else if( dTCounter-ctrlParTime > tRunPara.dYKTime || (0 != s_stop) || bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen],ti))//暂默认为0开关 #elif defined FUNC_FIT_QOS if( dTCounter-ctrlParTime > tRunPara.dYKTime || bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen],ti))//暂默认为0开关 #else if(ykcot == IEC_COT_DEACT) { cot = IEC_COT_DEACTCON; // 预置取消确认 bSetParaDisable = true; //取消预置时置1 rt_printf("取消遥参预置...\r\n"); break; } else if( dTCounter-ctrlParTime > tRunPara.dYKTime || bSetParaDisable || !wirteRunParId(sw,parAddr,&ps[addrStart+2+addLenParLen],ti))//暂默认为0开关 #endif { if(dTCounter-ctrlParTime > tRunPara.dYKTime) { bSetParaDisable = false; //超时后才清0,避免取消预置后在超时时间内多次发执行帧而误执行 rt_printf("设置遥参超时...\r\n"); } #ifdef FUNC_MORE_PRESET else if(0 != s_stop) { rt_printf("[第%d帧]101预置报文出错,请检查...\r\n",s_stop); } #endif else if(bSetParaDisable) { #ifdef FUNC_MORE_PRESET rt_printf("取消预置,禁止固化定值\r\n"); #else rt_printf("预置报文出错或取消预置,禁止固化定值\r\n"); #endif } cot = IEC_COT_ACTCON | IEC_COT_PN; // 否定应答 //屏蔽break语句,除出错定值外均可修改,与104遥调统一做法 //break; } } } } #ifdef FUNC_MORE_PRESET gb_101_yt = false; #endif //parTab.vsq = parNum; // if(cot&IEC_COT_PN) rt_printf("设置&&读取 遥参失败 parAddr=%x\r\n",parAddr); else rt_printf("设置&&读取 遥参OK parAddr=%x\r\n",parAddr); #ifndef FUNC_MORE_PRESET if((!(ykvalue&0x80) && ti==FOS_PAR_SET_MUL) #ifdef FUNC_FIT_QOS || (ykcot == IEC_COT_DEACT) #endif ) ctrlParTime = 0; #endif IEC101_Echo_PAR(pt,yksave,ti,cot,&ps[addrStart],len,parNum); #ifndef FUNC_MORE_PRESET if((!(ykvalue&0x80)) #ifdef FUNC_FIT_QOS || (ykcot == IEC_COT_DEACT) #endif ) { memset(buf_forCompare,0,sizeof(buf_forCompare)); i_forCompare = 0; j_forCompare = 0; } #endif return ; } cot = (ykcot == IEC_COT_ACT) ? IEC_COT_ACTCON : IEC_COT_DEACTCON; cot |= IEC_COT_PN; //rt_printf("设置遥参 error parAddr=%x\r\n",parAddr); //IEC101_Echo_PAR(pt,yksave,ti,cot,&ps[addrStart],len); } static void IEC101_RmtCtrl(IEC101_DEF *pt,u8 *pdat) // ps从传送原因开始 { u8 cot,ykcot,ykvalue,yktype,ykThz,v; WORD ykno; WORD yksave; u8 AppByte=0; u8 CotByte=0; int i,ret; bool bValidYk=false; u8 *ps=&pdat[2]; u8 ti=pdat[0]; //类型标示 u8 yk_cmd; yktype = 0; if(tRunPara.b101App2Byte) //应用单元双字节地址 { AppByte=1; } if(tRunPara.b101Cot2Byte) //双字节传送原因 { CotByte=1; } ykcot=(ps[0]&0x01f); ykvalue=ps[4+AppByte+CotByte]; yk_cmd = ykvalue&0x03; ykno=ps[2+AppByte+CotByte]+(WORD)ps[3+AppByte+CotByte]*256; yksave=ykno; cot=IEC_COT_UN_OA | IEC_COT_PN; v=ykvalue; if(ti==45) { ykvalue+=1; //单点遥控转为双点遥控 } for(i=0;ido_num;i++) //查找遥控转发表,看是否有有效的遥控点 { if(ykno != g_do_table[i].cp) // 遥控点号对应 { continue; } // 如果配置了级联通道,转发 if(g_do_table[i].link_ch) { if(lnk_master_yk(&g_do_table[i] ,ti,ps[0],v) == 0) { pt->lnk_yk_send_flag |= (1<us0_lnk_yk = ustimer_get_origin(); } else { cot = (ykcot == IEC_COT_ACT) ? IEC_COT_ACTCON : IEC_COT_DEACTCON; cot |= IEC_COT_PN; IEC101_Echo_YK(pt,yksave,ti,cot,v); } if(ykvalue&0x80) { yktype=YK_TYPE_SEL; } else { yktype=YK_TYPE_EXE; } refresh_co_file(true,ti,yktype,ykno,cot,yk_cmd); return; } // 本装置遥控 { cot=IEC_COT_ACTCON; bValidYk=true; if(ykvalue&0x80) { yktype=YK_TYPE_SEL; } else { yktype=YK_TYPE_EXE; } if(ykcot==8) //遥控撤销 { yktype=YK_TYPE_CANCEL; cot=IEC_COT_DEACTCON; } if((ykvalue&0x03)==2) { ykThz=YK_VAL_HZ; } else { ykThz=YK_VAL_TZ; } ret = RemoteCtrl(yktype,i,ykThz,MASTER_101+pt->chnl); if(ret != 0) { cot = IEC_COT_ACTCON | IEC_COT_PN; // 否定应答 } //接收到主站的遥控信息:选择,执行,撤销 refresh_co_file(true,ti,yktype,ykno,cot,yk_cmd); break; } } // 没有找到对应遥控点 if(i == g_table_head->do_num) { char buf[128]; char log_info[128]; sprintf(buf,"遥控:转发点表未找到对应点(ykno=0x%04x,yktype=%d,ykvalue=%d)",ykno,yktype,ykvalue); log_str_time(LOG_OPERATE,buf,0,0); rt_printf_time("%s\r\n",buf); sprintf(log_info,"IEC101(%d):Remote control:Corresponding di is not in the iectable(ykno=0x%04x,yktype=%d,ykvalue=%d)",pt->chnl+1,ykno,yktype,ykvalue); load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1); log_flag.com_err[pt->chnl] = true; } pt->bRmtSM2 = false; IEC101_Echo_YK(pt,yksave,ti,cot,v); //必须有对应点号 if(i != g_table_head->do_num) { //终端发出遥控信息:选择确认,执行确认 refresh_co_file(false,ti,yktype,ykno,cot,yk_cmd); } if((yktype==YK_TYPE_EXE)&&(cot==IEC_COT_ACTCON)) { IEC101_Echo_YK(pt,yksave,ti,10,v); refresh_co_file(false,ti,yktype,ykno,IEC_COT_ACTTERM,yk_cmd); } } /************************************************************************** 函数名称:IEC101_MFrameCall 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:应答短帧报文 输入参数:平衡方式下,发送帧 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_MFrameCall(IEC101_DEF *pt,unsigned char ZoneByte) { u8 *pd=pt->tx_buf_m; IEC101_SETPRM(ZoneByte); if(!pt->bDIR)IEC101_SETDIR(ZoneByte); if(!tRunPara.b101Addr2Byte) { pd[0]=5; pd[1]=0x10; pd[2]=ZoneByte; pd[3]=(u8)tRunPara.byAddr; pd[4]=pd[2]+pd[3]; pd[5]=0x16; } else { pd[0]=6; pd[1]=0x10; pd[2]=ZoneByte; pd[3]=(u8)tRunPara.byAddr; pd[4]=(u8)(tRunPara.byAddr>>8); pd[5]=pd[2]+pd[3]+pd[4]; pd[6]=0x16; } } static void IEC101_CallGprsInf(IEC101_DEF *pt) { u8 *pd=pt->tx_buf_s; pd[0]=9; pd[1]=0x55; pd[2]=0xAA; pd[3]=0x55; pd[4]=0xAA; pd[5]=0x1A; pd[6]=0x00; pd[7]=0x00; pd[8]=0x9A; pd[9]=0x7C; } /************************************************************************** 函数名称:IEC101_link_par 函数版本:1.00 作者: 创建日期:2019.4.19 函数功能说明:101规约遥参级联处理 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_link_par(IEC101_DEF *pt) { int i; LINKPAR_DEF *par; LINK_PARDATA *yd; if(pt->lnk_par_send_flag == 0) { return; } for(i=0; ilnk_par_send_flag & (1<flag[IECLINK_YK_ACK]) { yd = &par->data[IECLINK_YK_ACK]; IEC101_Echo_PAR(pt, par->cp_m, yd->ti, yd->cot,&yd->buf[0], yd->len,yd->vsq); par->flag[IECLINK_YK_ACK] = 0; rt_printf("参数应答帧\r\n"); } // 遥控结束帧 if(par->flag[IECLINK_YK_END]) { yd = &par->data[IECLINK_YK_END]; IEC101_Echo_PAR(pt, par->cp_m, yd->ti, yd->cot,&yd->buf[0], yd->len,yd->vsq); par->flag[IECLINK_YK_END] = 0; pt->lnk_par_send_flag &= ~(1<us0_lnk_par) > tRunPara.dSM2Time*USTIMER_SEC) { yd = &par->data[IECLINK_YK_SND]; rt_printf("参数遥控超时复位:cp_m=%04x,cp_s=%04x,ti=%d,v=%d.\r\n",par->cp_m,par->cp_s,yd->ti,yd->len); g_link_par[i].flag[IECLINK_YK_SND] = false; pt->lnk_par_send_flag &= ~(1<lnk_yk_send_flag == 0) { return; } for(i=0; ilnk_yk_send_flag & (1<flag[IECLINK_YK_ACK]) { yd = &yk->data[IECLINK_YK_ACK]; IEC101_Echo_YK(pt, yk->cp_m, yd->ti, yd->cot, yd->v); yk->flag[IECLINK_YK_ACK] = 0; } // 遥控结束帧 if(yk->flag[IECLINK_YK_END]) { yd = &yk->data[IECLINK_YK_END]; IEC101_Echo_YK(pt, yk->cp_m, yd->ti, yd->cot, yd->v); yk->flag[IECLINK_YK_END] = 0; pt->lnk_yk_send_flag &= ~(1<us0_lnk_yk) > tRunPara.dSM2Time*USTIMER_SEC) { yd = &yk->data[IECLINK_YK_SND]; rt_printf("级联遥控超时复位:cp_m=%04x,cp_s=%04x,ti=%d,v=%d.\r\n",yk->cp_m,yk->cp_s,yd->ti,yd->v); pt->lnk_yk_send_flag &= ~(1<v&0x80) { yktype=YK_TYPE_SEL; } else { yktype=YK_TYPE_EXE; } if(yd->cot==IEC_COT_DEACT) //遥控撤销 { yktype=YK_TYPE_CANCEL; } yk_cmd = yd->v&0x03; refresh_co_file(false,yd->ti,yktype,yk->cp_m,yd->cot,yk_cmd); } } } } #ifdef YPARA_LINK void IEC101_lnk_msg_ypara(IEC101_DEF *pt101) { u8 *pd; int num=0; u8 pdi=0,cot=0,ti=0,vsq=0; int index=0,i=0; msg_data_struct *msg_data = &pt101->msg_ypara_recv; pd = g_arrIECBuf; #if 0 /*遥参是否超时*/ if(!pt101->msg_ypara_send.flag) return; /*没有下发遥参不返回*/ if(ustimer_get_duration(pt101->us0_msg_ypara) > tRunPara.dSM2Time*USTIMER_SEC) { pt101->msg_ypara_send.flag = 0; /*超时*/ return; } /*遥参是否超时*/ #endif if(!msg_data->flag) return; /*有返回消息*/ msg_data->flag = 0; if(msg_data->type == modify_set) { msg_ypara_data_struct *ypara_msg = &pt101->msg_ypara_recv.ypara_msg; ti = 203; /*该协议各类遥参返回格式一致,统一接口即可*/ switch(ypara_msg->cmd) { case YP_SEL_RETURN: pdi = 0x80; cot = IEC_COT_ACTCON; break; case YP_SEL_FAULT: pdi = 0x80; cot = IEC_COT_ACTCON|IEC_COT_PN; break; case YP_EXE_RETURN: pdi = 0x00; cot = IEC_COT_ACTCON; break; case YP_EXE_FAULT: pdi = 0x00; cot = IEC_COT_ACTCON|IEC_COT_PN; break; case YP_CANCEL_RETURN: pdi = 0x40; cot = IEC_COT_DEACTCON; break; case YP_CANCEL_FAULT: pdi = 0x40; cot = IEC_COT_DEACTCON|IEC_COT_PN; break; default: break; } pd[num++]=(u8)m_runsection; //当前定值区 pd[num++]=(u8)(m_runsection>>8); //当前定值区 pd[num++] = pdi; /*特征符*/ //print_msg_base(ypara_msg); for(i=0;inum;i++) { index = get_index_link(msg_data->sport,ypara_msg->base_data[i].cp_s); if(index < 0) continue; pd[num++]=(u8)tParaID[index].parId; pd[num++]=(u8)(tParaID[index].parId>>8); pd[num++]=ypara_msg->base_data[i].val_iec.tag; pd[num++]=ypara_msg->base_data[i].val_iec.size; if(ypara_msg->base_data[i].val_iec.size >0) { memcpy(&pd[num],ypara_msg->base_data[i].val_iec.value,ypara_msg->base_data[i].val_iec.size); num += ypara_msg->base_data[i].val_iec.size; } vsq++; } IEC101_Asdu_Add(pt101,pd,num,vsq,cot,ti,FRAME_I); } } void IEC101_Rmt_msg_ypara_write(IEC101_DEF *pt,BYTE *pdat,BYTE vsq,u8 link_cp) { WORD section=0; BYTE *ps=0; //从点号开始 int i,j=0; u16 di = 0; msg_data_struct *msg_data; msg_ypara_data_struct *msg; iec_val_struct v; BYTE pdi; //参数特征标识符 msg_data = &pt->msg_ypara_send; msg = &pt->msg_ypara_send.ypara_msg; memset(msg,0,sizeof(msg_ypara_data_struct)); pt->msg_ypara_send.dport = link_cp; pt->msg_ypara_send.sport = pt->chnl; pt->msg_ypara_send.flag = 1; section=pdat[0]+(pdat[1]<<8); // 定值区号 pdi = pdat[2]; msg->section_p = section; //定值区 if((pdi&0xc0)==0) // 定值固化 { msg->cmd = YP_EXE; } else if((pdi&0xc0)==0x40) /*撤销*/ { msg->cmd = YP_CANCEL; } else // 参数预置 { msg->cmd = YP_SEL; ps=&pdat[3]; for(i=0;ibase_data[msg->num].cp_m = tParaID[j].parId; msg->base_data[msg->num].cp_s = tParaID[j].link_cp; msg->base_data[msg->num].val_iec = v; msg->num++; break; } } } msg_data->type = modify_set; msg_data->sport = pt->chnl; msg_data->dport = link_cp; mananger_msg_ypara_s(msg_data,msg_data->sport,msg_data->dport); pt->us0_msg_ypara = ustimer_get_origin(); } #endif int IEC101_TransFile(IEC101_DEF *pt,unsigned char *buf,int len); int IEC101_Maintain(IEC101_DEF *pt,unsigned char *buf,int len); #ifdef RCD_STRAN_S static void IEC101_Rcd_Ack(IEC101_DEF *pt101) //发送新录波文件名 { u8 *pd; int num; u8 rcd_num=0; char tmp[128]={0}; char note[100]; //组织报文 pd=g_arrIECBuf; num=0; if(get_queue_count(&pt101->rcd_qt.queue) >0) /*有新录波*/ { check_data_queque(&pt101->rcd_qt.queue,tmp); sprintf(note, "%s----------准备\r\n",tmp); log_str_time(LOG_OPERATE,note,0,1); pd[num++]=0; //信息体地址 pd[num++]=0; pd[num++]=1; /*文件数量*/ pd[num++]=strlen(tmp); /*文件名长度*/ memcpy(&pd[num],tmp,strlen(tmp)); num += strlen(tmp); } else { pd[num++]=0; //信息体地址 pd[num++]=0; // pd[num++]=rcd_num; } IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,7,RCD_TYPE,FRAME_I|FRAME_I_PRIOR_LOW);// } #endif void IEC101_Applay(IEC101_DEF *pt) { u8 *pd; u8 AddByte=0; u8 CotByte=0; //传送原因双字节 u8 AppByte=0; //应用单元地址双字节 u8 ti,vsq,cot,L,*info_addr,info_len; bool bLFrame=false; //短帧标志 #ifdef FUNC_ENCRY_IN_ONE_SERIAL if(tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) { if((pt->chnl != 1) && (pt->chnl != 3) && (pt->chnl != 4)) //投入海南加密时,固定串口2、4、5用于加密数据处理,不允许接收串口数据 { // 接收报文 IEC101_Recv_App(pt); if(pt->chnl == 0) { return; } } } else #endif { // 接收报文 IEC101_Recv_App(pt); } #ifdef IEC_NOLINK_NO_CALLYX if(g_link_comm.link_flag == false) return; #endif // 主动SOE处理,放在报文处理之前,使时间同步报文之前的SOE报文在时间报文应答前发送 IEC101_SendEvent(pt,tRunPara.bAutoCos); // 处理级联遥控 IEC101_lnk_yk(pt); // 处理级联遥参 IEC101_link_par(pt); #ifdef YPARA_LINK IEC101_lnk_msg_ypara(pt); #endif // 处理接收的加密报文 // 先处理加密报文,再处理普通报文,保证同一报文一个循环处理完 if(pt->bSM2Data) { if(pRunSet->bTT_SM2) //加密功能投入 { #ifdef ENCRYPT_SM2 IEC101_SM2(pt); #endif } pt->bSM2Data=false; } // 处理接收的普通报文 else if(pt->bData) { // 如果从动站buf有数据,说明至少有2帧报文还没有发送完, // 在gprs网络,同时收到多帧报文后连续应答处理时会出现这种情况。 // 为了保证过程的完整性,待数据发送完后再处理接收的报文。 if(pt->btx_buf_s) { return; } // 报文长度调整变量赋值 if(tRunPara.b101Addr2Byte) AddByte=1; if(tRunPara.b101Cot2Byte) CotByte=1; if(tRunPara.b101App2Byte) AppByte=1; // 打印接收的报文 if(g_print_101 && (g_print_port & (1<chnl))) { if(pt->recvbuf[0]==0x10) { rt_printf("RX_S101_%d:", pt->chnl); print_msg("",pt->recvbuf,(5+AddByte)); } else { rt_printf("RX_L101_%d:", pt->chnl); print_msg("",pt->recvbuf,pt->recvbuf[1]+6); } } // 由于101在本函数开始处接收数据,所以可以在此处复位接收标志 pt->bData=false; // 检查帧标志并跳过固定报文头 if(pt->recvbuf[0]==0x10) { pd=&pt->recvbuf[1];//短帧 L = 2; } else if(pt->recvbuf[0]==0x68) { pd=&pt->recvbuf[4];//长帧 L = pt->recvbuf[1]; bLFrame=true; } else { return; } // 检查是否是GPRS维护信息 if(pd[0]==0x05) { memcpy(tGprsInf.data,&pd[1],64); tGprsInf.bInf=true; return; } // 检查地址 if(tRunPara.b101Addr2Byte) //双字节地址 { WORD addr; addr=pd[1]+(pd[2]<<8); if(addr!=tRunPara.byAddr && addr!=0xffff) return; //不是发给本装置的数据 } else { if(pd[1]!=(u8)tRunPara.byAddr && pd[1]!=0xff) return; //不是发给本装置的数据 } // 通道空闲时间复位 pt->dValidTime=0; // 私有101文件传输协议 if ((((pd[0]&0x0f) == 3) || ((pd[0]&0x0f) == 4)) && (pd[2+AddByte] ==137)) { IEC101_TransFile(pt,&pd[2+AddByte],pt->recvbuf[1]-(2+AddByte)); pt->btx_buf_s = true; return; } // 私有维护工具软件接口 if ((((pd[0]&0x0f) == 3) || ((pd[0]&0x0f) == 4)) && (pd[2+AddByte] ==138)) { IEC101_Maintain(pt,&pd[2+AddByte],pt->recvbuf[1]-(2+AddByte)); pt->btx_buf_s = true; return; } //主站主动发送数据PRM=1 master if(pd[0]&0x40) { if(pd[0]&0x80) { pt->bDIR=true; } else { pt->bDIR=false; } // 非重复报文 if(!pRunSet->bTT_101FCB ||(((pd[0]&0x10)&&pt->FCBSave!=(pd[0]&0x20)) // FCV位有效,FCB位变位 || (!(pd[0]&0x10)) // FCV位无效 || pt->bFirstFCV) ) // 该标志有效,不判断FCB位 { //FCB位 if(pd[0]&0x10) { // 如果不是第一个FCB位且和以前FCB不同,确认发送BUF if((pt->bFirstFCV == 0) && (pt->FCBSave != (pd[0]&0x20))) { IEC101_AckOneBuf(pt); } pt->FCBSave=pd[0]&0x20; // 保存帧记数位 pt->bFirstFCV = 0; // 如果FCV位有效,标志清0 } switch((pd[0]&0x0f)) { // 只有非平衡101才会请求一二级数据 case 10: //请求/响应(一级数据) case 11: //请求/响应(二级数据) // 如果不是数据状态,不应收到2、3、4、10、11,不进行任何应答,让主站超时复位 if(pt->st101!=IEC101_ST_DATA) { return; } if(!IEC101_SendOneBuf(pt)) { if(pt->tfile.bSegtransing) // 文件传输,放入一级数据缓冲区 { IEC101_SegmentContinue(pt); } else if(pt->tf.bTransing) { iec_readfile_send(pt,false); } if(!IEC101_SendOneBuf(pt)) { pt->bycsend = IEC101_GetYc(pt); if(!pt->bycsend) // add by sunxi { IEC101_FrameEcho(pt,IEC101_ECHO_NO_DATA); } } } break; case 9: // 请求链路状态 IEC101_FrameEcho(pt,IEC101_ECHO_LINK_OK); break; case 3: //请求/确认 case 4: //请求/无应答 // 如果不是数据状态,不应收到2、3、4、10、11,不进行任何应答,让主站超时复位 // 如果是复位进程状态,不应答主站发送应用层报文 if(pt->st101!=IEC101_ST_DATA || pt->bResetProcess) { return; } ti = pd[2+AddByte]; vsq = pd[3+AddByte]; cot = pd[4+AddByte]; info_addr = pt->recvbuf + IEC101_InfoAddr(); info_len = L + 4 - IEC101_InfoAddr(); switch(ti) //类型标识判断 { case 250: // 级联报文数据上送 IEC101_LinkInf(pt); break; case 106: if(cot == 6) //延时获得 { // 此处不修改报文,真正发送的时候再计算修改 IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_ACTCON,ti,FRAME_I| FRAME_I_106); } else if(cot == 3) //延时传递 { // 保存主站下发的传输延时 pt->ms_delay = info_addr[2] + (info_addr[3]<<8); } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 103://0xFF: //set time ,BroadCast Command // 检查COT if(cot == IEC_COT_REQ||cot == IEC_COT_ACT) { IEC101_TimeSetEcho(pt,&pd[8+AddByte+AppByte+CotByte],cot);// Addbyte 链路地址和应用单元地址多了2个字节,传送原因多1个字节 } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } pt->bTimeSyn= true; IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 100: //总查询 // 检查COT if(cot != IEC_COT_ACT) { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } else { IEC101_CallAll(pt,pd[8+AddByte+AppByte+CotByte]);// Addbyte 链路地址和应用单元地址多了2个字节,传送原因多1个字节 lnk_call_all(); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 101: //电度总召 // 检查COT if(cot != IEC_COT_ACT) { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } else { IEC101_DDAll(pt,pd[8+AddByte+AppByte+CotByte]);// Addbyte 链路地址和应用单元地址多了2个字节,传送原因多1个字节 } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 104: //测试帧 IEC101_TestFrame(pt,&pd[8+AddByte+AppByte+CotByte]); IEC101_FrameEcho(pt,IEC101_ECHO_SURE); break; case 105: // 检查COT if(cot != IEC_COT_ACT) { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } else { u8 qrp; qrp = pd[8+AddByte+AppByte+CotByte]; #ifdef FUNC_RESET_EQU gb_ResetEqu = true; #endif switch(qrp)//复位进程命令限定词 QRP { //进程的总复位 case 1: rt_printf_time("IEC101(%d) Reset: 进程的总复位!\r\n",pt->chnl); IEC101_ResetTxBuf(pt); // 记录复位计时起点和设置复位进程标志 pt->us0_ResetProcess = ustimer_get_origin(); pt->bResetProcess = true; break; //复位事件缓冲区等待处理的带时标信息 case 2: rt_printf_time("IEC101(%d) Reset: 复位事件缓冲区!\r\n",pt->chnl); g_soe_queue.tail_send[pt->chnl].n = g_soe_queue.head.n; g_soe_queue.tail_ack[pt->chnl].n = g_soe_queue.tail_send[pt->chnl].n; break; } IEC101_ResetProcess(pt,qrp); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); break; case 45: //单点遥控 case 46: //双点遥控 // 检查单双点是否和参数设置相符 if((ti == 45 && tRunPara.YKtype == 1) || (ti == 46 && tRunPara.YKtype == 0) ) { // 对不支持的类型标识,否定应答 IEC101_FrameEcho(pt,IEC101_ECHO_DENY); rt_printf("101遥控类型不符(ti=%d,YKtype=%d)\r\n",ti,tRunPara.YKtype); break; } // 检查COT if(cot != IEC_COT_ACT && cot != IEC_COT_DEACT) { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I|FRAME_I_PRIOR_HIGH); } else { IEC101_RmtCtrl(pt, &pd[2+AddByte]); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 122: //召目录,选择文件,召唤文件,召唤节 IEC101_File_Echo(pt,&pd[2+AddByte]); //响应文件召唤 IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 124: //确认文件,确认节的应答 IEC101_File_Sure(pt,&pd[2+AddByte]); //对文件操作确认 IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 176: // 本装置复归 ResetHzLed(0); SignalReset(0,false); IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; #ifdef RCD_STRAN_S case RCD_TYPE: // 本装置复归 IEC101_Rcd_Ack(pt); IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; #endif case 200: // 切换定值区 if(cot == IEC_COT_ACT) { IEC101_SectionWrite(pt,&pd[8+AddByte+AppByte+CotByte]);// Addbyte 链路地址和应用单元地址多了2个字节,传送原因多1个字节 } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 201: // 读取当前定值区号 if(cot == IEC_COT_ACT) { IEC101_SectionRead(pt);// Addbyte 链路地址和应用单元地址多了2个字节,传送原因多1个字节 } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; break; case FOS_PAR_READ_ONE://南网佛山局 读取单个参数 case FOS_PAR_READ_MUL://南网 读取多个参数 case FOS_PAR_SET_ONE: //南网佛山局 设置单个参数 case FOS_PAR_SET_MUL://南网 设置多数参数 // 检查COT if(cot != IEC_COT_ACT && cot != IEC_COT_DEACT) { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } else { IEC101_ParCtrl(pt, &pd[2+AddByte]); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 202: // 读参数 if(cot == IEC_COT_ACT) { int ret; #ifdef YPARA_LINK ret=iec_setread_mananger((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,false); #else ret=iec_setread((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,false);//vsq为需读参数的个数,0,代表读全部参数,参数为从定值区开始的数据 #endif if(ret!=0) // 读定值出错,否定应答 { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_OA,ti,FRAME_I); } } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 203: // 修改参数 if(cot == IEC_COT_ACT||cot==IEC_COT_DEACT) { int ret; #ifdef YPARA_LINK int link_ch; link_ch = get_ypara_linkch_write((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,false); if(link_ch == 0) { rt_printf("101------写本地定值\r\n"); ret=iec_setwrite((void*)pt,&pd[6+AddByte+AppByte+CotByte],cot,vsq,false);//vsq为需读参数的个数,0,代表固化参数,参数为从定值区开始的数据 if(ret!=0) // 写定值出错,否定应答 { BYTE rcot=IEC_COT_ACTCON|IEC_COT_PN; if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON|IEC_COT_PN; IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); } else { BYTE rcot=IEC_COT_ACTCON; if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON; IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); } } else { rt_printf("101------写级联定值\r\n"); IEC101_Rmt_msg_ypara_write((void*)pt,&pd[6+AddByte+AppByte+CotByte],vsq,link_ch); if((link_ch&0x1f) == 0x1f) /*广播写直接返回确认*/ { BYTE rcot= IEC_COT_ACTCON; if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON; IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); } } #else ret=iec_setwrite((void*)pt,&pd[6+AddByte+AppByte+CotByte],cot,vsq,false);//vsq为需读参数的个数,0,代表固化参数,参数为从定值区开始的数据 if(ret!=0) // 写定值出错,否定应答 { BYTE rcot=IEC_COT_ACTCON|IEC_COT_PN; if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON|IEC_COT_PN; IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); } else { BYTE rcot=IEC_COT_ACTCON; if(cot==IEC_COT_DEACT)rcot=IEC_COT_DEACTCON; IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); } #endif } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 break; case 210: // 文件传输 { pt->tf.vsq=vsq; if(cot == IEC_COT_ACT||cot == IEC_COT_REQ) { int ret; ret=iec_file_app((void*)pt,&pt->recvbuf[12+AddByte+AppByte+CotByte],cot,pt->recvbuf[1]-(AddByte+CotByte+AppByte)-9,false);// 从操作标识开始 if(ret!=0) // 文件处理错误 { BYTE rcot=IEC_COT_UN_OA; if(ret==-2)rcot=IEC_COT_UN_COT; IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); } } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 } break; case 211: // 文件升级 { if(cot == IEC_COT_ACT||cot == IEC_COT_DEACT) { int ret; ret=iec_pro_update((void*)pt,cot,pd[8+AddByte+CotByte+AppByte],false);// 从操作标识开始 if(ret!=0) // 文件处理错误 { BYTE rcot=IEC_COT_UN_OA; IEC101_Asdu_Add(pt,info_addr,info_len,vsq,rcot,ti,FRAME_I); } } else { IEC101_Asdu_Add(pt,info_addr,info_len,vsq,IEC_COT_UN_COT|IEC_COT_PN,ti,FRAME_I); } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); //链路应答确认 } break; default: IEC101_FrameEcho(pt,IEC101_ECHO_DENY); break; } break; case 0: //复位通信单元 // 复位通道 IEC101_Reset(pt); if(pd[0]&0x80) // add by sunxi 防止IEC101_Reset调用后,bDIR变为默认值,后续发送报文,标示出错 { pt->bDIR=true; } else { pt->bDIR=false; } g_comm_link_status |= (1<chnl); IEC101_State(pt,tRunPara.tUartPara[pt->chnl].b101PH ? IEC101_ST_LINK : IEC101_ST_DATA); pt->LinkCount++; // 应答报文 IEC101_InitOK(pt); IEC101_FrameEcho(pt,IEC101_ECHO_SURE); // "重新连接后,总召前,发送完所有老的SOE,不发送COS" IEC101_SendEvent(pt,0); break; case 2: //链路测试功能 // 如果不是数据状态,不应收到2、3、4、10、11,不进行任何应答,让主站超时复位 if(pt->st101!=IEC101_ST_DATA) { return; } // 南瑞主站,功能码是2,发送长帧测试帧 if(bLFrame) { ti = pd[2+AddByte]; if(ti==104) { IEC101_TestFrame(pt,&pd[8+AddByte+AppByte+CotByte]); } } IEC101_FrameEcho(pt,IEC101_ECHO_SURE); break; default: IEC101_FrameEcho(pt,IEC101_ECHO_DENY); break; } //类型判断结束 // 广播地址不用应答 if( (pd[1]!=0xff) || (tRunPara.b101Addr2Byte && (pd[2]!=0xff)) // 平衡101广播查询链路状态回本机地址,江西吉安博威主站要求 || (tRunPara.tUartPara[pt->chnl].b101PH && (pd[0]&0x0f) == 9)) { pt->btx_buf_s = true; } } // FCV位有效,FCB没有变位,重发保存的确认帧。 else { //重发数据 if(pt->st101 == IEC101_ST_DATA) { if(pt->btx_buf_s) { rt_printf_time("IEC101_Applay:,btx_buf_s==1.\r\n"); } pt->btx_buf_s = true; } } } //平衡101 ,接收到主站回复的数据 else { if(!tRunPara.tUartPara[pt->chnl].b101PH) return; switch(pt->st101) { case IEC101_ST_LINK: { if((pd[0]&0x0f)==11) //应答链路状态 { IEC101_State(pt,IEC101_ST_RSTLINK); pt->bMSend=false; } break; } case IEC101_ST_RSTLINK: { if((pd[0]&0x0f)==0) //应答链路状态 { IEC101_State(pt,IEC101_ST_DATA); pt->bMSend=false; } break; } case IEC101_ST_DATA: { if((pd[0]&0x0f)==0) // 确认应答,调整一级数据缓冲区 { IEC101_AckOneBuf(pt); pt->bMSend=false; } break; } } } } // 获取GPRS信息 else if(pt->bCallGprsInf) { if(g_tRsComm[pt->chnl].bextsend|| pt->btx_buf_s|| pt->btx_buf_m) return; //正在发送数据,不处理 pt->bCallGprsInf=false; IEC101_CallGprsInf(pt); pt->btx_buf_s = true; } //无有效数据接收,平衡式101处理自动发送数据 else { if(!tRunPara.tUartPara[pt->chnl].b101PH)return; //如果启动了主站发送,必须先处理应答 if(pt->bMSend) { bool bRtn=true; // 20秒后,未收到应答报文 if(ustimer_get_duration(pt->us0_msend) > pRunSet->dT101Resend*USTIMER_SEC) { pt->us0_msend=ustimer_get_origin(); if(++pt->msend_retries>2) //重发三次后,重新初始化 { //rt_printf_time("IEC101(%d):启动帧重发错误!\r\n",pt->chnl); char log_info[128]; sprintf(log_info,"IEC101(%d):Restart the frame to make three errors",pt->chnl+1); load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1); log_flag.com_err[pt->chnl] = true; IEC101_Info_Printf_One(pt); IEC101_Reset(pt); } else { //需重新组织发送 pt->btx_buf_m = true; } return; } #ifdef COMM_STATION_NR if(pt->tx_buf[0].tb_head!=pt->tx_buf[0].tb_tail_send) //有遥控报文要发送 { if(ustimer_get_duration(pt->us0_msend) > 2*USTIMER_SEC) //且上帧报文发送超过2秒,不等待应道报文,直接发送 { IEC101_AckOneBuf(pt); pt->bMSend=false; bRtn=false; } } if(pt->bTestFrameL||pt->bSoeResume||pt->bTimeSyn)//总召前有SOE续传不需要主站确认 { pt->bTestFrameL = false; pt->bSoeResume = false; pt->bTimeSyn =false; IEC101_AckOneBuf(pt); pt->bMSend=false; bRtn=false; } #endif if(bRtn) { return; // 发送,未收到数据,返回 } } // 平衡101应答完成 // 先复位应答相关变量 pt->msend_retries=0; pt->us0_msend=ustimer_get_origin(); // 开始主动发送流程 switch(pt->st101) { case IEC101_ST_LINK: { IEC101_MFrameCall(pt,IEC101_CALL_LINK); pt->bMFCB=false; pt->bMSend=true; break; } case IEC101_ST_RSTLINK: { IEC101_MFrameCall(pt,IEC101_CALL_RSTLINK); pt->bMFCB=false; pt->bMSend=true; break; } case IEC101_ST_DATA: { pt->bMSend=IEC101_SendOneBuf(pt); if(!pt->bMSend) // 无数据 判断 { if(pt->tfile.bSegtransing) // 文件传输,放入一级数据缓冲区 { IEC101_SegmentContinue(pt); } else if(pt->tf.bTransing) // 国网新标准文件传输 { iec_readfile_send(pt,false); } else { pt->bycsend=IEC101_GetYc(pt); if (pt->bycsend)pt->bMSend = TRUE; } } break; } } //有数据要发送 if(pt->bMSend) { if(pt->btx_buf_m) { //rt_printf_time("IEC101_Applay:bMSend==1,btx_buf_m==1.\r\n"); } pt->bMFCB=!pt->bMFCB; pt->btx_buf_m = true; } } } /************************************************************************** 函数名称:IEC101_SendEvent 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:上送事件记录及变位信息,在RS485定时处理任务(IEC101_AutoTimer)中调用 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ EVENT_STRUCT event_p[64]; u32 IEC101_SendEvent(IEC101_DEF *pt,int is_send_cos) { u8 type; int vsq=0, num_vsp; u8 arrCos[200],arrSoe[200]; u8 arrCos1[200],arrSoe1[200]; u16 head,cnt; u16 soe_no_s,soe_no_d,arg_index[64]; bool bFault; FAULT_PARA_BUF *tf=&pt->tfault; // 主动上送条件: // 1、是IEC101_ST_DATA状态 // 2、没有在处理加密遥控报文 // 3、不是复位进程状态 if((pt->st101 != IEC101_ST_DATA) || pt->bRmtSM2 || pt->bResetProcess) { return 0; } // 不允许通讯续传的情况下,总召后才允许上送事件 if(pt->bSendChange == 0)// && pRunSet->bTT_SoeResumeComm == 0) { return 0; } #ifdef FUN_SOE_DELAY_TIME if((pt->bfirst_call) && (dTCounter - pt->DTSendChange <= 15*T_1s)) { return 0; } pt->bfirst_call = false; #endif //一级缓冲区未空,不添加事件记录 if(pt->tx_buf[0].tb_head != pt->tx_buf[0].tb_tail_send) { //rt_printf("%s(0):tb_head=%d,tb_tail_send=%d\r\n",__func__,pt->tx_buf[0].tb_head,pt->tx_buf[0].tb_tail_send); return 0; } if(pt->tx_buf[1].tb_head != pt->tx_buf[1].tb_tail_send) { return 0; } #ifndef JX_YB_SEND_SOE #ifndef IEC_JXYB_DEAL if(g_run_stu.bjx) //检修压板投入,不发送变化的事件记录 { g_soe_queue.tail_send[pt->chnl].n = g_soe_queue.tail_eep.n; //soe cos tf->head = tf->tail;//故障参数 return 0; } #endif #endif //发送级联来的故障参数 if(tRunPara.bEvPara) { if(tf->head!=tf->tail) { u8 index=tf->tail; IEC101_Asdu_Add_sts(pt,&tf->data[index][1],tf->data[index][0],0,3,42,FRAME_I_PRIOR_LOW ,0); tf->tail++; return 0; } } // 等SOE保存后再发送,避免通讯确认没有保存的事件 head = g_soe_queue.tail_eep.n; // 一次调用,将事件池中所有事件发送完, // 已保证满足"重新连接后,总召前,发送完所有老的SOE,不发送COS"的需求。 cnt = 0; while(1) { u8 yxtype=0; u8 pcosnum=0,pcosnum1=0; // 检查发送队列空间,空间应大于1/4,防止发送缓冲区满 if(IEC101_BufFreeNum(&pt->tx_buf[0]) < (IEC_TBUF_NUM>>2)) { rt_printf("IEC101_SendEvent:BufFreeNum=%d.\r\n",IEC101_BufFreeNum(&pt->tx_buf[0])); break; } // 获取一帧报文的事件 bFault=false; num_vsp = asdu_get_event(pt->chnl,head,arrCos,arrSoe,arrCos1,arrSoe1,&soe_no_s,&soe_no_d,0,&bFault,&event_p[0],&arg_index[0],&yxtype,&pcosnum,&pcosnum1); if(num_vsp==0) break; //rt_printf("%s:vsq_s=%d\r\n",__func__,vsq); if(pcosnum != 0) { // 发送COS if(is_send_cos&&pt->bSendChange) { type = 1; IEC101_Asdu_Add(pt,arrCos,pcosnum*3,pcosnum,3,type,FRAME_I_COS); } } vsq=(u8)(num_vsp&0xff); if(vsq != 0) { cnt += vsq; // 发送SOE type = 30; IEC101_Asdu_Add_sts(pt,arrSoe,vsq*10,vsq,3,type,FRAME_I_SOE ,soe_no_s); } if(pcosnum1 != 0) { // 发送COS if(is_send_cos) { type = 3; IEC101_Asdu_Add(pt,arrCos1,pcosnum1*3,pcosnum1,3,type,FRAME_I_COS); } } vsq=(u8)((num_vsp>>8)&0xff); //rt_printf("%s:vsq_d=%d\r\n",__func__,vsq); if(vsq != 0) { cnt += vsq; // 发送SOE type = 31 ; IEC101_Asdu_Add_sts(pt,arrSoe1,vsq*10,vsq,3,type,FRAME_I_SOE ,soe_no_d); // rt_printf_time("IEC101_SendEvent:soe_no=%d.\r\n",soe_no); } vsq=(u8)((num_vsp>>16)&0xff); //rt_printf("%s:vsq_e=%d\r\n",__func__,vsq); if(vsq>0 && tRunPara.bEvPara) // 上送故障信息 { int i; u16 no; u8 SIQ,week; struct rtc_time_t rt; for(i=0;i:=无效 } #endif timespec_to_rtc(event_p[i].ts,&rt,1); week = WEEK_DAY(event_p[i].ts.tv_sec); //if(pRunSet->bTT_faultparaDKY) //{ yc_num=asdu_get_event_yc(arrSoe,0,arg_index[i]); // 101规约是6个字节 按照规约规定,101 及104 遥信信息体地址都是两个字节 //} //else //{ // vsq=asdu_get_event_yc(arrSoe,1,arg_index); // 101规约是6个字节 按照规约规定,101 及104 遥信信息体地址都是两个字节 //} *pd++=1; //遥信个数 *pd++ =( yxtype==1) ? 31 : 30; //类型标识 *pd++=(u8)no; // 信息体地址L *pd++=(u8)(no>>8); // 信息体地址H *pd++=SIQ; // 遥信 *pd++=(u8)rt.ms; *pd++=(u8)(rt.ms>>8); *pd++=(u8)rt.min; *pd++=(u8)rt.hour; *pd++=((week<<5) | rt.day); *pd++=(u8)rt.month; *pd++=(u8)rt.year; *pd++=yc_num; //遥 测个数 *pd++ =13; //类型标识 浮点值 //if(pRunSet->bTT_faultparaDKY) //{ memcpy(pd,arrSoe,yc_num*6); // 标准规定,遥测点号是2个字节 IEC101_Asdu_Add_sts(pt,g_arrIECBuf,yc_num*6+14,0,3,42,FRAME_I_PRIOR_LOW ,0); //} //else //{ // memcpy(pd,arrSoe,vsq*7); // 标准规定,遥测点号是3个字节 // IEC101_Asdu_Add_sts(pt,g_arrIECBuf,vsq*7+14,0,3,42,FRAME_I_PRIOR_LOW ,0); //} } } //pt->bSoeRelink=false; } return cnt; } /************************************************************************** 函数名称:IEC101_Recv_Rst 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:复归数据的接受 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_Recv_Rst(IEC101_DEF *pt) { if(pt->nTypeCounter) { rt_printf_time("IEC101_Recv_Rst:state=%d,counter=%d\r\n",pt->nTypeCounter,pt->nRecvCounter); } pt->nTypeCounter=0; pt->nRecvLenth=0; pt->nRecvCounter=0; } /************************************************************************** 函数名称:IEC101_AutoTimer 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:101规约链路内容定时处理部分,100毫秒调用1次 输入参数: 输出参数: 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_AutoTimer(IEC101_DEF *pt) { // 如果有数据就发送 IEC101_Send_Polling(pt); // 配电终端在发送复位进程确认命令后需等待3秒,然后重启进程 if(pt->bResetProcess && ustimer_get_duration(pt->us0_ResetProcess) > 3*USTIMER_SEC) { IEC101_Reset(pt); #ifdef IEC_RESET_PROCESS rt_printf("装置重启\r\n"); watchdog_reset_cpu(2); //装置复位 #endif } // 如果连接没有建立,不处理下面流程 if(pt->st101 == IEC101_ST_NONE) { return; } // 处理加密遥控报文标志超时返回 if(pRunSet->bTT_SM2) { if(ustimer_get_duration(pt->us0_SM2Over)>60*USTIMER_SEC) { pt->bRmtSM2 = false; } } // 1秒处理任务 if(ustimer_get_duration(pt->us0_sec) > USTIMER_SEC) { pt->us0_sec += USTIMER_SEC; pt->secs++; // 通道检查 默认10分钟没有收到数据,认为通道错误 if(pt->dValidTime++>pRunSet->dT101Check) { char log_info[128]; rt_printf_time("IEC101(%d):通道超时\r\n",pt->chnl); sprintf(log_info,"IEC101(%d):Channel timeout",pt->chnl+1); load_hs_log_rcd(TYPE_COMM_ERR,true,NULL,log_info,1); log_flag.com_err[pt->chnl] = true; IEC101_Info_Printf_One(pt); IEC101_Reset(pt); } } if(pt->tf.bTransing||pt->tf.bdatTraned) // 文件启动了传输,在3次重发间隔内,未有文件内容传输更新,释放已申请的文件 { if(ustimer_get_duration(pt->tf.us0_file_trans)>(pRunSet->dT101Resend*USTIMER_SEC*3)) { #ifdef RCD_STRAN_S log_str_time(LOG_OPERATE,"读文件传输超时!!!!!!!",0,1); #endif iec_freefile(&pt->tf); pt->tf.bTransing=false; pt->tf.bdatTraned=false; } } if(pt->tf.bdatwriting) // 文件启动了传输,在3次重发间隔内,未有文件内容传输更新,释放已申请的写文件内存 { if(ustimer_get_duration(pt->tf.us0_file_write)>(pRunSet->dT101Resend*USTIMER_SEC*3)) { if(!pt->tf.writebuf) { rt_free(&pt->tf.writebuf); pt->tf.writebuf=NULL; } pt->tf.bdatwriting=false; } } if(pt->tf.bUpdateReset) // 文件启动了传输,在3次重发间隔内,未有文件内容传输更新,释放已申请的写文件内存 { if(ustimer_get_duration(pt->tf.us0_updatereset)>(USTIMER_SEC*2)) { rt_printf("101远程升级,装置复位\r\n"); pt->tf.bUpdateReset=false; watchdog_reset_cpu(2); //装置复位 } } if(pt->tr.bpreset) // 参数预置 { if(ustimer_get_duration(pt->tr.us0_preset)>(tRunPara.dSM2Time*USTIMER_SEC)) { int i; rt_printf("101预置参数超时清除\r\n"); pt->tr.bpreset=false; for(i=0;itr.di[i]=0; } } } } static bool IEC101_RmtCheck(IEC101_DEF *pt) //判断是否是遥控报文 { u8 AddByte=0; if(!pRunSet->bTT_SM2)return false; //加密功能未投入 if(tRunPara.b101Addr2Byte) AddByte=1; if((pt->recvbuf[0]==0x68) &&(pt->recvbuf[4]&0x40) &&((pt->recvbuf[6+AddByte]==45)||(pt->recvbuf[6+AddByte]==46))) // 遥控报文 { return true; } return false; } /************************************************************************** 函数名称:IEC101_Recv 函数版本:1.00 作者: 创建日期:2008.9.1 函数功能说明:101规约数据接受判断,只判断是否满足报文格式 输入参数: 输出参数:有完整帧接受,返回1,否则返回 0 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static int IEC101_Recv(IEC101_DEF *pt,u8 byRevData) { #ifdef FUNC_ENCRY_IN_ONE_SERIAL IEC101_DEF *pt1; static int iCount=0,enc_tunnel=0; #endif // 帧内字符间隔超时检测 if(g_tRsComm[pt->chnl].b_recv_reset) { // 不能复位fifo rt_fifo_reset(&pt->recv_fifo); //IEC101_Recv_Rst(pt); g_tRsComm[pt->chnl].b_recv_reset = 0; } pt->recvbuf[pt->nRecvCounter++]=byRevData; pt->dTRecvPiece=dTCounter; //接收字符时的时刻,在大循环中判断,是否需要复归 switch(pt->nTypeCounter) { case 0: //统计接收帧数 s_stat_rx_frame(pt->chnl); if(byRevData==0x68) { pt->nTypeCounter=1; } else if(byRevData==0x10) { if(!tRunPara.b101Addr2Byte) { pt->nRecvLenth=2; } else { pt->nRecvLenth=3; } pt->nTypeCounter=6; pt->nRecvCnt=0; pt->sum=0; } else if(byRevData==0x16) //SM2 报文 { pt->nTypeCounter=20; pt->nRecvCounter=256; pt->recvbuf[pt->nRecvCounter++]=byRevData; pt->recvbuf[0]=0x68; //恢复被0x16 覆盖的第一字节数据 if(!pRunSet->bTT_SM2) //加密功能未投入 { IEC101_Recv_Rst(pt); //统计接收错误帧数 s_stat_rx_errframe(pt->chnl); } } #ifdef FUNC_ENCRY_IN_ONE_SERIAL else if(byRevData==0xAA)//海南101规约报文 { pt->nTypeCounter=26; pt->sum=0; } #endif else { rt_printf_time("IEC101_Recv(%d):0x%02x.\r\n",pt->chnl,byRevData); IEC101_Recv_Rst(pt); //统计接收错误帧数 s_stat_rx_errframe(pt->chnl); } break; case 1: pt->nTypeCounter=2; pt->nRecvLenth=byRevData; //长帧长度 if(pt->nRecvLenth>IEC101_RECVBUF_MAX) { IEC101_Recv_Rst(pt); //统计接收错误帧数 s_stat_rx_errframe(pt->chnl); } break; case 2: if(pt->nRecvLenth==byRevData) { pt->nTypeCounter=3; } else { IEC101_Recv_Rst(pt); //统计接收错误帧数 s_stat_rx_errframe(pt->chnl); } break; case 3: if(byRevData==0x68) { pt->nTypeCounter=6; pt->nRecvCnt=0; pt->sum=0; } else { IEC101_Recv_Rst(pt); //统计接收错误帧数 s_stat_rx_errframe(pt->chnl); } break; //case 4: //控制域 // pt->sum=byRevData; // pt->nTypeCounter=5; // break; //case 5: //目标地址 // pt->sum+=byRevData; // pt->nRecvCnt=0; // pt->nTypeCounter=6; // if(pt->nRecvLenth==2)//短帧 // pt->nTypeCounter=7; // break; case 6: //有效数据 //有效数据 if(pt->recvbuf[4]==0x05&&pt->recvbuf[0]==0x68) // 映翰通 GPRS信息返回报文,同101不同,需特殊处理 { pt->nRecvLenth=0x30+1; } if(++pt->nRecvCnt>=pt->nRecvLenth) { pt->nTypeCounter=7; } pt->sum+=byRevData; break; case 7: //校验和 pt->recvsum=byRevData; pt->nTypeCounter=8; break; case 8: //结束符 pt->nTypeCounter = 0; IEC101_Recv_Rst(pt); if(byRevData==0x16) //校验结束字符,报文接收完毕, { if((pt->sum != pt->recvsum)&&(pt->recvbuf[4]!=0x05)) // 映翰通 GPRS信息返回报文,同101不同,需特殊处理 { s_stat_rx_errframe(pt->chnl);//统计接收帧错误计数 } else if(IEC101_RmtCheck(pt)) //是遥控报文 { pt->bData=false; //应用层暂不处理,等解密完成后处理 if(pRunSet->bTT_SM2) { pt->bRmtSM2 = true; pt->us0_SM2Over=ustimer_get_origin(); } } else { pt->bData=true; if (uart_test_begin) { uart_test_flag[pt->chnl][1] = 1; } // 唤醒主循环 mainloop_wakeup(); return 1; } } break; case 20: // SM2报文 L2长度 pt->SM2L2=byRevData; pt->nTypeCounter++; break; case 21: // SM2报文 L2长度 pt->nTypeCounter++; if(byRevData>=pt->SM2L2) //L2长度包含L3长度,L3chnl); IEC101_Recv_Rst(pt); } break; case 22: if(byRevData==0x16) { pt->nTypeCounter++; pt->nRecvCnt=0; } else { //统计接收错误帧数 s_stat_rx_errframe(pt->chnl); IEC101_Recv_Rst(pt); } break; case 23: if(++pt->nRecvCnt>=pt->SM2L2) { pt->nTypeCounter++; } break; case 24: //校验和 pt->nTypeCounter++; break; case 25: //结束符 pt->nTypeCounter = 0; IEC101_Recv_Rst(pt); if(byRevData==0x16) //校验结束字符,报文接收完毕, { pt->bSM2Data=true; mainloop_wakeup(); return 1; } else { s_stat_rx_errframe(pt->chnl); } break; #ifdef FUNC_ENCRY_IN_ONE_SERIAL case 26: //帧长低字节 pt->nTypeCounter++; pt->nRecvLenth = byRevData; pt->sum += byRevData; break; case 27: //帧长高字节 pt->nTypeCounter++; pt->nRecvLenth = (byRevData<<8) | pt->nRecvLenth; pt->sum += byRevData; break; case 28: //加密隧道编号 pt->nTypeCounter++; pt->enc_tunnel = byRevData; if(pt->enc_tunnel>1) { enc_tunnel = pt->enc_tunnel + 1; } else { enc_tunnel = pt->enc_tunnel; } pt->sum += byRevData; pt->nRecvCnt=0; break; case 29: //数据 if(++pt->nRecvCnt>=pt->nRecvLenth) { pt->nTypeCounter++; } pt->sum += byRevData; if(tRunPara.tUartPara[0].wProtocol == PROTOCOL_WED_ENC) { pt1 = (IEC101_DEF *)g_tRsComm[enc_tunnel].ptBuf; pt1->recvbuf[iCount] = byRevData; pt1->enc_tunnel = pt->enc_tunnel; pt1->nRecvLenth = pt->nRecvLenth; iCount++; } break; case 30: //校验和 pt->recvsum = byRevData; pt->nTypeCounter++; break; case 31: //帧尾 pt->nTypeCounter = 0; IEC101_Recv_Rst(pt); iCount = 0; if(byRevData==0x55) //校验结束字符,报文接收完毕, { if(pt->sum != pt->recvsum) { s_stat_rx_errframe(pt->chnl);//统计接收帧错误计数 } else { pt1 = (IEC101_DEF *)g_tRsComm[enc_tunnel].ptBuf; pt1->bData=true; enc_tunnel = 0; // 唤醒主循环 mainloop_wakeup(); return 1; } } break; #endif default: //统计接收错误帧数 s_stat_rx_errframe(pt->chnl); IEC101_Recv_Rst(pt); break; } //类型校验结束 return 0; } void IEC101_Recv_App(IEC101_DEF *pt) { u8 c; // 接收数据 while(pt->bSM2Data== false && pt->bData == false) { if(rt_fifo_get(&pt->recv_fifo,&c,1) == 0) { if(g_tRsComm[pt->chnl].b_recv_reset) { IEC101_Recv_Rst(pt); g_tRsComm[pt->chnl].b_recv_reset = 0; } break; } if(pRunSet->bTT_ESAM&&(!pt->bMaintainComm)) { int ret; ret = sec_recv(&pt->rx_buf,c,pt->extsendbuf,true); if(ret==SEC_FRAME_OK_APP) // 带规约的报文 { pt->bData = 1; } else if(ret==SEC_FRAME_OK_EXT) //纯安全扩展报文 发送 { IEC101_ext_send(pt,pt->extsendbuf+2,((pt->extsendbuf[0]<<8)+pt->extsendbuf[1])); } } else { #ifdef FUNC_ENCRY_IN_ONE_SERIAL int ret; ret = IEC101_Recv(pt,c); if(ret == 1) break; #else IEC101_Recv(pt,c); #endif } } } void IEC101_Recv_Int(IEC101_DEF *pt,u8 byRevData) { //记录每个不定长帧报文的接收时刻 if(!pRunSet->bTT_ESAM||pt->bMaintainComm) { if(byRevData == 0x68) { struct rt_fifo *f; f = &pt->recv_fifo; // 判断不定帧长报头 0x68 L L 0X68 if( (f->buffer[(f->in-1)&(f->size - 1)]) == (f->buffer[(f->in-2)&(f->size - 1)]) && (f->buffer[(f->in-3)&(f->size - 1)]) == 0x68) { pt->us0_frame = ustimer_get_origin(); } } } rt_fifo_put(&pt->recv_fifo,&byRevData,1); } int IEC101_PH_GprsInfo(void) { int i; for(i=0;ibCallGprsInf=true; } } return 0; } void IEC101_Info_Printf_One(IEC101_DEF *p) { iec_tbuf_t *pt ; rt_printf("\r\n 通道属性:type=%d,chnl=%d\r\n",p->type,p->chnl); if(p->st101 == 0) { return; } rt_printf(" 规约状态:state=%d,bSendChange=%d\r\n",p->st101,p->bSendChange); rt_printf(" 连接计时:LinkCount(%d):%d秒\r\n",p->LinkCount,p->secs); pt= &p->tx_buf[0]; rt_printf("发送BUF[0]:head=%d,tail_send=%d,tail_ack=%d\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack); pt= &p->tx_buf[1]; rt_printf("发送BUF[1]:head=%d,tail_send=%d,tail_ack=%d\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack); rt_printf("SOE通讯BUF:head=%d,tail_send=%d,tail_ack=%d\r\n",g_soe_queue.head.n,g_soe_queue.tail_send[p->chnl].n,g_soe_queue.tail_ack[p->chnl].n); rt_printf("SOE事件BUF:head=%d,tail_send=%d\r\n", g_soe_queue.head.n,g_soe_queue.tail_eep.n); pt= &p->tx_buf[2]; rt_printf("发送BUF[1]:head=%d,tail_send=%d,tail_ack=%d\r\n",pt->tb_head,pt->tb_tail_send,pt->tb_tail_ack); rt_printf("SOE通讯BUF:head=%d,tail_send=%d,tail_ack=%d\r\n",g_soe_queue.head.n,g_soe_queue.tail_send[p->chnl].n,g_soe_queue.tail_ack[p->chnl].n); rt_printf("SOE事件BUF:head=%d,tail_send=%d\r\n", g_soe_queue.head.n,g_soe_queue.tail_eep.n); return; } int IEC101_Info_Printf(void) { int i; IEC101_DEF *p; //串口101 rt_printf("\r\n串口101:"); for(i=0;isetion_msg; section=ps[0]+(ps[1]<<8); // 定值区号 msg_data->sport = pt101->chnl; msg_data->dport = 0x1F; //进行广播 msg_data->type = settion_swht; setion_msg->section_p = section; mananger_msg_ypara_s(msg_data,msg_data->sport,msg_data->dport); #endif } void IEC101_SectionWrite(IEC101_DEF *pt101,u8 *ps) //确认帧 { u8 *pd; int num; u8 cot; #if 0 // TODO 定值区处理 EWen WORD section; if(pRunSet->wEquTypeManager) IEC101_setion_chg_msg(pt101,ps); //组包 section=ps[0]+(ps[1]<<8); // 定值区号 cot=0x47; if(section>8); //当前定值区 pd[num++]=0; //最小定值区 pd[num++]=0; //最小定值区 pd[num++]=SEC_NUMBER-1; //最大定值区 1 标准上是7个字节,暂时认为是2个字节 pd[num++]=0; //最大定值区 2 cot =IEC_COT_ACTCON; IEC101_Asdu_Add(pt101,g_arrIECBuf,num,1,cot,201,FRAME_I);// } /**************************************************远方修改定值结束*****************************************************/ /***********************************************下面是101规约文件传输部分代码2016-8-10**************************************************/ /************************************************************************** 函数名称:IEC101_Direct_Echo 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:101规约目录响应 输出参数:pt101缓冲区地址,COT 传送原因 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_Direct_Echo(IEC101_DEF *pt101,DWORD InfoAddr) { BYTE *pd=g_arrIECBuf; //从第7个字节开始 BYTE vsq; int filenum; struct dir_file_struct *pdir,*pdirfree; int i; vsq=0; pdir=hf_get_dir_file(IEC_Getdir(InfoAddr),&filenum); // 根据信息体地址,判断读读哪个目录,获取目录信息 if(pdir==NULL) { IEC101_Asdu_Add(pt101,g_arrIECBuf,0,vsq,5,126, FRAME_I); //无文件应答 return; } pdirfree=pdir; *pd++ =(BYTE)InfoAddr; *pd++ =(BYTE)(InfoAddr>>8); for(i=0;ifile_name); *pd++ = (BYTE)(pdir->file_name>>8); /*文件长度*/ *pd++ = (BYTE)(pdir->file_size); *pd++ = (BYTE)(pdir->file_size>>8); *pd++ = (BYTE)(pdir->file_size>>16); /*文件的状态SOF*/ *pd++ = 0; /*日历时钟*/ timespec_to_rtc(pdir->file_time,&rtc,1); *pd++ = (BYTE)(rtc.ms); *pd++ = (BYTE)(rtc.ms>>8); *pd++ = (BYTE)(rtc.min); *pd++ = (BYTE)(rtc.hour); *pd++ = (BYTE)(rtc.day); *pd++ = (BYTE)(rtc.month); *pd++ = (BYTE)(rtc.year); vsq++; if((vsq>=15)&&i<(pdir->file_size-1))// 目录超出一帧上送范围,分帧,VSQ置后续标准位 { vsq|=0x80; IEC101_Asdu_Add(pt101,g_arrIECBuf,(vsq&0x7f)*13+2,vsq,5,126, FRAME_I); //无文件应答 vsq=0; pd=g_arrIECBuf; } pdir++; } rt_free(pdirfree); if(vsq>0) { if(vsq>1) { vsq|=0x80; } IEC101_Asdu_Add(pt101,g_arrIECBuf,(vsq&0x7f)*13,vsq,5,126, FRAME_I); //无文件应答 } else { IEC101_Asdu_Add(pt101,g_arrIECBuf,0,vsq,5,126, FRAME_I); //无文件应答 } } /************************************************************************** 函数名称:IEC101_EchoFileReady 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:应答文件准备好 输出参数:pt101缓冲区地址,filename 文件名 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_EchoFileReady(IEC101_DEF *pt101,DWORD InfoAddr,WORD filename) { BYTE *pd=g_arrIECBuf; //从第7个字节开始 int filelenth,sectionnum; filelenth=hf_get_file_inf(IEC_Getdir(InfoAddr),filename,§ionnum); /*文件名称*/ *pd++ =(BYTE)InfoAddr; *pd++ =(BYTE)(InfoAddr>>8); *pd++ = (BYTE)(filename); // 10 *pd++ = (BYTE)(filename>>8); // 11 *pd++=(BYTE)filelenth; // 文件长度 *pd++=(BYTE)(filelenth>>8); //文件长度 *pd++=(BYTE)(filelenth>>16); //文件长度 if(filelenth>0) { *pd++ = 0; /*FRQ=0肯定认可*/ } else { *pd++ = 0x80; /*FRQ=0否定定认可*/ } pt101->tfile.filelenth=filelenth; pt101->tfile.setctionnum=sectionnum; pt101->tfile.filename=filename; pt101->tfile.InfoAddr=InfoAddr; pt101->tfile.filechecksum=0; IEC101_Asdu_Add(pt101,g_arrIECBuf,8,1,13,120, FRAME_I); //无文件应答 } /************************************************************************** 函数名称:IEC101_EchoSectionReady 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:应答节准备好 输出参数:pt101缓冲区地址,filename 文件名 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_EchoSectionReady(IEC101_DEF *pt101,DWORD InfoAddr,WORD filename,BYTE section) { BYTE *pd=g_arrIECBuf; //从第7个字节开始 int setctionlenth; char *pdat=NULL; IEC101_FreeFile(pt101); *pd++ =(BYTE)InfoAddr; *pd++ =(BYTE)(InfoAddr>>8); /*文件名称*/ *pd++ = (BYTE)(filename); // 10 *pd++ = (BYTE)(filename>>8); // 11 if(pt101->tfile.InfoAddr==InfoAddr&&pt101->tfile.filename==filename&§ion<=pt101->tfile.setctionnum) //文件名相同文件有效 { pdat=hf_get_file_part(IEC_Getdir(InfoAddr), filename, section, &setctionlenth); } *pd++=section; //节名 /*节长度长度*/ *pd++ = (BYTE)(setctionlenth); //12 *pd++ = (BYTE)(setctionlenth>>8); // 13 *pd++ = (BYTE)(setctionlenth>>16); // 14 if(pdat==NULL||setctionlenth==0) { *pd++ = 0x80; /*FRQ=0否定定认可*/ } else { *pd++ = 0x0; /*FRQ=0 肯定认可*/ // 15 pt101->tfile.pdat=pdat; //保存节内容地址,后续需要释放 pt101->tfile.sectionlenth=setctionlenth; pt101->tfile.transsection=section; pt101->tfile.bFiletransing=true; pt101->tfile.bSegtransing=false; pt101->tfile.us0_file_trans=ustimer_get_origin(); pt101->tfile.transBytes=0; pt101->tfile.sectionchecksum=0; } IEC101_Asdu_Add(pt101,g_arrIECBuf,9,1,13,121, FRAME_I); //无文件应答 } /************************************************************************** 函数名称:IEC101_SegmentSend 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:应答节传输中段的数据 输出参数:pt101缓冲区地址,filename 文件名,section 节名 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_SegmentSend(IEC101_DEF *pt101,DWORD InfoAddr,WORD filename,BYTE section) { BYTE *pd=g_arrIECBuf; //从第7个字节开始 BYTE sectionchecksum=0; //节校验和 BYTE segmentbytes; BYTE cot; int i; cot=13; segmentbytes=0; if(pt101->tfile.bFiletransing &&pt101->tfile.InfoAddr==InfoAddr &&pt101->tfile.filename==filename &§ion==pt101->tfile.transsection) //文件名相同 节相同 { if(pt101->tfile.transBytes+FILE_SEGMENT_BYTES>=pt101->tfile.sectionlenth) { segmentbytes=pt101->tfile.sectionlenth%FILE_SEGMENT_BYTES; // 最后的段的字节数 } else { segmentbytes=FILE_SEGMENT_BYTES; } pt101->tfile.sectionchecksum=sectionchecksum; pt101->tfile.us0_file_trans=ustimer_get_origin(); pt101->tfile.bSegtransing=true; } else { cot|=0x40; segmentbytes=0; IEC101_FreeFile(pt101); } /*文件名称*/ *pd++ =(BYTE)InfoAddr; *pd++ =(BYTE)(InfoAddr>>8); *pd++ = (BYTE)(filename); // 10 *pd++ = (BYTE)(filename>>8); // 11 *pd++=section; //节名 12 *pd++=segmentbytes; //段的长度 13 for(i=0;itfile.pdat[pt101->tfile.transBytes++]; sectionchecksum+=*pd++; } IEC101_Asdu_Add(pt101,g_arrIECBuf,6+segmentbytes,1,cot,125, FRAME_I); //无文件应答 } /************************************************************************** 函数名称:IEC101_EchoLastSection 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:应答最后的节或段 输出参数:pt101缓冲区地址,filename 文件名,section 节名,LSQ限定词 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ static void IEC101_EchoLastSection(IEC101_DEF *pt101,WORD filename,BYTE section,BYTE LSQ) { BYTE *pd=g_arrIECBuf; //从第7个字节开始 /*文件名称*/ *pd++ =(BYTE)pt101->tfile.InfoAddr; *pd++ =(BYTE)(pt101->tfile.InfoAddr>>8); *pd++ = (BYTE)(filename); // 10 *pd++ = (BYTE)(filename>>8); // 11 *pd++=section; //节名 12 switch(LSQ) { case 1: *pd++=1; //LSQ=1:不带停止激活文件传输13 *pd++=pt101->tfile.filechecksum; //文件校验和 14 break; case 3: *pd++=3; //LSQ=3:不带停止激活的节传输 *pd++=pt101->tfile.sectionchecksum; //节校验和 break; } IEC101_Asdu_Add(pt101,g_arrIECBuf,7,1,13,123, FRAME_I); //无文件应答 } /************************************************************************** 函数名称:IEC101_SegmentContinue 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:主循环中继续发送节中剩余的段 输出参数:pt101缓冲区地址,filename 文件名,section 节名 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_SegmentContinue(IEC101_DEF *pt101) { BYTE *pd=g_arrIECBuf; //从第7个字节开始 BYTE sectionchecksum=0; //节校验和 BYTE segmentbytes; int i; if(!pt101->tfile.bSegtransing)return; if(pt101->tfile.transBytestfile.sectionlenth) // 段还未传输完毕 { if(pt101->tfile.transBytes+FILE_SEGMENT_BYTES>=pt101->tfile.sectionlenth) { segmentbytes=pt101->tfile.sectionlenth%FILE_SEGMENT_BYTES; // 最后的段的字节数 } else { segmentbytes=FILE_SEGMENT_BYTES; } *pd++ =(BYTE)pt101->tfile.InfoAddr; *pd++ =(BYTE)(pt101->tfile.InfoAddr>>8); /*文件名称*/ *pd++ = (BYTE)(pt101->tfile.filename); // 10 *pd++ = (BYTE)(pt101->tfile.filename>>8); // 11 *pd++=pt101->tfile.transsection; //节名 12 *pd++=segmentbytes; //段的长度 13 for(i=0;itfile.pdat[pt101->tfile.transBytes++]; sectionchecksum+=*pd++; } pt101->tfile.sectionchecksum+=sectionchecksum; pt101->tfile.us0_file_trans=ustimer_get_origin(); IEC101_Asdu_Add(pt101,g_arrIECBuf,6+segmentbytes,1,13,125, FRAME_I); //无文件应答 } else { IEC101_EchoLastSection(pt101,pt101->tfile.filename,pt101->tfile.transsection,3); IEC101_FreeFile(pt101); } } void IEC101_File_Respone(IEC101_DEF *pt101,BYTE *dat,BYTE cot,BYTE SCAFQ) // { BYTE *pd=g_arrIECBuf; //从第7个字节开始 u8 vsq,type; u8 CotByte=0; //传送原因双字节 u8 AppByte=0; //应用单元地址双字节 if(tRunPara.b101Cot2Byte) CotByte=1; if(tRunPara.b101App2Byte) AppByte=1; type=dat[0]; vsq=dat[1]; *pd++=dat[4+CotByte+AppByte]; // 应用单元地址 *pd++=dat[5+CotByte+AppByte]; // *pd++=dat[6+CotByte+AppByte]; // 文件名 *pd++=dat[7+CotByte+AppByte]; // *pd++=dat[8+CotByte+AppByte]; //节名 12 *pd++=SCAFQ; //限定词 IEC101_Asdu_Add(pt101,g_arrIECBuf,6,vsq,cot,type, FRAME_I); //无文件应答 } /************************************************************************** 函数名称:IEC101_File_Echo 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:对文件召唤的应答处理 输出参数:pt101缓冲区地址,dat报文缓冲区地址从报文的帧类型开始 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_File_App(IEC101_DEF *pt101,BYTE *dat) //dat 从报文的帧类型开始 { BYTE COT,SCQ; //传送原因 WORD filename,section; //文件名 节名 DWORD InfoAddr; //信息体地址 u8 CotByte=0; //传送原因双字节 u8 AppByte=0; //应用单元地址双字节 if(tRunPara.b101Cot2Byte) CotByte=1; if(tRunPara.b101App2Byte) AppByte=1; COT=dat[2]; InfoAddr=dat[4+CotByte+AppByte]+(dat[5+CotByte+AppByte]<<8); filename=dat[6+CotByte+AppByte]+(dat[7+CotByte+AppByte]<<8); //文件名 section=dat[8+CotByte+AppByte]; //节名 SCQ = dat[9+CotByte+AppByte]; //选择和召唤限定词 //rt_printf("\r\n COT=%d addr=%4x filename=%d,section=%d,SCQ=%d",COT,InfoAddr,filename,section,SCQ); if(IEC_Getdir(InfoAddr)==NULL) { IEC101_File_Respone(pt101,dat,(47|0x40),SCQ); // 否定应答,非所希望的通信服务 return; } if(COT == 5) /*请求目录 */ { IEC101_Direct_Echo(pt101,InfoAddr); /*目录响应 cot =5代表召唤目录*/ } else if(COT == 13) { switch(SCQ) { case 1: /*选择文件*/ IEC101_EchoFileReady(pt101,InfoAddr,filename); break; case 2: /*请求文件*/ IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//节准备好报文 break; case 6: /*请求节*/ IEC101_SegmentSend(pt101,InfoAddr,filename,section); //发送本节中所有的段 break; default: IEC101_File_Respone(pt101,dat,COT,SCQ|0x30); // 否定应答,非所希望的通信服务 break; } } else { IEC101_File_Respone(pt101,dat,(45|0x40),SCQ); // 否定应答,未知的传送原因 } } /************************************************************************** 函数名称:IEC101_File_Echo 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:对文件召唤的应答处理 输出参数:pt101缓冲区地址,dat报文缓冲区地址从报文的帧类型开始 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_File_Echo(IEC101_DEF *pt101,BYTE *dat) //dat 从报文的帧类型开始 { BYTE COT,SCQ; //传送原因 WORD filename,section; //文件名 节名 DWORD InfoAddr; //信息体地址 u8 CotByte=0; //传送原因双字节 u8 AppByte=0; //应用单元地址双字节 if(tRunPara.b101Cot2Byte) CotByte=1; if(tRunPara.b101App2Byte) AppByte=1; COT=dat[2]; InfoAddr=dat[4+CotByte+AppByte]+(dat[5+CotByte+AppByte]<<8); filename=dat[6+CotByte+AppByte]+(dat[7+CotByte+AppByte]<<8); //文件名 section=dat[8+CotByte+AppByte]; //节名 SCQ = dat[9+CotByte+AppByte]; //选择和召唤限定词 //rt_printf("\r\n COT=%d addr=%4x filename=%d,section=%d,SCQ=%d",COT,InfoAddr,filename,section,SCQ); if(IEC_Getdir(InfoAddr)==NULL) { IEC101_File_Respone(pt101,dat,(47|0x40),SCQ); // 否定应答,非所希望的通信服务 return; } if(COT == 5) /*请求目录 */ { IEC101_Direct_Echo(pt101,InfoAddr); /*目录响应 cot =5代表召唤目录*/ } else if(COT == 13) { switch(SCQ) { case 1: /*选择文件*/ IEC101_EchoFileReady(pt101,InfoAddr,filename); break; case 2: /*请求文件*/ IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//节准备好报文 break; case 6: /*请求节*/ IEC101_SegmentSend(pt101,InfoAddr,filename,section); //发送本节中所有的段 break; default: IEC101_File_Respone(pt101,dat,COT,SCQ|0x30); // 否定应答,非所希望的通信服务 break; } } else { IEC101_File_Respone(pt101,dat,(45|0x40),SCQ); // 否定应答,未知的传送原因 } } /************************************************************************** 函数名称:IEC101_File_Sure 函数版本:1.00 作者: 创建日期:2007.9.1 函数功能说明:对文件处理的确认应答 输出参数:pt缓冲区地址,dat报文缓冲区地址从报文的帧类型开始 返回值: 更新信息: 更新日志1: 日期: 修改者: 修改内容: 修改原因: ***************************************************************************/ void IEC101_File_Sure(IEC101_DEF *pt101,BYTE *dat) //dat 从报文的帧类型开始 { BYTE COT,AFQ; //传送原因 WORD filename,section; //文件名 节名 DWORD InfoAddr; //信息体地址 u8 CotByte=0; //传送原因双字节 u8 AppByte=0; //应用单元地址双字节 if(tRunPara.b101Cot2Byte) CotByte=1; if(tRunPara.b101App2Byte) AppByte=1; COT=dat[2]; InfoAddr=dat[4+CotByte+AppByte]+(dat[5+CotByte+AppByte]<<8); filename=dat[6+CotByte+AppByte]+(dat[7+CotByte+AppByte]<<8); //文件名 section=dat[8+CotByte+AppByte]; //节名 AFQ = dat[9+CotByte+AppByte]; //选择和召唤限定词 if(IEC_Getdir(InfoAddr)==NULL) { IEC101_File_Respone(pt101,dat,(47|0x40),AFQ); // 否定应答,未知的信息体地址 return; } if(COT != 13) { IEC101_File_Respone(pt101,dat,(45|0x40),AFQ); // 否定应答,未知的传送原因 } switch(AFQ) /*AFQ文件确认或节确认*/ { case 1: //文件传输的正确认可 IEC101_FreeFile(pt101); IEC101_File_Respone(pt101,dat,COT,AFQ); // 需商讨,文件传输完毕后,对文件的确认,需要上送什么信息合适,以前是上送更新后的目录 sunxi break; case 2: //文件传输的否定认可 IEC101_FreeFile(pt101); IEC101_File_Respone(pt101,dat,COT,AFQ); // 需商讨,文件传输完毕后,对文件的确认,需要上送什么信息合适,以前是上送更新后的目录 sunxi break; case 3: //节传输的正确认可 IEC101_FreeFile(pt101); if(pt101->tfile.transsectiontfile.setctionnum-1)//现有调试工具UTDEBU,节名从0开始, tfile.setctionnum 需减1 sunxi ,后续程序恢复 { pt101->tfile.filechecksum+=pt101->tfile.sectionchecksum; //文件校验和 section+=1; IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//下一节准备好 } else if(section>=pt101->tfile.setctionnum-1) // //现有调试工具UTDEBU,节名从0开始, tfile.setctionnum 需减1 sunxi,后续程序恢复 { IEC101_EchoLastSection(pt101,filename,section,1); //LSQ = 3; 最后的段 } break; case 4: //节传输的否定认可 IEC101_FreeFile(pt101); IEC101_EchoSectionReady(pt101,InfoAddr,filename,section);//节准备好报文 break; default: break; } } void IEC101_FreeFile(IEC101_DEF *pt101) { if(pt101->tfile.bFiletransing) { pt101->tfile.bFiletransing=false; pt101->tfile.bSegtransing=false; if(!pt101->tfile.pdat) { rt_free(pt101->tfile.pdat); pt101->tfile.pdat=NULL; } rt_printf("\r\nIEC101文件节传输完毕,释放内存,文件:%d 节:%d\r\n",pt101->tfile.filename,pt101->tfile.transsection); } }