欧姆龙fins协议 C++开发~~第一篇博客

FINS(factory interface network service)通信协议是欧姆龙公司开发的用于工业自动化控制网络的指令/响应系统。

运用 FINS指令可实现各种网络间的无缝通信,通过编程发送FINS指令,上位机或PLC就能够读写另一个PLC数据区的内容,甚至控制其运行状态,从而简化了用户程序。

FINS协议支持工业以太网,这就为OMRON PLC与上位机以太网通信的实现提供了途径。

FINS协议的标准资料在欧姆龙官网,大家可以百度搜索。

欧姆龙Fins协议

1、Fins协议和我们常用的ModbusTCP协议不同,首先体现在有握手验证命令,就是说在正常的三次握手的基础上,还需要进行一次握手,如果能正常握手,方可通信,这样做的目的也是为了PLC通信安全。

2、Fins握手命令

发送报文格式: 

 

 

 

 

返回报文格式: 

 
 

C++开发代码

#include "stdafx.h"
#include "Communication.h"


CCommunication::CCommunication()
{
	m_nRecvTimeout = 5000;
	m_bLongConnect = true;
	m_hSock = INVALID_SOCKET;
	InitSockWSA();

	memset(&m_pSA, 0, sizeof(struct sockaddr_in));
	
	m_hRecvExit = CreateEvent(NULL, TRUE, FALSE, NULL);
	m_hRecvExitFinish = CreateEvent(NULL, TRUE, FALSE, NULL);
	m_hRecvTimeoutEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	m_hRecvStartData = CreateEvent(NULL, TRUE, FALSE, NULL);

	CreateRecvThread();
}


CCommunication::~CCommunication()
{
	ExitRecvThread();
	ReleaseSockWSA();

	CloseHandle(m_hRecvExit);
	CloseHandle(m_hRecvExitFinish);
	CloseHandle(m_hRecvTimeoutEvent);
	CloseHandle(m_hRecvStartData);
}



void CCommunication::InitSockWSA()
{
	// 必须的
	WSADATA wsaData;
	if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
	{		
	}
}


void CCommunication::ReleaseSockWSA()
{
	// 必须的
	if(WSACleanup() != 0)
	{		
	}
}

// 释放
void CCommunication::Release()
{

}

// TCP连接
long CCommunication::Connect()
{
	if(m_hSock == INVALID_SOCKET)
	{
		m_hSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if(m_hSock != INVALID_SOCKET)
		{		
			if(connect(m_hSock, (struct sockaddr*)&m_pSA, sizeof(m_pSA)) < 0)
			{					
				closesocket(m_hSock);
				m_hSock = INVALID_SOCKET;	
				return FINS_SOCKET_CONNECT_FAIL;
			}	
		}
		else
		{
			return FINS_SOCKET_CREATE_FAIL;
		}
	}

	return 0;
}

// TCP关闭连接
void CCommunication::CloseConnect()
{
	vLocker lock(&m_syncLockTcp);
	if (m_hSock != INVALID_SOCKET)
	{
		shutdown(m_hSock, 2);
		closesocket(m_hSock);
		m_hSock = INVALID_SOCKET;

		OnCloseConnect();
	}	
}


// 设置超时
void CCommunication::SetTimeout(__int32 nMs)
{
	if(nMs < 100)
	{
		nMs = 100;
	}
	m_nRecvTimeout = nMs;
}

int CCommunication::GetTimeout()
{
	return m_nRecvTimeout;
}


// 长连接或者短连接模式
void CCommunication::SetMode(bool bLongConnect)
{
	m_bLongConnect = bLongConnect;
}

// socket参数
void CCommunication::SetTcpIP(const char* pIP)
{	
	m_pSA.sin_family = AF_INET;	
	m_pSA.sin_addr.s_addr = inet_addr(pIP);	
}

// socket参数
void CCommunication::SetTcpPort(__int32 nPort)
{	
	m_pSA.sin_family = AF_INET;
	m_pSA.sin_port = htons(nPort);
}




// 数据发读
long CCommunication::SendSyncData(CMyString pSendData, CMyString& pRecvData)
{
	vLocker lock(&m_syncLock);

	long nCode = Connect();
	if(nCode != 0)
	{
		return nCode;
	}

	// 清空原来的数据
	m_pRecvData.SetSize(0);

	// 发送数据
	int res = send(m_hSock, pSendData.GetString(), pSendData.Size(), 0);
	if(res == SOCKET_ERROR)
	{
		CloseConnect();
		return FINS_SEND_FAIL;
	}
	
	// 数据读取
	nCode = RecvData(pRecvData);

	// 短连接每次都关闭
	if(!m_bLongConnect)
	{
		CloseConnect();
	}

	return nCode;
}



