C++代码审阅–ice104协议从站(7)_104规约c++-程序员宅基地

技术标签: c++  C++实践  软件测试实践  

代码理解

IEC104NASlave.h窗口协议从站实现文件
已注释过内容不再注释,请参考
C++代码审阅–ice104协议从站(1)
C++代码审阅–ice104协议从站(2)
C++代码审阅–ice104协议从站(3)
C++代码审阅–ice104协议从站(4)
C++代码审阅–ice104协议从站(5)
C++代码审阅–ice104协议从站(6)
后面几篇内容只需了解iec104协议基础知识即可理解,请看以下链接
电力IEC104规约协议解读(含源码下载)

// IEC104Slave.cpp: implementation of the CIEC104Slave class.
//
//

#include "stdafx.h"
#include "IEC104NASlave.h"
#include "IEC104Slave.h"

#pragma comment (lib, "WS2_32.lib")
#define PORT 2404
#define CLIENT_NUM 10
#define MAX_NUM_WORD 1024
#define MAX_ASDU_LENGTH 249

#define NA_TRIP 1
#define NA_CLOSE 0

BOOL isYKACK = false;

BOOL isT1Start = false;
BOOL isT2Start = false;
BOOL isT3Start = false;

BOOL isRevI = false;

int  timer_T1 = 0;			// t1计数变量
int	 timer_T2 = 0;			// t2计数变量
int	 timer_T3 = 0;			// t3计数变量

int	 IMsgLen;				// 遥测帧ASDU的长度
BYTE IMsg[249];


#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

CIEC104Slave *iec104slave;
SOCKET currentSock;
SOCKET Server = NULL; 

BOOL IsListeThr = TRUE;
BOOL IsTimerHandler = TRUE;
BOOL isRxHandler = TRUE;
BOOL isDealIFrameHandler = TRUE;
BOOL IsStart = TRUE;
BOOL isDealNoIFrameResponseS = TRUE;




//定义结构体类型;
typedef struct ClientInfo
{
    
	SOCKET sock;
	SOCKADDR_IN clientAddr; // 定义地址族
}ClientInfomation;

HANDLE		hRevThread		  = NULL;
HANDLE      hTrThread		  = NULL;
HANDLE      hStartThread	  = NULL;
HANDLE		hDealIFrameThread = NULL;
HANDLE      hDealNOSResponse  = NULL;

//
// Construction/Destruction
//

CIEC104Slave::CIEC104Slave()
{
    
	iec104slave = this;
	this->Connected = false;
	
	SetParams();

	hRevThread			= 0;
	hTrThread			= 0;
	hStartThread		= 0;
//	hDealIFrameThread	= 0;

	RxCounter			= 0;
	TxCounter			= 0;
	LastAckRx			= 0;
	LastAckTx			= 0;
}

CIEC104Slave::~CIEC104Slave()
{
    


}

DWORD WINAPI CIEC104Slave::ListeThr(LPVOID lpParam)
{
      	
	while(IsListeThr)  // IsListeThr
	{
     
		SOCKET client;
		sockaddr_in from;
		ClientInfomation SendSocket[CLIENT_NUM];
		int currentClient=0; 
		int fromlen ;
		fromlen = sizeof(from);

		//	printf("  正在等待用户连接...\n"); 
		client = INVALID_SOCKET;
		while(INVALID_SOCKET == client && Server)	// 只要服务器处于启动状态就应该等	  && Server   && Server && iec104slave->Connected
		{
    
			client = accept(Server, (struct sockaddr *)&from, &fromlen); //如果没有用户连接一直执行这句
		}
		isT3Start = true;
#if 1
		if(!hTrThread)
		{
    
			hTrThread = CreateThread(0, 0, &TimerHandler, (LPVOID)&SendSocket[currentClient],0, NULL);
			if(hTrThread==0)
			{
    
				AfxMessageBox("Create hTrThread Thread Failed!");
				break;
			}
		}
#endif

		// 建立连接后, 发送序号和接收序号清零
		iec104slave->RxCounter = 0;
		iec104slave->LastAckTx = 0;
		
		timer_T3 = 0;                                                    // 启动T3定时器(连接建立)
		
		// 设置界面内容
		iec104slave->OnUpdateTime(3);  
		// 设置初始剩余时间		
		iec104slave->OnUpdateRemainTime(3, iec104slave->T3 - timer_T3);
		
		currentClient=(currentClient+1)%10;
		//	printf("%s connected \n",inet_ntoa(from.sin_addr));
		//record client socket;
		SendSocket[currentClient].sock = client;
		SendSocket[currentClient].clientAddr.sin_addr=from.sin_addr;
		
		client = INVALID_SOCKET;
#if 1	
		if(!hRevThread)
		{
    
			hRevThread = CreateThread(NULL, 0, RxHandler, (LPVOID)&SendSocket[currentClient], 0, NULL);
			if ( hRevThread == NULL )
			{
    
				//	cout<<"Create Thread Failed!"<<endl;
				AfxMessageBox("Create Thread Failed!");
				break;
			}
		}
#endif
		iec104slave->Connected = true;
		Sleep(75);
	} 
	if(hStartThread != NULL)
	{
    
		CloseHandle(hStartThread);
		hStartThread = NULL;
	}
	return 0; 
} 

DWORD WINAPI CIEC104Slave::DealNOIFrameResponseS(LPVOID lpParam)      // 未被确认的 I 帧最大数目 k
{
    
	/*
	while(isDealNoIFrameResponseS)
	{
		if((iec104slave->TxCounter - iec104slave->LastAckTx) >= (iec104slave->K)*2) 	 // 发送的需要与最后响应的序号比较差值大于K
		{													
			iec104slave->slave_stop();						 // 断开连接													
			iec104slave->slave_start();						 // 重新启动监听
			iec104slave->TxCounter = 0;
			iec104slave->LastAckTx = 0;
		}
	}
	*/
	if(hDealNOSResponse != NULL)
	{
    
		CloseHandle(hDealNOSResponse);
		hDealNOSResponse = NULL;
	}
	return 0;
}

