OneApiConnect(二)三菱MC通讯协议源代码,高效稳定

  • Post author:
  • Post category:其他


每家PLC厂商都有自己的通讯协议,三菱有MC、倍福有ADS、modbus tcp/rtu,然而没有统一性的接口协议。

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

我们为大家提供高效的开源基础接口,高效稳定的接口让MES同时对接更多的设备数量,降低运营成本,降低服务器使用率而产生的稳定性问题。

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

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

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

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


金南瓜科技


下边是我们的接口库以及源代码。

下载地址

软件及资料书


https://gitee.com/secsgem/one-api-connect/tree/master/

https://github.com/SECSGEM/Fins

QQqun:30806722

#pragma once


//
//	网站 www.semisecs.com
//	广州金南瓜科技有限公司保留版权
// 



#ifndef __MC_MEMEORY_DATA__
#define __MC_MEMEORY_DATA__

// 内存数据类型:软元件
namespace MC_DATA_TYPE
{
	enum ENUM
	{
		ERR = 0x00,		// 错误
		D = 0xA8,		// 数据寄存器
		W = 0xB4,		// 链接寄存器		
	};
}

// CPU类型
namespace MELSEC_CPU_TYPE
{
	enum ENUM
	{
		Other = 0,	// 其他如FX5U
		L,			// L CPU
		Q,			// Q CPU
		QnA,		// QnA CPU		
	};
}

#endif


// 三菱MC协议
class CMelsecCommunicationExport
{
public:
	// 初始化
	virtual __int32 Init()=0; 

	// 释放
	virtual void Release()=0; 

public:

	virtual void SetParam(const char* pType, const char* pValue)=0;					// 参数	

	// 读数据	
	virtual __int32 ReadByteData(__int32 nAddr, __int32 nSize, __int8* pData, __int32 nDataType)=0;		// 1字节			
	virtual __int32 ReadShortData(__int32 nAddr, __int32 nSize, __int16* pData, __int32 nDataType)=0;	// 2字节			
	virtual __int32 ReadIntData(__int32 nAddr, __int32 nSize, __int32* pData, __int32 nDataType)=0;		// 4字节

	// 写数据
	virtual __int32 WriteByteData(__int32 nAddr, __int32 nSize, __int8* pData, __int32 nDataType)=0;		// 1字节
	virtual __int32 WriteShortData(__int32 nAddr, __int32 nSize, __int16* pData, __int32 nDataType)=0;		// 2字节
	virtual __int32 WriteIntData(__int32 nAddr, __int32 nSize, __int32* pData, __int32 nDataType)=0;		// 4字节										
	
};

