欧姆龙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;
}

浙公网安备 33010602011771号