// RX Thread  消息接收线程
DWORD WINAPI CIEC104Slave::RxHandler(LPVOID lpParam)
{
    
	CString str, ch;
	char buf[65535];
	int  res = 0;
	unsigned int  j = 0;
	unsigned char len = 0;
	
	ClientInfomation *CientSocket = (ClientInfomation*)lpParam;
	
	while(isRxHandler)
	{
    
		if(res != SOCKET_ERROR)
		{
    
			res = recv(CientSocket->sock, buf, sizeof(buf), 0);
			int n = WSAGetLastError();

			CString str, ch;
			for(int i = 0; i<res; i++)
			{
    
				ch.Format(" %02x ", buf[i]);
				str += ch;
			}
			str = "";
			ch = "";

			if(res <= 0 || res == WSAECONNRESET)
			{
    
				//	TRACE( "Connection Closed.\n");
				isT3Start = false;										// 关闭t3定时器(断开连接时)
				iec104slave->OnUpdateTime(3, TRUE);
				iec104slave->OnUpdateRemainTime(3, timer_T3, TRUE);
				iec104slave->OnUpdateTime(2, TRUE);
				iec104slave->OnUpdateRemainTime(2, timer_T2, TRUE);
				iec104slave->OnUpdateTime(1, TRUE);
				iec104slave->OnUpdateRemainTime(1, timer_T1, TRUE);
				isT2Start = FALSE;
				isT1Start = FALSE;
				timer_T1 = 0;
				timer_T2 = 0;
				timer_T3 = 0;

				// 如果是主站将连接关闭则要在这里将相应的server也关闭
				// closesocket(Server);
				// WSACleanup();
				iec104slave->TxCounter = 0;
				iec104slave->RxCounter = 0;

				// 更新界面
				AfxMessageBox("连接已断开");
				// 将当前的client销毁
				// 如果是我断开的那我不需要做任何事情
				// 如果是其他原因导致断开的我应该始终处于侦听状态
				// iec104slave->OnUpdateWnd();
				break;
			}

			/* 解析报文,根据接收的报文内容响应回复信息*/
			j   = 0;
			len = 0;
			while(j < (unsigned)res && res >= 6)						 // 68是启动字符  长度  控制域1-4
			{
    
				if(buf[j] == 0x68)
				{
    
					len = buf[j+1];
					// call callback
					iec104slave->OnRxMsg((BYTE*)&buf[j], len+2);

					APCI *p = (APCI*) &buf[j];
					currentSock = CientSocket->sock;

					int n = (p->field1 & 0x03);

					/* 以下分别处理U S I格式帧,函数封装*/
					timer_T3 = 0;											// 收到I/S/U格式帧T3复位 重新开始计数
					iec104slave->OnUpdateTime(3);
					iec104slave->OnUpdateRemainTime(3, iec104slave->T3-timer_T3);
					int formatType = p->field1&0x03;

					
					if(!hDealNOSResponse)
					{
    
						hDealNOSResponse = CreateThread(NULL, 0, DealNOIFrameResponseS, NULL, 0, NULL);
						if(hDealNOSResponse==0)
						{
    
							AfxMessageBox("Create hDealNOSResponse Thread Failed!");
							break;
						}
					}
					

					if(formatType == 0 || formatType == 2)
					{
    
																		
						int revSendNum = (p->field2<<8) | p->field1 ;       // 接收序号也就是对方的发送序号
						iec104slave->RxCounter = revSendNum;
						/*
						if(iec104slave->RxCounter == revSendNum)
							iec104slave->RxCounter += 2;				    // 如果收到的发送序号与接收序号相等,接收序号加1
						else if(iec104slave->RxCounter < revSendNum)
							;												// 有报文丢失
						else if(iec104slave->RxCounter > revSendNum)
							;												// 发送方发生了重传
						*/
																			// 公共地址    2字节
						for(int i = 0; i< NA_IEC_104_ASDUADDRESSBYTE; i++)  
							iec104slave->CommonAsduAddress[i] = buf[NA_STARTASDUADDRESS +i +j];

																			// 信息体地址  3字节
						for(int i=0; i<NA_IEC_104_INFORMATIONBYTE; i++)
							iec104slave->InformationObject[i] = buf[NA_STARTINFORMATION+i+j];

												
						isT2Start = false;									 // 关闭T2定时器

						iec104slave->OnUpdateTime(2, TRUE);
						iec104slave->OnUpdateRemainTime(2, timer_T2, TRUE);
																			 // 启个线程去处理I格式帧
						isRevI = true;
						IMsgLen = len+2-sizeof(APCI);
						memset(IMsg, 0, sizeof(IMsg));
						memcpy(IMsg, (BYTE*)&buf[j+sizeof(APCI)], IMsgLen);

						if(!hDealIFrameThread)
						{
    
							hDealIFrameThread = CreateThread(NULL, 0, DealIFrameHandler, (LPVOID)&buf, 0, NULL);
							if(hDealIFrameThread==0)
							{
    
								AfxMessageBox("Create hDealIFrameThread Thread Failed!");
								break;
							}
						}					

						iec104slave->Send_S_Msg();							 // RTU每接收到一帧I帧都会发送一帧S帧用于确认
					}
					else
					{
    
						if(formatType == 1)    /*  S  */ 
						{
    
							// 从第一次收到S帧开始启线程

					 		isT2Start = false;								// 关闭T2定时器
							iec104slave->OnUpdateTime(2, TRUE);
							iec104slave->OnUpdateRemainTime(2, timer_T2, TRUE);
							iec104slave->LastAckTx = (p->field4<<8) | p->field3;  // 获取接收序号,接收序号与发送序号比较
						}
						else
						{
    
							if(formatType == 3) /*  U  */
								iec104slave->ProcessFormatU(p);
						}

					}

					j = j + len + 1;										// 考虑到一次可能接收到多个报文(每个报文以68开头)
					_ASSERT(len < res);										//ERROR
				}
				j++;
			}
			Sleep(75);
		}
	}
	if(hRevThread != NULL)
	{
    
		CloseHandle(hRevThread);
		hRevThread = NULL;
	}
	return 0;
}