```cpp
#include "stdafx.h"
#include "McHandle.h"


//
//	网站 www.semisecs.com
//	广州金南瓜科技有限公司保留版权
// 



CMcHandle::CMcHandle()
{
	SetFrameType(MC_3E_FRAME);		
	m_nMaxOneRead = 32;
	m_nMaxOneWrite = 10;
}


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

// 重写数据接收,用于协议识别
void CMcHandle::OnDataRecv(char* pData, int nSize)
{
	m_pRecvData.Append(pData, nSize);
	if (m_pRecvData.Size() >= MC_Q_HEAD_SIZE_3E + MC_HEAD_SIZE_3E)
	{
		int nAllSize = GetPageSizeBy3E(m_pRecvData.GetString());
		if (m_pRecvData.Size() >= nAllSize)
		{
			SetRecvComplete(nAllSize);
		}
	}
}

// 3E、E4
void CMcHandle::SetFrameType(int nType)
{
	m_nFrameType = nType;
}

// 设置CPU类型
void CMcHandle::SetCpuType(MELSEC_CPU_TYPE::ENUM nCpuType)
{
	m_nCpuType = nCpuType;
}

// 获取包的总长度
int CMcHandle::GetPageSizeBy3E(char* pData)
{
	int nLow = pData[7];
	int nHight = pData[8];

	// 高位
	int nSize = nHight;
	nSize =  nHight << 8;

	// 低位
	nSize = nSize | (nLow & 0xFF);

	// 计算包总长度
	nSize = nSize + MC_Q_HEAD_SIZE_3E;

	return nSize;
}



// 读取缓冲存储	器数据
long CMcHandle::ReadBufferMemoryHandle(int nAddr, int nSize)
{
	CMyString pSendData;
	
	// 获取内存
	int nAllSize;
	nAllSize  = MC_HEAD_SIZE_3E;
	nAllSize += MC_Q_HEAD_SIZE_3E;
	nAllSize += MC_READ_BUFFER_MEMORY_SIZE; 
	pSendData.SetSize(nAllSize);
	char* pBuff = pSendData.GetString();

	// 头部信息
	MC_3E_HEAD pHeadr;
	pHeadr.nData = 0x0050;		// 发送的头部代码
	pHeadr.GetData(pBuff);

	// Q部分
	MC_3E_Q_HEAD pQHeader;
	pQHeader.SetDataSize(MC_READ_BUFFER_MEMORY_SIZE);
	pQHeader.SetTimeout(GetTimeout());
	pQHeader.GetData(pBuff + MC_HEAD_SIZE_3E);

	// 数据部分
	MC_READ_BUFFER_MEMORY pMemory;
	pMemory.nAddr = nAddr;
	pMemory.nSize = nSize;
	pMemory.GetData(pBuff + MC_HEAD_SIZE_3E + MC_Q_HEAD_SIZE_3E);

	// 发送数据
	CMyString pRecvData;
	return SendSyncData(pSendData, pRecvData);
}


long CMcHandle::ReadMemoryData(int nAddr, int nSize, MC_DATA_TYPE::ENUM nType,
	__int16* pData)
{
	long nCode = 0;

	// 读取一次
	nCode = ReadMemoryDataHandle(nAddr, nSize, nType, pData);
	return nCode;
}


// 读取内存、寄存器数据
long CMcHandle::ReadMemoryDataHandle(int nAddr, int nSize, MC_DATA_TYPE::ENUM nType,
	__int16* pData)
{	
	CMyString pSendData;
	
	// 获取发送内存
	int nAllSize;
	nAllSize  = MC_HEAD_SIZE_3E;
	nAllSize += MC_Q_HEAD_SIZE_3E;
	nAllSize += MC_READ_MEMORY_SIZE; 
	pSendData.SetSize(nAllSize);
	char* pBuff = pSendData.GetString();

	// 头部信息
	MC_3E_HEAD pHeadr;
	pHeadr.nData = 0x0050;		// 发送的头部代码
	pHeadr.GetData(pBuff);

	// Q部分
	MC_3E_Q_HEAD pQHeader;
	pQHeader.SetDataSize(MC_READ_MEMORY_SIZE);
	pQHeader.SetTimeout(GetTimeout());
	pQHeader.GetData(pBuff + MC_HEAD_SIZE_3E);

	// 数据部分
	MC_READ_MEMORY pMemory;
	pMemory.nCmd = 0x0401;
	pMemory.nSubCmd = 0x00;
	pMemory.nAddr = nAddr;
	pMemory.nSize = nSize;
	pMemory.nMemoryCode = nType;
	pMemory.GetData(pBuff + MC_HEAD_SIZE_3E + MC_Q_HEAD_SIZE_3E);

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

		if (pReplyData.GetReplyDataSize() == nSize)
		{
			memcpy(pData, pReplyData.GetReplyData(), nSize);
		}
		else
		{
			// 读取到的数据和请求的数据不一致
		}

	}
	return nCode;
}




// 检查答复数据是否错误
long CMcHandle::CheckReplyDataIsError(char* pData, int nSize)
{
	// 获取发送内存
	int nMinSize;
	nMinSize  = MC_HEAD_SIZE_3E;
	nMinSize += MC_Q_HEAD_SIZE_3E;

	if (nSize < nMinSize)
	{
		// 答复数据长度太短,不满足
		return MC_REPLY_DATA_TOO_SHORT;
	}

	// 答复的头部		
	MC_3E_HEAD pHeadr;
	pHeadr.SetData(pData);
	if(pHeadr.nData != 0x00D0)
	{
		return MC_REPLY_HEAD_DATA_ERROR;
	}
	
	// 答复的Q头
	MC_3E_Q_HEAD_REPLY pQHeadReply;
	pQHeadReply.SetDataSize(pData + MC_HEAD_SIZE_3E);
	if(pQHeadReply.IsErrorData())
	{
		return MC_REPLY_HEAD_DATA_ERROR;
	}
	if(pQHeadReply.IsAbnormal())
	{
		return MC_REPLY_Q_HEAD_DATA_ABNORMAL;
	}

	return 0;
}



long CMcHandle::WriteMemoryData(int nAddr, int nSize, MC_DATA_TYPE::ENUM nType,
	__int16* pData)
{
	long nCode = 0;

	// 读取一次
	if (nCode = WriteMemoryDataHandle(nAddr, nSize, nType, pData))
	{
		return nCode;
	}
	return nCode;
}


long CMcHandle::WriteMemoryDataHandle(int nAddr, int nSize, MC_DATA_TYPE::ENUM nType, 
	__int16* pData)
{
	CMyString pSendData;

	// 数据字节长度
	int nDataByte = nSize * 2;
	nDataByte += MC_WRITE_MEMORY_FIX_SIZE;

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

	// 头部信息
	MC_3E_HEAD pHeadr;
	pHeadr.nData = 0x0050;		// 发送的头部代码
	pHeadr.GetData(pBuff);

	// Q部分
	MC_3E_Q_HEAD pQHeader;
	pQHeader.SetDataSize(nDataByte);
	pQHeader.SetTimeout(GetTimeout());
	pQHeader.GetData(pBuff + MC_HEAD_SIZE_3E);

	// 数据部分
	MC_WRITE_MEMORY pMemory;
	pMemory.nCmd = 0x1401;
	pMemory.nSubCmd = 0x00;
	pMemory.nAddr = nAddr;
	pMemory.nSize = nSize;
	pMemory.nMemoryCode = nType;
	pMemory.pData = (char*)pData;
	pMemory.GetData(pBuff + MC_HEAD_SIZE_3E + MC_Q_HEAD_SIZE_3E);

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















版权声明:本文为weixin_42113317原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。