// 数据读取
// 带超时处理
long CCommunication::RecvData(CMyString& pRecvData)
{		
	long nCode = 0;
	StartRecvData();

	// 等待接收
	ResetEvent(m_hRecvTimeoutEvent);
	DWORD rs = WaitForSingleObject(m_hRecvTimeoutEvent, m_nRecvTimeout);
	
	// 成功返回值
	if (rs == WAIT_OBJECT_0)
	{
		pRecvData = m_pRecvData;
	}

	// 超时等其他错误
	else
	{
		StopRecvData();
		CloseConnect();
		nCode = FINS_RECV_WAIT_TIMEOUT;
	}
	
	return nCode;	
}


// 创建接收线程
// 
void CCommunication::CreateRecvThread()
{
	ResetEvent(m_hRecvExitFinish);
	_beginthread(RunRecvThread, 0, this);
}

// 退出接收线程
// 
void CCommunication::ExitRecvThread()
{
	CloseConnect();
	SetEvent(m_hRecvExit);	
	SetEvent(m_hRecvStartData);
	WaitForSingleObject(m_hRecvExitFinish, INFINITE);
}



// 定时器线程
void CCommunication::RunRecvThread(void* lp)
{
	CCommunication* pMC = (CCommunication*)lp;
	pMC->RecvHandle();
	SetEvent(pMC->m_hRecvExitFinish);	// 处理完毕
}

// 定时器
void CCommunication::RecvHandle()
{
	while (!IsExitThread())
	{
		WaitForSingleObject(m_hRecvStartData, INFINITE);
		if (IsExitThread())
		{
			break;
		}		

		OneRecvData();
	}
}

// 一次接收数据
void CCommunication::OneRecvData()
{
	m_nSize = 200;
	m_nRecvSize = recv(m_hSock, m_pBuff, m_nSize, 0);
	if(m_nRecvSize == SOCKET_ERROR)
	{
		CloseConnect();
		m_nRecvCode = FINS_SOCKET_CONNECT_ERR;
	}
	else
	{			
		m_pRecvData.Append(m_pBuff, m_nRecvSize);			
		m_nRecvCode = 0;	
		OnDataRecv(m_pBuff, m_nRecvSize);	
	}
}

// 告诉通讯,数据接收完成
void CCommunication::SetRecvComplete(int nSize)
{
	ResetEvent(m_hRecvStartData);			// 复位接收
	SetEvent(m_hRecvTimeoutEvent);			// 接收成功	
}

// 重写数据接收,开始接收数据,用于协议识别
void CCommunication::OnBeginRecv()
{
}

// 通讯关闭
void CCommunication::OnCloseConnect()
{

}

bool CCommunication::IsExitThread()
{
	if(WaitForSingleObject(m_hRecvExit, 0) == WAIT_OBJECT_0)
	{
		return true;
	}
	return false;
}



// 开始接收数据
void CCommunication::StartRecvData()
{
	OnBeginRecv();
	SetEvent(m_hRecvStartData);
}

// 停止接收数据
void CCommunication::StopRecvData()
{
	ResetEvent(m_hRecvStartData);
}

// 断开通讯
void CCommunication::Disconnect()
{

}

  

#include "stdafx.h"
#include "FinsHandle.h"

CFinsHandle::CFinsHandle()
{
	m_bEstablishCommunicationByFins = false;
	m_nIpNode = 0;
}

// 重写数据接收,用于协议识别
void CFinsHandle::OnDataRecv(char* pData, int nSize)
{
	if (nSize > 0)
	{	
		m_pRecvData.Append(pData, nSize);
		if (m_pRecvData.Size() >= FINS_TCP_HEAD_SIZE)
		{
			FINS_TCP_HEAD pHead;
			pHead.SetData(m_pRecvData.GetString());
			int nAllSize = pHead.GetLength();

			if (m_pRecvData.Size() >= nAllSize)
			{
				SetRecvComplete(nAllSize);
			}
		}
	}
}


// 重写数据接收,开始接收数据,用于协议识别
void CFinsHandle::OnBeginRecv()
{
	m_pRecvData.SetSize(0);
}

// 通讯关闭
void CFinsHandle::OnCloseConnect()
{
	m_bEstablishCommunicationByFins = false;
}