int CIEC104Slave::Send_U_Msg(int cmd)
{
    
	if(GetSockConnectStatus())
	{
    
		APCI header;
		header.start = 0x68;
		header.len=0x04;
		header.field1 = 0x03 | cmd;					// U-Format
		header.field2 = 0;
		header.field3 = 0;
		header.field4 = 0;
		
		if(send(currentSock, (const char*)&header, sizeof(header), 0) == SOCKET_ERROR)
		{
    
			printf(LastError, "send() failed with error: %d\n", WSAGetLastError());
			TRACE(LastError);
			slave_stop();
			return -1;
		}
		
		// call callback
	    iec104slave->OnTxMsg((BYTE*)&header,sizeof(header));
		iec104slave->timer_U_Testflag = false;		// reset TestFR time-out
		return 0;
	}	
	return -1;
}


int CIEC104Slave::slave_start()
{
    
	hRevThread		  = NULL;
	hTrThread		  = NULL;
	hStartThread	  = NULL;
	hDealIFrameThread = NULL;

	IsListeThr				= TRUE;
	IsTimerHandler			= TRUE;
	isRxHandler				= TRUE;
	isDealIFrameHandler		= TRUE;
	IsStart					= TRUE;
	isDealNoIFrameResponseS = TRUE;

	WSADATA WSAData; 
	int WSAreturn; 
	
	//初始化套接字
	WSAreturn = WSAStartup(MAKEWORD(2,2),&WSAData);
	if(WSAreturn) 
	{
     
		//	cout<<"Init Windows Socket Failed::"<<GetLastError()<<endl;
		AfxMessageBox("Init Windows Socket Failed");
		return 0; 
	}
	// 创建套接字

	Server = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); 
	if(Server ==INVALID_SOCKET) 
	{
     
		// cout<<"Create Socket Failed::"<<GetLastError()<<endl;
		AfxMessageBox("Create Socket Failed");
		return 0; 
	} 
	
	sockaddr_in local; 
	local.sin_family = AF_INET; 
	//local.sin_addr.s_addr = inet_addr(IP_ADDRESS);
	local.sin_addr.s_addr = INADDR_ANY;
	// 绑定
	local.sin_port = htons(PORT); 
	if(bind(Server,(sockaddr *)&local,sizeof(local)) ==SOCKET_ERROR) 
	{
     
		//	cout<<"Bind Socket Failed::"<<GetLastError()<<endl;
		AfxMessageBox("Bind Socket Failed");
		return 0; 
	} 
	
	if(listen(Server,CLIENT_NUM)==SOCKET_ERROR)                       // 侦听
	{
     
		//	cout<<"listen Socket Failed::"<<GetLastError()<<endl;
		AfxMessageBox("listen Socket Failed");
		return 0; 
	} 
	AfxMessageBox("服务端已经启动");

	hStartThread = CreateThread(NULL, 0, ListeThr, (LPVOID)this, 0, NULL);
	if (hStartThread == NULL )
	{
    
		return -1;
	}
	return 0;	
}

void CIEC104Slave::slave_stop()
{
    	
	int rev = closesocket(Server);
	if(rev != 0)
	{
    
		int x = WSAGetLastError();
		if(WSAGetLastError() == WSAENOTSOCK)
			AfxMessageBox("closesocket");
		int y = WSACleanup();
		return;
	}
	this->Connected = false;
	WSACleanup();

	Server = NULL;

	IsListeThr				= FALSE;
	IsTimerHandler			= FALSE;
	isRxHandler				= FALSE;
	isDealIFrameHandler		= FALSE;
	IsStart					= FALSE;
	isDealNoIFrameResponseS = FALSE;

	
	if(hRevThread != NULL)
	{
    
		CloseHandle(hRevThread);
		hRevThread = NULL;
	}

	if(hTrThread != NULL)
	{
    
		CloseHandle(hTrThread);
		hTrThread = NULL;
	}

	if(hDealIFrameThread != NULL)
	{
    
		CloseHandle(hDealIFrameThread);
		hDealIFrameThread = NULL;
	}

	if(hStartThread != NULL)
	{
    
		CloseHandle(hStartThread);
		hStartThread = NULL;
	}
	
	if(hDealNOSResponse != NULL)
	{
    
		CloseHandle(hDealNOSResponse);
		hDealNOSResponse = NULL;
	}	
}


BOOL CIEC104Slave::GetSockConnectStatus()
{
    
	return Connected;
}

