每家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;
}