long CFinsHandle::ReadMemoryData(int nAddr, int nSize, FINS_DATA_TYPE::ENUM nType, __int16* pData)
{
	long nCode = 0;
	if(nCode = EstablishCommunicationByFins())
	{
		return nCode;
	}

	CMyString pSendData;

	// 获取发送内存
	int nAllSize;
	nAllSize  = FINS_TCP_HEAD_SIZE;
	nAllSize += FINS_CONTROL_HEAD_SIZE;
	nAllSize += FINS_MEMORY_AREA_READ_SIZE; 
	pSendData.SetSize(nAllSize);
	char* pBuff = pSendData.GetString();
	
	// 头部信息
	FINS_TCP_HEAD pHead;	
	pHead.nCommand = FINS_TCP_CMD_DATA;
	pHead.SetLength(nAllSize);		//长度
	pHead.GetData(pBuff);

	// control部分
	FINS_CONTROL_HEAD pControlHead;
	pControlHead.nDA1 = 0; 
	pControlHead.nSA1 = m_nIpNode;
	pControlHead.nCmd1 = 0x01;
	pControlHead.nCmd2 = 0x01;
	pControlHead.GetData(pBuff + FINS_TCP_HEAD_SIZE);

	// 数据部分
	FINS_MEMORY_AREA_READ pMemory;
	pMemory.nAreaCode = nType;
	pMemory.nAddr = nAddr;
	pMemory.nBitNo = 0;
	pMemory.nLength = nSize;
	pMemory.GetData(pBuff + FINS_TCP_HEAD_SIZE + FINS_CONTROL_HEAD_SIZE);
	
	//	// 发送数据
	CMyString pRecvData;
	nCode = SendSyncData(pSendData, pRecvData);
	if (nCode == 0)
	{
		// 先判断答复数据的头数据是否正确	
		if(nCode = CheckReplyDataIsError(pRecvData.GetString(), pRecvData.Size()))
		{
			return nCode;
		}

		// 数据头
		FINS_TCP_HEAD pHeadReply;
		pHeadReply.SetData(pRecvData.GetString());

		// 答复长度要求
		int nMinSize = FINS_TCP_HEAD_SIZE + FINS_CONTROL_HEAD_SIZE + FINS_MEMORY_AREA_READ_FIX_R_SIZE + nSize * 2;
		if(pHeadReply.GetLength() < nMinSize)
		{
			return FINS_REPLY_READ_DATA_TOO_SHORT;
		}
		else
		{
			// FINS协议部分
			FINS_CONTROL_HEAD pControlHeadReply;
			pControlHeadReply.SetData(pRecvData.GetString());
		
			if (pControlHeadReply.nCmd1 != pControlHead.nCmd1 ||
				pControlHeadReply.nCmd2 != pControlHead.nCmd2)
			{
				// 命令不一致
				return FINS_REPLY_CMD_NO_IS_REQUST_CMD;
			}

			// 答复数据
			FINS_MEMORY_AREA_READ_REPLY pReplyData;
			pReplyData.SetData(pRecvData.GetString(), pRecvData.Size());	
			if(pReplyData.nEndCode != 0)
			{
				return FINS_REPLY_READ_DATA_FAIL;	
			}

			// 拷贝数据
			int nReadByte = nSize * 2;
			if (pReplyData.GetDataBytsSize() == nReadByte)
			{
				memcpy(pData, pReplyData.GetData(), nReadByte);
			}
			else
			{				
				return FINS_REPLY_READ_DATA_TOO_SHORT;				
			}
		}
	}
	return nCode;
}