/* 解析I格式报文*/
int CIEC104Slave::ProcessFormatI(BYTE *msgbuf, unsigned int len)
{
    
	// 应该将整个ASDU传输过来,而不应该只是一个ASDU头
	unsigned short i;
	AsduQualifier = msgbuf[NA_STARTQUALIFIER];

	ASDU_header *header=NULL;
	unsigned char num=0, cause=0, n=0;
	bool seq=false;
	int	 asdu=0;
	
	header = (ASDU_header*)msgbuf;
	
	num = header->qual & 0x7F;	           // number of objects.
	if(header->qual & 0x80)		           // sequence of objects ?
		seq = true;
	
	cause = header->tx_cause_1;            // cause of tx.
	asdu = header->commom_asdu_2 << 8;
	asdu |= header->commom_asdu_1;

	// actual time
	SYSTEMTIME st;
	FILETIME ft;
	GetSystemTime(&st);
	SystemTimeToFileTime(&st,&ft);
	
	// type identification
	switch(header->type)
	{
    
	case 1:									// 单点遥信
		if(cause == 5)
			NAIec104YXACK(msgbuf, len);
		break;
	case 11:								// 遥测  标度化测量值
		if(cause == 5)
			NAIec104YCACK(msgbuf, len);
		break;
	case 15:                                // 电度值
		break;
	case 45:								// 遥控
		if(cause == 6 && !isYKACK)			// 遥控返校
		{
    
			NAIec104YKACK(msgbuf, len);
			isYKACK = true;
		}
		else if(cause == 6 && isYKACK)		// 执行确认
		{
    
			NAIec104YKEXEACK(msgbuf, len);
			isYKACK = false;
		}
		if(cause == 8)						// 遥控撤销
			NAIec104YKDeactACK(msgbuf, len);
		break;
	case 49:
		if(cause == 5)						// 遥调请求
			NAIec104YTACK(msgbuf, len);
		break;
	case 47:
		YkInformation = msgbuf[NA_STARTINFORMATION+NA_IEC_104_INFORMATIONBYTE];
		YkReason = msgbuf[NA_STARTREASON];
		YkObject = msgbuf[NA_STARTINFORMATION]+msgbuf[NA_STARTINFORMATION+1]*256;
		YkObject -= 0xb01;		
		NAIec104ProcessYkYt();
		break;
	case 53:                                // 请求功图数据
		NAIec104ProcessGT(); 
		break;
	case 55:                                // 请求历史数据
		NAIec104ProcessHistory(msgbuf, len);
		break;
	case 100:								// 总召唤命令  包含分组召唤和总召唤
		NAIec104InterrogationAll();
		break;
	case 101:								// 计数量召唤 电能脉冲召唤
		Qcc = msgbuf[NA_IEC_104_QCC_POSITION];  
		FreezeSign = Qcc&0xc0;	
		FreezeSign>>=6;
		PulseGroup = Qcc&0x3f;
		NAIec104ProcessPulseAll(); 
		break;
	case 103:								// 时钟同步
		{
    
			CString str, ch;
			for(i=0;i<7;i++)
			{
    
				TimeSave[i] = msgbuf[NA_IEC_104_TIME_POSITION+i];
				ch.Format(" %02x ", TimeSave[i]);
				str += ch;
			}
			NAIec104ProcessTime();
		}	
		break;
	default:
		return -1;
	}
	return 0;
}

int CIEC104Slave::Send_S_Msg()
{
    
	if(GetSockConnectStatus())// 
	{
    
		APCI header;
		header.start  = 0x68;
		header.len    = 0x04;
		header.field1 = 0x01;				         // S-Format
		header.field2 = 0x00;
		header.field3 = RxCounter & 0xFE; 
		header.field4 = (RxCounter>>8) & 0xFF;
		
		LastAckRx = RxCounter;				        // memorize last rx acknowledge
		
		if(send(currentSock, (const char*)&header, sizeof(header), 0) == SOCKET_ERROR)
		{
    		
			return -1;
		}	
		// call callback
		iec104slave->OnTxMsg((BYTE*)&header,sizeof(header));
		iec104slave->timer_U_Testflag = false;		// reset TestFR time-out
		iec104slave->timer_S_Ackflag  = false;		// reset ACK time-out.
		return 0;
	}
	return -1;
}


int CIEC104Slave::Send_I_Msg(BYTE *msgbuf, unsigned int len)
{
    
	CString str, ch;
	for(unsigned int i = 0; i<len; i++)
	{
    
		ch.Format(" %02x ", msgbuf[i]);
		str += ch;
	}

	if(GetSockConnectStatus())
	{
    
		char buf[1024];	
		APCI *header = (APCI*)buf;
		(*header).start = 0x68;
		(*header).len = 0x04 + len;		
		(*header).field1 = TxCounter & 0xFE;	// I-Format
		(*header).field2 = (TxCounter>>8) & 0xFF;
		(*header).field3 = RxCounter & 0xFE;	// I-Format
		(*header).field4 = (RxCounter>>8) & 0xFF;

		CString str, ch;
		ch.Format(" %02x ", (*header).field1);
		str += ch;

		ch.Format(" %02x ", (*header).field2);
		str += ch;

		ch.Format(" %02x ", (*header).field3);
		str += ch;

		ch.Format(" %02x ", (*header).field4);
		str += ch;
		
		LastAckRx = RxCounter;					// memorize last rx acknowledge
		
		memcpy(&buf[sizeof(APCI)],msgbuf,len);
		
		if(send(currentSock, (const char*)buf, len + sizeof(APCI), 0) == SOCKET_ERROR)
		{
    
			printf(LastError, "send() failed with error: %d\n", WSAGetLastError());
			TRACE(LastError);
			slave_stop();
			return -1;
		}
		
		// call callback
		iec104slave->OnTxMsg((BYTE*)&buf, len + sizeof(APCI));
		
		iec104slave->timer_U_Testflag = false;		// reset TestFR time-out
		iec104slave->timer_S_Ackflag  = false;		// reset ACK time-out.
		
		// increment N(S) Send Sequence Number
		TxCounter += 2;
		
		return 0;
	}
	return -1;
}

void CIEC104Slave::ProcessFormatU(APCI *p)
{
    
	 if((p->field1 & 0xfc) == CMD_STARTV)	  // U启动
	{
    
		Send_U_Msg(CMD_STARTC);
	}
	else if((p->field1 & 0xfc) == CMD_STOPV)  // U停止
	{
    
		Send_U_Msg(CMD_STOPC);
	}
	else if((p->field1 & 0xfc) == CMD_TESTV)  // U测试
	{
    
		Send_U_Msg(CMD_TESTC);
	}
	else if((p->field1 & 0xfc) == CMD_TESTC)  // U测试确认帧
	{
    					              
		isT1Start = false;					  // 关闭T1计时器(收到U测试确认帧)
		// 将控件设置为无效
		iec104slave->OnUpdateTime(1, TRUE);
		iec104slave->OnUpdateRemainTime(1, timer_T1, TRUE);
	}
}

