OneAPIConnect 欧姆龙通讯Fins协议源代码,应用于半导体7*24小运行稳定

每家PLC厂商都有自己的通讯协议,西门子S7协议,三菱有MC、倍福有ADS,然而没有统一性的接口协议。

金南瓜科技为适应每一家通讯,每一家设备商、MES和工厂等都需要针对每款产品开发相应的通讯接口。

OneConnectAPI为实现统一的接口,去适配每一家厂商的协议。为中国工控行业快速发展而贡献,每一家公司都需要重新制造轮子,这是非常浪费时间和金钱,同时也不能保证稳定性以及持续的维护。

我们采取高效的多线程处理方案,保证极其高效的读写性能,对电脑性能要求极其低,一直进行读写操作,CPU使用率不超过1%(Atom E3940下测试)。

用户可以在一台工控机上进行对上百台的PLC主动读写操作,我们在光伏行业大量应用和测试过。

我们在半导体行业深耕多年,积累大量的经验,实现功能的同时,也需要保证极其严格的稳定性,晶圆生成设备7*24小时不能出任何故障。

#pragma once

// 用于TCP/IP通讯

class CCommunication
{
public:
	CCommunication();
	virtual ~CCommunication();

	// 释放
	void Release();	
	void SetTimeout(int nMs);					// 设置超时
	void SetMode(bool bLongConnect);			// 长连接或者短连接模式
	void SetTcpIP(const char* pIP);				// socket参数
	void SetTcpPort(int nPort);					// socket参数
	int GetTimeout();
	
	CResult Connect();			// TCP连接
	void Disconnect();		// 断开通讯

	CResult SendSyncData(CMyString pSendData, CMyString& pRecvData);	// 数据发读
	
protected:

	// 数据接收
	void SetRecvComplete(int nSize);	// 告诉通讯,数据接收完成
	virtual void OnBeginRecv();								// 重写数据接收,开始接收数据,用于协议识别
	virtual void OnDataRecv(char* pData, int nSize) = 0;	// 重写数据接收,用于协议识别
	virtual void OnCloseConnect();							// 通讯关闭

private:
		
	void CloseConnect();	// TCP关闭连接

	void InitSockWSA();
	void ReleaseSockWSA();

	// 接收数据线程
	void CreateRecvThread();		// 创建接收线程
	void ExitRecvThread();			// 退出接收线程
	static void RunRecvThread(void* lp);
	void RecvHandle();
	bool IsExitThread();
	void OneRecvData();				// 一次接收数据
	void StartRecvData();			// 开始接收数据
	void StopRecvData();			

	// 数据读取,从缓存内读取
	CResult RecvData(CMyString& pRecvData);	
	

private:
	
	bool m_bLongConnect;		// 长连接		
	int m_nRecvTimeout;			// 接收超时

	// socket
	SOCKET m_hSock; 
	struct sockaddr_in m_pSA;
	vCritical m_syncLock;		// 每次只能单个tcp读写
	vCritical m_syncLockTcp;	// soekct操作

	HANDLE m_hRecvTimeoutEvent;		// 接收线程超时
	HANDLE m_hRecvExitFinish;		// 定时器线程退出完毕
	HANDLE m_hRecvExit;				// 接收退出
	HANDLE m_hRecvStartData;		// 开始接收数据

	CMyString m_pRecvData;		// 接收到的数据
	long m_nRecvCode;			// 读取错误代码

	// 临时缓
	int m_nSize;
	int m_nRecvSize;
	char m_pBuff[200];

};













#pragma once
#include "Communication.h"


class CFinsHandle : public CCommunication
{
public:
	CFinsHandle();
		
	CResult ReadMemoryData(int nAddr, int nSize, FINS_DATA_TYPE::ENUM nType, __int16* pData);
	CResult WriteMemoryData(int nAddr, int nSize, FINS_DATA_TYPE::ENUM nType, __int16* pData);
	
private:
	
	virtual void OnBeginRecv();							// 重写数据接收,开始接收数据
	virtual void OnDataRecv(char* pData, int nSize);	// 重写数据接收,用于协议识别
	virtual void OnCloseConnect();						// 通讯关闭

	// 检查答复数据是否错误
	CResult CheckReplyDataIsError(char* pData, int nSize);		// 检查答复数据是否错误

	long GetFinsNodeAddress();	// 获取fins节点地址		
	CResult EstablishCommunicationByFins();	// 建立Fins通讯
	
private:

	CMyString m_pRecvData;
	bool m_bEstablishCommunicationByFins;	// 与Fins建立通讯
	int m_nIpNode;	// IP节点

};
























#pragma once
#include "Export/InterfaceExport.h"
#include "FinsHandle.h"

class CFinsReadWrite : public CInterfaceExport
{
public:

	// 参数
	virtual CResult SetParament(const char* pName, const char* pValue); 
//	virtual void SetEventNotify();		// 事件通知

	// 初始化模块
	virtual CResult Connect(); 
	virtual CResult Disconnect(); 

	// 释放
	virtual void Release(); 
	
	// 读出
	virtual CResult Read(const char* pAddr, bool* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Read(const char* pAddr, char* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Read(const char* pAddr, __int16* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Read(const char* pAddr, __int32* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Read(const char* pAddr, __int64* pData, __int32 size, const char* pCtrlData); 

	// 写入
	virtual CResult Write(const char* pAddr, bool* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Write(const char* pAddr, char* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Write(const char* pAddr, __int16* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Write(const char* pAddr, __int32* pData, __int32 size, const char* pCtrlData); 
	virtual CResult Write(const char* pAddr, __int64* pData, __int32 size, const char* pCtrlData); 												
		
private:
	CResult ReadData(int nAddr, __int16* pData, int nSize, int nDataType);		// 读取2字节一次	
	CResult WriteData(int nAddr, __int16* pData, int nSize, int nDataType);	// 读取2字节一次
	void GetDataTypeAndAddr(string pAddr, int& nDataType, int& nAddr);		// 获取数据地址
	int GetDataArea(string pArea);		// 获取数据区

private:
	CFinsHandle m_pHandle;

};












































#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连接
CResult CCommunication::Connect()
{
	vLocker lock(&m_syncLockTcp);
	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 CResult(FINS_SOCKET_CONNECT_FAIL, "FINS TCP连接失败,请查看网络是否有问题");
			}	
		}
		else
		{
			return CResult(FINS_SOCKET_CREATE_FAIL, "FINS SOCKET创建失败");			
		}
	}

	return CResult(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);
}




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

	// 连接
	CResult rc = Connect();
	if(rc.nCode != 0)
	{
		return rc;
	}

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

	// 发送数据
	int res = send(m_hSock, pSendData.GetString(), pSendData.Size(), 0);
	if(res == SOCKET_ERROR)
	{
		CloseConnect();
		return CResult(FINS_SEND_FAIL, "FINS 发送数据失败,请查看网络是否正常");
	}
	
	// 数据读取
	rc = RecvData(pRecvData);

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

	return rc;
}



// 数据读取
// 带超时处理
CResult CCommunication::RecvData(CMyString& pRecvData)
{		
	CResult rc;
	StartRecvData();

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

	// 超时等其他错误
	else
	{
		StopRecvData();
		CloseConnect();

		rc.SetData(FINS_RECV_WAIT_TIMEOUT, "FINS 等待数据答复超时");
	}
	
	return rc;	
}


// 创建接收线程
// 
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()
{
	CloseConnect();
}
















#include "stdafx.h"
#include "FinsReadWrite.h"



// 参数
CResult CFinsReadWrite::SetParament(const char* pName, const char* pValue)
{
	if(pName == nullptr ||
		pValue == nullptr)
	{
		return CResult(FINS_PARAM_POINT_ENTPY, "FINS 参数指针为空");
	}

	string pNameString = pName;

	if (pNameString == API_PARAM_FINS_IP)
	{
		m_pHandle.SetTcpIP(pValue);
	}
	else if (pNameString == API_PARAM_FINS_PORT)
	{
		int nPort = ::atoi(pValue);
		m_pHandle.SetTcpPort(nPort);
	}
	else if (pNameString == API_PARAM_FINS_TIMEOUT)
	{
		int nTimerOut = ::atoi(pValue);
		m_pHandle.SetTimeout(nTimerOut);
	}

	return CResult(0);
}

// 初始化模块
CResult CFinsReadWrite::Connect()
{
	return m_pHandle.Connect();	 
}

CResult CFinsReadWrite::Disconnect()
{
	m_pHandle.Disconnect();	 
	return CResult(0);
}


// 释放
void CFinsReadWrite::Release()
{
	delete this;
}

// bool
CResult CFinsReadWrite::Read(const char* pAddr, bool* pData, __int32 nSize, const char* pCtrlData)
{
	return CResult(0);
	//	long nCode = 0;
	//	int nDataType;		// 数据类型
	//	int nAddr;			// 数据地址
	//
	//	GetDataTypeAndAddr(pAddr, nDataType, nAddr);		
	//	return ReadData(nAddr, (__int16*)pData, nSize * 2, nDataType);	
}

// 1字节			
CResult CFinsReadWrite::Read(const char* pAddr, char* pData, __int32 nSize, const char* pCtrlData)
{
	if (pAddr == nullptr ||
		pData == nullptr ||
		pCtrlData == nullptr)
	{
		return CResult(FINS_PARAM_POINT_ENTPY, "FINS 参数指针为空");
	}
		
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址
	GetDataTypeAndAddr(pAddr, nDataType, nAddr);	
	
	CResult rc;
	if(nSize % 2)
	{
		// 奇数处理
		int nReadSize = nSize;
		nReadSize++;

		__int16* pReadData = (__int16*)malloc(nReadSize);
		if (pReadData == nullptr)
		{
			return FINS_MALLOC_FAIL;
		}

		// 读取数据
		rc = ReadData(nAddr, pReadData, nReadSize / 2, nDataType);
		if(rc.nCode == 0)
		{
			memcpy(pData, pReadData, nSize);
		}
		free(pReadData);
	}
	else
	{
		rc = ReadData(nAddr, (__int16*)pData, nSize / 2, nDataType);
	}
	return rc;
}

// 2字节			
CResult CFinsReadWrite::Read(const char* pAddr, __int16* pData, __int32 nSize, const char* pCtrlData)
{
	long nCode = 0;
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址

	GetDataTypeAndAddr(pAddr, nDataType, nAddr);		
	return ReadData(nAddr, pData, nSize, nDataType);
}

// 4字节
CResult CFinsReadWrite::Read(const char* pAddr, __int32* pData, __int32 nSize, const char* pCtrlData)
{
	long nCode = 0;
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址

	GetDataTypeAndAddr(pAddr, nDataType, nAddr);		
	return ReadData(nAddr, (__int16*)pData, nSize * 2, nDataType);	
}

// 8字节
CResult CFinsReadWrite::Read(const char* pAddr, __int64* pData, __int32 nSize, const char* pCtrlData)
{
	long nCode = 0;
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址

	GetDataTypeAndAddr(pAddr, nDataType, nAddr);		
	return ReadData(nAddr, (__int16*)pData, nSize * 2, nDataType);	
}

// 读取2字节一次	
CResult CFinsReadWrite::ReadData(int nAddr, __int16* pData, int nSize, int nDataType)
{
	FINS_DATA_TYPE::ENUM nType = (FINS_DATA_TYPE::ENUM)nDataType;
	switch(nType)
	{
	case FINS_DATA_TYPE::DM:
	case FINS_DATA_TYPE::W:
	case FINS_DATA_TYPE::H:
		{
			return m_pHandle.ReadMemoryData(nAddr, nSize, nType, pData);
		}
		break;
	}

	// 没有对应地址区域
	char pBuff[300] = {0};
	sprintf(pBuff, "FINS 没有对应的数据地址区域:地址", nAddr);	
	return CResult(FINS_NOT_DATA_AREA, pBuff);
}

// bool
CResult CFinsReadWrite::Write(const char* pAddr, bool* pData, __int32 nSize, const char* pCtrlData)
{
	return CResult(0);
	//	if (pAddr == nullptr ||
	//		pData == nullptr ||
	//		pCtrlData == nullptr)
	//	{
	//		return CResult(FINS_PARAM_POINT_ENTPY, "FINS 参数指针为空");
	//	}
	//
	//	int nDataType;		// 数据类型
	//	int nAddr;			// 数据地址
	//
	//	// 获取PLC区域和地址
	//	GetDataTypeAndAddr(pAddr, nDataType, nAddr);	
	//	return WriteData(nAddr, pData, nSize, nDataType);	
}	

// 1字节
CResult CFinsReadWrite::Write(const char* pAddr, char* pData, __int32 nSize, const char* pCtrlData)
{
	if (pAddr == nullptr ||
		pData == nullptr ||
		pCtrlData == nullptr)
	{
		return CResult(FINS_PARAM_POINT_ENTPY, "FINS 参数指针为空");
	}

	CResult rc;
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址

	// 获取PLC区域和地址
	GetDataTypeAndAddr(pAddr, nDataType, nAddr);

	if(nSize % 2)
	{
		// 奇数处理
		int nReadSize = nSize;
		nReadSize++;

		__int8* pReadData = (__int8*)malloc(nReadSize);
		if (pReadData == nullptr)
		{
			return FINS_MALLOC_FAIL;
		}

		memset(pReadData, 0, nReadSize);
		memcpy(pReadData, pData, nSize);

		// 读取数据
		rc = WriteData(nAddr, (__int16*)pReadData, nReadSize / 2, nDataType);		
		free(pReadData);
	}
	else
	{
		rc = WriteData(nAddr, (__int16*)pData, nSize / 2, nDataType);
	}
	return rc;
}	

// 2字节
CResult CFinsReadWrite::Write(const char* pAddr, __int16* pData, __int32 nSize, const char* pCtrlData)
{
	if (pAddr == nullptr ||
		pData == nullptr ||
		pCtrlData == nullptr)
	{
		return CResult(FINS_PARAM_POINT_ENTPY, "FINS 参数指针为空");
	}
		
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址

	// 获取PLC区域和地址
	GetDataTypeAndAddr(pAddr, nDataType, nAddr);	
	return WriteData(nAddr, pData, nSize, nDataType);	
}	

// 4字节	
CResult CFinsReadWrite::Write(const char* pAddr, __int32* pData, __int32 nSize, const char* pCtrlData)
{
	if (pAddr == nullptr ||
		pData == nullptr ||
		pCtrlData == nullptr)
	{
		return CResult(FINS_PARAM_POINT_ENTPY, "FINS 参数指针为空");
	}

	long nCode = 0;
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址

	// 获取PLC区域和地址
	GetDataTypeAndAddr(pAddr, nDataType, nAddr);	
	return WriteData(nAddr, (__int16*)pData, nSize * 2, nDataType);	
}									

// 8字节	
CResult CFinsReadWrite::Write(const char* pAddr, __int64* pData, __int32 nSize, const char* pCtrlData)
{
	if (pAddr == nullptr ||
		pData == nullptr ||
		pCtrlData == nullptr)
	{
		return CResult(FINS_PARAM_POINT_ENTPY, "FINS 参数指针为空");
	}
	
	int nDataType;		// 数据类型
	int nAddr;			// 数据地址

	// 获取PLC区域和地址
	GetDataTypeAndAddr(pAddr, nDataType, nAddr);
	return WriteData(nAddr, (__int16*)pData, nSize * 4, nDataType);	
}									



// 读取2字节一次	
CResult CFinsReadWrite::WriteData(int nAddr, __int16* pData, int nSize, int nDataType)
{
	FINS_DATA_TYPE::ENUM nType = (FINS_DATA_TYPE::ENUM)nDataType;
	switch(nType)
	{
	case FINS_DATA_TYPE::DM:
	case FINS_DATA_TYPE::W:
	case FINS_DATA_TYPE::H:
		{
			return m_pHandle.WriteMemoryData(nAddr, nSize, nType, pData);
		}
		break;
	}

	// 没有对应地址区域
	char pBuff[300] = {0};
	sprintf(pBuff, "FINS 没有对应的数据地址区域:地址", nAddr);	
	return CResult(FINS_NOT_DATA_AREA, pBuff);
}


// 获取数据地址
void CFinsReadWrite::GetDataTypeAndAddr(string pAddr, int& nDataType, int& nAddr)
{
	unsigned int i = 0;
	for (; i < pAddr.size(); i++)
	{
		if(pAddr[i] >= '0' && pAddr[i] <= '9')
		{
			break;
		}
	}

	// 数据类型
	string pType = pAddr.substr(0, i);
	nDataType = GetDataArea(pType);

	// 数据地址
	string pStartAddr = pAddr.substr(i, pAddr.size() - i);
	nAddr = ::atol(pStartAddr.c_str());
}


// 获取数据区
int CFinsReadWrite::GetDataArea(string pArea)
{
	int nDataType = 0;

	if (pArea == "DM") { nDataType = FINS_DATA_TYPE::DM; }
	else if (pArea == "D") { nDataType = FINS_DATA_TYPE::DM; }
	else if (pArea == "W") { nDataType = FINS_DATA_TYPE::W; }
	else if (pArea == "H") { nDataType = FINS_DATA_TYPE::H; }
	else { nDataType = FINS_DATA_TYPE::ERR; }

	return nDataType;
}




























评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值