long CFinsHandle::WriteMemoryData(int nAddr, int nSize, FINS_DATA_TYPE::ENUM nType, __int16* pData)
{
	long nCode = 0;
	if(nCode = EstablishCommunicationByFins())
	{
		return nCode;
	}

	CMyString pSendData;
	int nBytsSize = nSize * 2;

	// 获取发送内存
	int nAllSize;
	nAllSize  = FINS_TCP_HEAD_SIZE;
	nAllSize += FINS_CONTROL_HEAD_SIZE;
	nAllSize += FINS_MEMORY_AREA_READ_SIZE; 
	nAllSize += nBytsSize;
	pSendData.SetSize(nAllSize);
	char* pBuff = pSendData.GetString();


	// 头部信息
	FINS_TCP_HEAD pHead;	
	pHead.nCommand = FINS_TCP_CMD_DATA;
	pHead.SetLength(nAllSize);		//长度
	pHead.GetData(pBuff);

	// control部分
	FINS_CONTROL_HEAD pControlHead;
	pControlHead.nDA1 = 0; 
	pControlHead.nSA1 = m_nIpNode;
	pControlHead.nCmd1 = 0x01;
	pControlHead.nCmd2 = 0x02;
	pControlHead.GetData(pBuff + FINS_TCP_HEAD_SIZE);

	// 数据部分
	FINS_MEMORY_AREA_WRITE pMemory;
	pMemory.nAreaCode = nType;
	pMemory.nAddr = nAddr;
	pMemory.nBitNo = 0;
	pMemory.nLength = nSize;
	pMemory.pData.Append((char*)pData, nBytsSize);
	pMemory.GetData(pBuff + FINS_TCP_HEAD_SIZE + FINS_CONTROL_HEAD_SIZE);

	//	// 发送数据
	CMyString pRecvData;
	nCode = SendSyncData(pSendData, pRecvData);
	if (nCode == 0)
	{
		// 先判断答复数据的头数据是否正确			
		if(nCode = CheckReplyDataIsError(pRecvData.GetString(), pRecvData.Size()))
		{
			return nCode;
		}
		
		// 数据头
		FINS_TCP_HEAD pHeadReply;
		pHeadReply.SetData(pRecvData.GetString());

		// 答复长度要求
		int nMinSize = FINS_TCP_HEAD_SIZE + FINS_CONTROL_HEAD_SIZE + FINS_MEMORY_AREA_WRITE_R_SIZE;
		if(pHeadReply.GetLength() < nMinSize)
		{
			return FINS_REPLY_READ_DATA_TOO_SHORT;
		}
		else
		{
			// FINS协议部分
			FINS_CONTROL_HEAD pControlHeadReply;
			pControlHeadReply.SetData(pRecvData.GetString());
			if (pControlHeadReply.nCmd1 != pControlHead.nCmd1 ||
				pControlHeadReply.nCmd2 != pControlHead.nCmd2)
			{
				// 命令不一致
				return FINS_REPLY_CMD_NO_IS_REQUST_CMD;
			}
						
			// 答复数据
			FINS_MEMORY_AREA_WRITE_REPLY pReplyData;
			pReplyData.SetData(pRecvData.GetString());				
			if(pReplyData.nEndCode != 0)
			{
				return FINS_REPLY_WRITE_DATA_FAIL;	
			}
		}
		
	}
	return nCode;
}


	// 获取fins节点地址
long CFinsHandle::GetFinsNodeAddress()
{
	return 0;
}


// 检查答复数据是否错误
long CFinsHandle::CheckReplyDataIsError(char* pData, int nSize)
{
	if (nSize < FINS_TCP_HEAD_SIZE)
	{
		// 小于最小要求数据
		return FINS_REPLY_DATA_TOO_SHORT;
	}
	
	// 消息错误
	FINS_TCP_HEAD pHeadReply;
	pHeadReply.SetData(pData);
	if (pHeadReply.nErrorCode)
	{
		return FINS_REPLY_ERROR_BY_MESSAGE;
	}
		
	return 0;
}


// 建立通讯
long CFinsHandle::EstablishCommunicationByFins()
{
	if (m_bEstablishCommunicationByFins)
	{
		// 已经建立通讯连接了
		return 0;
	}
	
	long nCode = 0;
	CMyString pSendData;

	// 获取发送内存
	int nAllSize;
	nAllSize  = FINS_TCP_HEAD_SIZE;
	nAllSize += FINS_CONNECT_REQUST_SIZE;
	pSendData.SetSize(nAllSize);	
	char* pBuff = pSendData.GetString();

	// 头部信息
	FINS_TCP_HEAD pHead;	
	pHead.nCommand = FINS_TCP_CMD_CONNECT_REQUST;
	pHead.SetLength(nAllSize);		//长度
	pHead.GetData(pBuff);
	
	// IP地址
	FINS_CONNECT_REQUST pConnectRequst;	
	pConnectRequst.GetData(pBuff + FINS_TCP_HEAD_SIZE);
		
// 发送数据
	CMyString pRecvData;
	if(nCode = SendSyncData(pSendData, pRecvData))
	{
		return nCode;
	}

	// 处理返回值
	FINS_TCP_HEAD pHeadReply;
	pHeadReply.SetData(pRecvData.GetString());
	
	// 检查头信息
	if (pHeadReply.nErrorCode == 0 &&
		pHeadReply.nCommand == FINS_TCP_CMD_CONNECT_RESPONSE)
	{

		// 提取 IP Node信息
		if (pHeadReply.GetLength() == FINS_TCP_HEAD_SIZE + FINS_CONNECT_RESPONS_SIZE)
		{
			// 提取
			FINS_CONNECT_RESPONSE pConnectResponse;
			pConnectResponse.SetData(pRecvData.GetString());
			m_nIpNode = pConnectResponse.pClientAddrss[3];
			
			// 建立通讯成功
			m_bEstablishCommunicationByFins = true;

			return 0;
		}					
	}	

	return FINS_REQUST_CONNECT_FAIL;
}

  

posted @ 2022-07-17 23:03  我为鸡狂·  阅读(867)  评论(0)    收藏  举报