void CIEC104Slave::NAIec104ProcessYkYt()
{
    
	unsigned char     YkAction;
	// NS_RELATION       Dear;
	YkWhat=0;  /* nothing */ 
	if(YkReason==6)
	{
    
		if(YkInformation&0x80)    /* YkSelect */
			YkWhat=1;
		else 
			YkWhat=2;             /* YkExe */		
	}
	else 
		if(YkReason==8)           /* Esc */
			YkWhat=3;
	switch(YkWhat)
	{
    
		case 1:  /* select */
			ReceiveYkId = YkObject;
            YkAction = YkInformation&0x03;
			switch(YkAction)
			{
    
			case  1:   /* Open */
				YkCellAction = NA_TRIP;
				break;
			case  2:    /*  close */
				YkCellAction = NA_CLOSE;       
				break;     
			default:
				break;
			 }

			break;
		case 2:  /* YkExe  */
			ReceiveYkId=YkObject;
			YkAction=YkInformation&0x03;
			switch(YkAction)
			{
    
            case  1:   /* Open */
				YkCellAction = NA_TRIP;
				break;
            case  2:    /*  close */
				YkCellAction = NA_CLOSE;       
				break;     
            default:
				YkCellAction = 0xff;       
				break;
			}
			break;
		case 3:  /* Esc */
			ReceiveYkId = YkObject;
			YkAction = YkInformation&0x03;
			switch(YkAction)
			{
    
            case  1:   /* Open */
				YkCellAction = NA_TRIP;
				break;
            case  2:    /*  close */
				YkCellAction = NA_CLOSE;       
				break;     
            default:
				YkCellAction = 0xff;       
				break;
			}
			break;
		default:
          break;
	}
}

void CIEC104Slave::NAIec104ProcessTime()
{
    
	unsigned short   i,Index;

	ASDU msg;
	msg.header.type = 103;							// 时钟同步命令
	msg.header.qual = 0x01;							// number of elements
	msg.header.tx_cause_1 = 0x07;			        // confirm
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = CommonAsduAddress[0];
	msg.header.commom_asdu_2 = CommonAsduAddress[1];

													// group information
	Index = 0;
	msg.data[Index++] = InformationObject[0];
	msg.data[Index++] = InformationObject[1];
	msg.data[Index++] = InformationObject[2];
	for(i=0; i<7; i++)
		msg.data[Index++] = TimeSave[i];

	Send_I_Msg((BYTE*)&msg, Index+6);
}

void CIEC104Slave::NAIec104InterrogationAll()
{
    
	//	1. 总召确认
	InterrogationConfirm();

	//	2. 发送遥信所有数据 0-7共8组遥信数据
	ReturnAllYXData();

	//	3. 发送遥测所有数据 8-11共4组遥测数据
	ReturnAllYCData();

	//	5. 激活结束
	InterrogationComplete();
}

/*****************总召唤确认*************************/
void CIEC104Slave::InterrogationConfirm()
{
    
	ASDU msg;
	msg.header.type = 0x64;							// interrogation command
	msg.header.qual = 0x01;							// number of elements
	msg.header.tx_cause_1 = 0x07;					// confirm
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = CommonAsduAddress[0];
	msg.header.commom_asdu_2 = CommonAsduAddress[1];
	// group information
	msg.data[0] = InformationObject[0];
	msg.data[1] = InformationObject[1];
	msg.data[2] = InformationObject[2];
	msg.data[3] = 20;
	Send_I_Msg((BYTE*)&msg, 10);
}

/*****************总召唤结束*************************/
void CIEC104Slave::InterrogationComplete()
{
    
	ASDU msg;
	msg.header.type = 0x64;							// interrogation command
	msg.header.qual = 0x01;				        	// number of elements
	msg.header.tx_cause_1 = 0x0a;					// confirm
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = CommonAsduAddress[0];
	msg.header.commom_asdu_2 = CommonAsduAddress[1];
	// group information
	msg.data[0] = InformationObject[0];
	msg.data[1] = InformationObject[1];
	msg.data[2] = InformationObject[2];
	msg.data[3] = 20;
	Send_I_Msg((BYTE*)&msg, 10);
}

/*****************返回所有遥信数据:共8组(0-7)*************************/
void CIEC104Slave::ReturnAllYXData()
{
    
	unsigned char      *p, start;
	unsigned short     Index;
	unsigned long      InformationObject, i;

	CString ch, str;

#if 0
	for(i = 0; i<288; i++)
	{
    
		if(i%10 == 0)
			str += '\n';
		ch.Format(" %02x ", ALLYXData[i]);
		str += ch;
	}
#endif

	/* Send  Yx   total send 1024 yx  共288组数据*/
	for(start=0; start<NA_IEC_104_TOTAL_YX_TIMES; start++)
    {
    
		ASDU msg;
		msg.header.type = 0x01;					        // 单点信息
		msg.header.qual = NA_IEC_104_ONCE_YX|0x80;		// number of elements
		msg.header.tx_cause_1 = 20;			         
		msg.header.tx_cause_2 = 0x00;
		msg.header.commom_asdu_1 = CommonAsduAddress[0];
		msg.header.commom_asdu_2 = CommonAsduAddress[1];
		
		// group information
		Index = 0;
		InformationObject = 0x01 + start*NA_IEC_104_ONCE_YX;
		p = (unsigned char  *)&InformationObject;

		for(i= 0; i<NA_IEC_104_INFORMATIONBYTE; i++)
		{
    
			msg.data[Index++] = p[i];
		}


#if 0
		ch.Format(" %02x ", p[0]);
		str += ch;
		ch.Format(" %02x ", p[1]);
		str += ch;
		ch.Format(" %02x ", p[2]);
		str += ch;
#endif

		for(i = (InformationObject-1); i< (InformationObject+NA_IEC_104_ONCE_YX-1); i++)
		{
    		
			msg.data[Index++] = ALLYXData[i];		
		}
		
		Send_I_Msg((BYTE*)&msg, Index+6);
	}

}

/*****************返回所有遥测数据:发60次,一次100个字节,一共5902,最后一次发02个*************************/
void CIEC104Slave::ReturnAllYCData()
{
    
	/* Send  Yc    */
	CString			   str, ch;
	unsigned char      *p, start;
	unsigned short     iIndex,  i;
	unsigned long      InformationObject;

	for(start=0; start<NA_IEC_104_TOTAL_YC_TIMES; start++)// NA_IEC_104_TOTAL_YC_TIMES
	{
    
		ASDU msg;
		msg.header.type = 0x0b;							  // 标度化测量值

		if(start != (NA_IEC_104_TOTAL_YC_TIMES-1))
		{
    
			msg.header.qual = NA_IEC_104_ONCE_YC|0x80;	  // number of elements
		}
		else
		{
    
			msg.header.qual = 0x81;
		}

		
		msg.header.tx_cause_1 = 20;						  // 响应总召
		msg.header.tx_cause_2 = 0x00;
		msg.header.commom_asdu_1 = CommonAsduAddress[0];
		msg.header.commom_asdu_2 = CommonAsduAddress[1];
		
		// group information
		iIndex = 0;
		InformationObject = 0x4001 + start*NA_IEC_104_ONCE_YC;
		p = (unsigned char  *)&InformationObject;
		for(i= 0; i<NA_IEC_104_INFORMATIONBYTE; i++)
		{
    
			msg.data[iIndex++] = p[i];
			ch.Format(" %02x ", p[i]);
			str += ch;
		}

		if(start != (NA_IEC_104_TOTAL_YC_TIMES-1))
		{
    
			for(i = 0 + start*NA_IEC_104_ONCE_YC; i< NA_IEC_104_ONCE_YC+start*NA_IEC_104_ONCE_YC; i++)
			{
    
				msg.data[iIndex++] = LOBYTE(ALLYCData[i]);
				msg.data[iIndex++] = HIBYTE(ALLYCData[i]); 
				msg.data[iIndex++] = 0x00;		
			}
		}
		else
		{
    
			for(i = 0 + start*NA_IEC_104_ONCE_YC; i< 1+start*NA_IEC_104_ONCE_YC; i++)
			{
    
				msg.data[iIndex++] = LOBYTE(ALLYCData[i]);
				msg.data[iIndex++] = HIBYTE(ALLYCData[i]); 
				msg.data[iIndex++] = 0x00;		
			}
		}		

	//	int timeOrigin = GetTickCount();
	//	while((GetTickCount() - timeOrigin) < 50);
		Sleep(75);
		Send_I_Msg((BYTE*)&msg, iIndex + 6);
	}


}


DWORD WINAPI CIEC104Slave::DealIFrameHandler(LPVOID lpParam)
{
    
	while(isDealIFrameHandler) // 
	{
    
		if(isRevI)
		{
    
			if(iec104slave->ProcessFormatI(IMsg, IMsgLen) != -1)
			{
    
				isRevI = false;
				isT2Start = true;									 // 启动T2定时器
				timer_T2 = 0;										 // 发送完I格式帧以后开始计时
				iec104slave->OnUpdateTime(2);
				iec104slave->OnUpdateRemainTime(2, iec104slave->T2 - timer_T2);
			}
		}
		Sleep(75);
	}
	if(hDealIFrameThread != NULL)
	{
    
		CloseHandle(hDealIFrameThread);
		hDealIFrameThread = NULL;
	}
	return 0;
}


void CIEC104Slave::NAIec104ProcessPulseAck()
{
    
	ASDU msg;
	msg.header.type = 101;					// 电能脉冲召唤命令
	msg.header.qual = 0x01;					// number of elements
	msg.header.tx_cause_1 = 0x07;			// confirm
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = CommonAsduAddress[0];
	msg.header.commom_asdu_2 = CommonAsduAddress[1];

	// group information
	msg.data[0] = InformationObject[0];
	msg.data[1] = InformationObject[1];
	msg.data[2] = InformationObject[2];
	msg.data[3] = Qcc;
	Send_I_Msg((BYTE*)&msg, 10);
}

void CIEC104Slave::NAIec104ProcessPulseEnd()
{
    
	ASDU msg;
	msg.header.type = 101;					// 电能脉冲召唤命令
	msg.header.qual = 0x01;					// number of elements
	msg.header.tx_cause_1 = 0x0a;			// over
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = CommonAsduAddress[0];
	msg.header.commom_asdu_2 = CommonAsduAddress[1];
	
	// group information
	msg.data[0] = InformationObject[0];
	msg.data[1] = InformationObject[1];
	msg.data[2] = InformationObject[2];
	msg.data[3] = Qcc;
	Send_I_Msg((BYTE*)&msg, 10);
}

void CIEC104Slave::NAIec104ProcessPulseAll()
{
    
	/*  First Send  Confirm */
	NAIec104ProcessPulseAck();
	/* 累计量 */
	ReturnALLYMData();
	/* Send Pulse End Packet */
	NAIec104ProcessPulseEnd();
}


/*****************返回所有电度值信息***********************/
void CIEC104Slave::ReturnALLYMData()
{
    
	unsigned char       *p;
	unsigned short      i, iIndex;
	unsigned long       InformationObject;
//	WORD highWord;
//	BYTE lowWordHighByte, lowWordLowByte;

	ASDU msg;
	msg.header.type = 15;									// 电能脉冲召唤命令
	msg.header.qual = NA_IEC_104_ONCE_YM|0x80;				// number of elements 20, 连续的20个数据
	msg.header.tx_cause_1 = 5;								// 请求,被请求
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = CommonAsduAddress[0];
	msg.header.commom_asdu_2 = CommonAsduAddress[1];

	iIndex = 0;
	InformationObject = 0x6401;
	p = (unsigned char  *)&InformationObject;
	
	for(i= 0; i<NA_IEC_104_INFORMATIONBYTE; i++)
	{
    
		msg.data[iIndex++] = p[i];
	}

	CString str, ch;
	for(i = 0; i< NA_IEC_104_ONCE_YM; i++)
	{
    
		msg.data[iIndex++] = HIBYTE(HIWORD(ALLYMData[i])); 
		msg.data[iIndex++] = LOBYTE(HIWORD(ALLYMData[i])); 
		msg.data[iIndex++] = HIBYTE(LOWORD(ALLYMData[i]));
		msg.data[iIndex++] = LOBYTE(LOWORD(ALLYMData[i]));
	/*
		int iTemp = 0;
		ch.Format(" %08x ", ALLYMData[i]);
		msg.data[iIndex++] = HIBYTE(HIWORD(ALLYMData[i])); 
		ch.Format(" %02x ", HIBYTE(HIWORD(ALLYMData[i])));
		str += ch;
		iTemp++;

		msg.data[iIndex++] = LOBYTE(HIWORD(ALLYMData[i])); 
		ch.Format(" %02x ", LOBYTE(HIWORD(ALLYMData[i])));
		str += ch;

		msg.data[iIndex++] = HIBYTE(LOWORD(ALLYMData[i])); 
		ch.Format(" %02x ", HIBYTE(LOWORD(ALLYMData[i])));
		str += ch;

		msg.data[iIndex++] = LOBYTE(LOWORD(ALLYMData[i]));
		ch.Format(" %02x ", LOBYTE(LOWORD(ALLYMData[i])));
		str += ch;
	*/
	}

	BYTE TEST[128];
	memcpy(TEST, (BYTE*)&msg, iIndex+6);
	for(i = 0; i< iIndex +6; i++)
	{
    
		ch.Format(" %02x ", TEST[i]);
		str += ch;
	}

	Send_I_Msg((BYTE*)&msg, iIndex+6);

	/*
	if(isRevS)
	{
		Send_I_Msg((BYTE*)&msg, iIndex+6);
		isRevS = false;
	}
	*/
}

void CIEC104Slave::NAIec104YKACK(BYTE *msgbuf, unsigned int len)
{
    
	msgbuf[2] = 0x07;
	Send_I_Msg(msgbuf, len);
}

void CIEC104Slave::NAIec104YKDeactACK(BYTE *msgbuf, unsigned int len)
{
    
	msgbuf[2] = 0x09;
	Send_I_Msg(msgbuf, len);
}

void CIEC104Slave::NAIec104YKEXEACK(BYTE *msgbuf, unsigned int len)
{
    
	msgbuf[2] = 0x07;
	Send_I_Msg(msgbuf, len);
}

void CIEC104Slave::NAIec104YTACK(BYTE *msgbuf, unsigned int len)
{
    
	Send_I_Msg(msgbuf, len);
}

void CIEC104Slave::NAIec104YXACK(BYTE *msgbuf, unsigned int len)
{
    
	BOOL isContinuous;
	unsigned short dataNum, startPosition, iIndex, i;

	// 解析可变结构限定词
	// 解析信息体起始地址
	// 取数据,组包

	// {" 01  82  05  00  01  00  01  00  00 "}
	BYTE lenSign; 
	lenSign = msgbuf[1];

	// 判断是否连续
	if(lenSign & 0x80)
		isContinuous = true;// 连续
	else
		isContinuous = false;

	// 求出有多少个数据
	dataNum = lenSign & 0x7f;

	WORD startAddress;
	startAddress = MAKEWORD(msgbuf[6], msgbuf[7]);

	startPosition = startAddress - 0x0001;

	// 相对位置 
	ASDU msg;
	msg.header.type = 0x01;									// 单点遥信
	msg.header.qual = dataNum|0x80;							// number of elements
	msg.header.tx_cause_1 = 0x05;							// 请求,被请求
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = msgbuf[4];                   // 公共地址
	msg.header.commom_asdu_2 = msgbuf[5];	

	iIndex = 0;
	msg.data[iIndex++] = msgbuf[6];
	msg.data[iIndex++] = msgbuf[7];
	msg.data[iIndex++] = msgbuf[8];

	for(i = 0; i<dataNum; i++)
	{
    
		msg.data[iIndex++] = ALLYXData[startPosition+i];
	}	

	Send_I_Msg((BYTE*)&msg, iIndex +6); // 根据要求返回遥信数据,解析要回哪些地址的数据,要看限定词
}

void CIEC104Slave::NAIec104YCACK(BYTE *msgbuf, unsigned int len)
{
    
	Send_I_Msg(msgbuf, len);  // 根据要求返回遥测数据
}

DWORD WINAPI CIEC104Slave::TimerHandler(LPVOID lpParam)
{
    
	while(IsTimerHandler)
	{
    
		// 间隔1s
		int timeOrigin = GetTickCount();
		while((GetTickCount() - timeOrigin) < 1000);

#if 1		
        // T1:RTU(服务器)端启动U格式测试过程后等待U格式测试应答的超时时间(超时处理:断开连接)
		// 启动条件:发送U测试帧
		// 关闭条件:接收U测试帧
		if(isT1Start)
		{
    
			timer_T1++;
			iec104slave->OnUpdateRemainTime(1, iec104slave->T1-timer_T1);
			if(timer_T1 > iec104slave->T1)
			{
    
				// timer_T1 = 0;
				AfxMessageBox("t1超时");
				iec104slave->slave_stop();
			}
		}		
#endif

#if 1
		// T2: RTU(服务器)端
		//	1)	以突发的传送原因向主站(客户)端上送了变化信息
		//	2)	或以激活结束的传送原因向主站(客户)端上送了总召唤/电度召唤结束后
		//	等待主站(客户)端回S格式的超时时间,若超过此时间还没有收到,就主动关闭TCP连接
		//  处理完I格式帧后开始计时(置0),接收到S帧置0
		//  启动条件:发送完所有I帧
		//  关闭条件:收到S/I帧
		if(isT2Start)
		{
    
			timer_T2++;
			iec104slave->OnUpdateRemainTime(2, iec104slave->T2-timer_T2);
			if(timer_T2 > iec104slave->T2)
			{
    
				timer_T2 = 0;
				AfxMessageBox("t2超时");
				iec104slave->slave_stop();
			}
		}
	
#endif
		
#if 1
		// T3:当RTU(服务器)端和主站(客户)端之间没有实际的数据交换时,任何一端启动U格式测试过程的最大间隔时间(超时处理:发送U测)
		// 启动条件:建立连接
		// 关闭条件:断开连接
		if(isT3Start)
		{
    
			timer_T3++;
			iec104slave->OnUpdateRemainTime(3, iec104slave->T3-timer_T3);
			if(timer_T3 > iec104slave->T3 && !isT1Start)
			{
    
				//	timer_T3 = 0;
				//	AfxMessageBox("t3超时");
				iec104slave->Send_U_Msg(CMD_TESTV);    
				isT1Start = true;                    // 启动T1计时器(发送U测试帧)
				// 设置界面内容
				iec104slave->OnUpdateTime(1);  
				// 设置初始剩余时间		
				timer_T1 = 0;						 // 发送完U测置0
				iec104slave->OnUpdateRemainTime(1, iec104slave->T1 - timer_T1);
			}
		}
		
#endif
	Sleep(75);
	}
	if(hTrThread != NULL)
	{
    
		CloseHandle(hTrThread);
		hTrThread = NULL;
	}
	return 0;
}

void CIEC104Slave::SetParams(int t0, int t1, int t2, int t3, int k, int w)
{
    
	T0 = t0;
	T1 = t1;
	T2 = t2;
	T3 = t3;
	K  =  k;
	W  =  w;
}

void CIEC104Slave::startT2Timer()
{
    
	isT2Start = true;                // 启动T2定时器
	timer_T2 = 0;
	iec104slave->OnUpdateTime(2);
	iec104slave->OnUpdateRemainTime(2, iec104slave->T2-timer_T2);
}

void CIEC104Slave::SetAllYXData(BYTE *yxBuf, int yxLen)
{
    
	memcpy(ALLYXData, yxBuf, yxLen);
#if 0
	CString ch, str;
	for(int i = 0; i<yxLen; i++)
	{
    
		if(i%10 == 0)
			str += '\n';
		ch.Format(" %02x ", ALLYXData[i]);
		str += ch;
 	}
	AfxMessageBox(str);
#endif
}

void CIEC104Slave::SetAllYCData(WORD *ycBuf, int ycLen)
{
    
	// memcpy(ALLYCData, ycBuf, ycLen);
	memmove(ALLYCData, ycBuf, ycLen);
#if 0
	CString str, ch;
	for(int i = 0; i<2951; i++)
	{
    
		ch.Format(" %04x ", ALLYCData[i]);
		str += ch;
	}
	str = "";
	ch = "";
#endif
}

void CIEC104Slave::SetAllYMData(DWORD *ymBuf, int ymLen)   // 电度量数据
{
    
	memcpy(ALLYMData, ymBuf, ymLen);
}

void CIEC104Slave::MyMemcpy(void *dest, void *src, int len)
{
    
	for(int i = 0; i< len; i++)
	{
    
		((char*)dest)[i] = ((char*)src)[i];
	}
}

void CIEC104Slave::NAIec104ProcessGT()
{
    
	//	AfxMessageBox("读取功图命令");
	// 从站->主站:应答功图数据
	// 从站->主站:应答功图数据
	// 从站->主站:应答功图数据

	// 从站->主站:功图数据上传结束(RTU结束后自行解除冻结)
	NAIec104ProcessGTEnd();
}

void CIEC104Slave::NAIec104ProcessGTEnd()
{
    
	ASDU msg;
	msg.header.type = 0x35;					// 电能脉冲召唤命令
	msg.header.qual = 0x01;					// number of elements
	msg.header.tx_cause_1 = 0x0a;			// over
	msg.header.tx_cause_2 = 0x00;
	msg.header.commom_asdu_1 = CommonAsduAddress[0];
	msg.header.commom_asdu_2 = CommonAsduAddress[1];
	
	// group information
	msg.data[0] = InformationObject[0];
	msg.data[1] = InformationObject[1];
	msg.data[2] = InformationObject[2];
	msg.data[3] = 20;
	Send_I_Msg((BYTE*)&msg, 10);
}

void CIEC104Slave::NAIec104ProcessHistory(BYTE *msgbuf, unsigned int len)
{
    
	// 回复历史YX数据
	// 回复历史YC数据
	// 回复历史电度数据
	// 回复历史GT数据
	// 历史数据请求结束帧
	NAIec104ProcessHistoryEnd(msgbuf, len);
}

void CIEC104Slave::NAIec104ProcessHistoryEnd(BYTE *msgbuf, unsigned int len)
{
    
/*
	CString str, ch;
	for(int i = 0; i<len; i++)
	{
		ch.Format(" %02x ", msgbuf[i]);
		str += ch;
	}
*/

	msgbuf[2] = 0x0a;
	Send_I_Msg(msgbuf, len);
}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Hughier/article/details/110071157

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan