OPC技术是通常是用于PLC和上位机通讯的一个基于COM的一个通讯组件。比如某个项目是用西门子系列的PLC控制的自动化系统通过西门子的中间件通常会安装S7-200 PC Access或者SimaticNet的OPC服务端软件。这两个软件的目的就是在上位机的系统中搭建了一个OPC Server并提供访问可以编程实现遵循OPC技术使上位机与PLC通讯的功能。
此外,需要强调一点,OPC并不是西门子的技术,它是一个标准,凡是遵循OPC技术的PLC都能共通过其标准与下位机通讯 。
几个关于OPC的概念:
同步读:在调用OPC的接口函数时实时的将数据(组)通过出口参数传出
同步写:在调用OPC的接口函数时实时的将数据写入寄存器
异步读:调用OPC接口不会直接写入PLC寄存器,而是在注册的回调函数中拿到数据的相关信息
异步写:也是在回调函数中将数据写入
组的概念:
组中可以包含项,是多个项的一个集合
组是和OPC提供的IO接口绑定的,OPC的IO接口是用于读写数据
项的概念:
项其实就是对于了PLC上的对应地址,每种OPC服务器根据厂商不同定义的格式不同
关于封装的类
情况说明(这个类是本人在开发上位机软件与西门子S200系列PLC通信是所编写的 OPC服务器是SimaticNet)
几个疑惑点说明:
OPC服务器提供了几个接口的头文件需要在项目中添加
需要加到工程中的文件:
opc.h
Pre_OPC.h
Pre_OPC.cpp
以下几个文件不用添加到工程中但需要放在工作目录下
opcerror.h
opcda_i.c
opcda.h
opccomn_i.c
opccomn.h
相关文件OPC实现文件(c++)
封装的OPC 操作类
#ifndef OPCRES_H_
#define OPCRES_H_
#include "Pre_OPC.h"
#define LOCALE_ID 0x409// Code 0x409 = ENGLISH
#define ASYNC 0x01
#define SYNC 0x10
class OPCRes{
public:
OPCRes(int SyncItemNum,int AsyncItemNum,IOPCServer *pIOPCServer,HWND hWnd);
~OPCRes();
void UninitRes();
void OPC_AddGroup(LPCWSTR SyncGroupName,LPCWSTR AsyncGroupName);
void OPC_AddItem(BOOL IsActive);
void OPC_SetItem(DWORD flag,int Order,LPWSTR in_ID,CString Name);
void OPC_ReadItem(DWORD flag);
int OPC_ReadItem(DWORD flag,CString Name);
int OPC_WriteItem(DWORD flag,int Order,int in_Value);
void OPC_Subscribe(BOOL IsActiveCheck);
DWORD GetAsyncCount();
static void InitCOM(IOPCServer* &pIOPCServer,LPWSTR CLSID_ID);//传递IOPCServer指针类型的引用将外部声明的变量传进来
static void UninitCOM(IOPCServer* &pIOPCServer);
private:
OPCHANDLE m_GrpSrvHandle;
IOPCServer *m_pIOPCServer;
IOPCSyncIO *m_pIOPCSyncIO_S;//用于同步访问数据项的同步读写
IOPCSyncIO *m_pIOPCSyncIO_A;//用于异步访问数据项的同步写入
IOPCAsyncIO2 *m_pIOPCAsyncIO2;//用于异步访问数据项的异步读取
IOPCGroupStateMgt *m_pIOPCGroupStateMgt;
IOPCItemMgt *m_pIOPCItemMgt[2]; //数据项状态指针1异步0同步
OPCITEMRESULT *m_pAsyncItemResult;//异步数据项返回信息
OPCITEMDEF *m_AsyncItem; //异步数据项指针
CString *m_AsyncItemName; //异步数据项的指定名字
HRESULT *m_pAsyncErrors; //异步数据错误信息
DWORD m_AsyncItemCount; //异步数据项的个数
OPCITEMRESULT *m_pSyncItemResult; //同步数据项返回信息
OPCITEMDEF *m_SyncItem; //同步数据项指针
CString *m_SyncItemName; //同步数据项的指定名字
HRESULT *m_pSyncErrors; //同步数据错误信息
DWORD m_SyncItemCount; //同步数据项的个数
DWORD m_flag; //标记当前对象存在那种类型的数据项
DWORD m_dwAdvise; //用于OPC Server返回的回调接口标识
public:
BOOL m_ActiveCheck;
int *m_ReadVal_A; //存放异步数据项的值
CString *m_ReadQu_A; //存放异步数据项的品质标签
CString *m_ReadTs_A; //存放异步数据项的时间戳
int *m_ReadVal_S; //存放同步数据项的值
CString *m_ReadQu_S; //存放同步数据项的品质标签
CString *m_ReadTs_S; //存放同步数据项的时间戳
};
#endif //OPCRES_H_
实现
#include "stdafx.h"
#include "callback.h"
#include "OPCRes.h"
CComModule _Module;//必须定义此全局变量否则无法使用COM组件
/*****************************************************
函数名称:构造函数
参数说明:
in 1、指定同步组的数据项个数
in 2、指定异步组的数据项个数
in 3、传进窗口类定义的OPC服务接口指针
******************************************************/
OPCRes::OPCRes(int SyncNum,int AsyncNum,IOPCServer *pIOPCServer)
:m_SyncItemCount(SyncNum),m_AsyncItemCount(AsyncNum),m_pIOPCServer(pIOPCServer),m_ActiveCheck(FALSE),m_flag(0x00)
{
if(!(AsyncNum+SyncNum))
{
AfxMessageBox("必须设置监控的数据项",MB_ICONERROR);
ExitProcess(0);
}
if( SyncNum>0 )
{
m_flag|=SYNC;
m_SyncItem=new OPCITEMDEF[SyncNum];//同步的数据项数组
m_SyncItemName=new CString[SyncNum];
m_ReadVal_S=new int[SyncNum];
m_ReadQu_S=new CString[SyncNum];
m_ReadTs_S=new CString[SyncNum];
}
if( AsyncNum>0 )
{
m_flag|=ASYNC;
m_AsyncItem=new OPCITEMDEF[AsyncNum];//异步的数据项数组
m_AsyncItemName=new CString[AsyncNum];
m_ReadVal_A=new int[AsyncNum];
m_ReadQu_A=new CString[AsyncNum];
m_ReadTs_A=new CString[AsyncNum];
}
m_pAsyncItemResult = NULL;
m_pSyncItemResult = NULL;
}
/*****************************************************
函数名称:析构函数
说明: 释放OPC资源
******************************************************/
OPCRes::~OPCRes()
{
if( (m_flag&SYNC) == SYNC)
{
delete[] m_SyncItem;
delete[] m_ReadVal_S;
delete[] m_ReadQu_S;
delete[] m_ReadTs_S;
delete[] m_SyncItemName;
}
if( (m_flag&ASYNC) == ASYNC)
{
delete[] m_AsyncItem;
delete[] m_ReadVal_A;
delete[] m_ReadQu_A;
delete[] m_ReadTs_A;
delete[] m_AsyncItemName;
}
}
/*****************************************************
函数名称:增加数据组
参数说明:
in 1、指定同步组名
in 2、指定异步组名
返回值: 无返回值
情况说明: 每个对象各有一个同步组一个异步组
******************************************************/
void OPCRes::OPC_AddGroup(LPCWSTR SyncGroupName,LPCWSTR AsyncGroupName)
{
HRESULT r1; //接收AddGroup函数的返回值用于判断
long TimeBias = 0; //in 与标准时间的校正值
float PercentDeadband = 0.0; //in 要舍弃的数据
DWORD RevisedUpdateRate; //out 服务器的数据刷新率
CString szErrorText; //记录输出信息
//创建同步的数据组
if( (m_flag & SYNC) == SYNC)
{
//添加组对象并查询IOPCItemMgt接口
r1=m_pIOPCServer->AddGroup(SyncGroupName,FALSE,500,1,&TimeBias,&PercentDeadband,LOCALE_ID,\
&m_GrpSrvHandle,&RevisedUpdateRate,IID_IOPCItemMgt,(LPUNKNOWN*)&m_pIOPCItemMgt[0]);
if(r1 == OPC_S_UNSUPPORTEDRATE)
{
szErrorText.Format ("同步数据组的实际刷新率为%d,与请求值不同",RevisedUpdateRate );
AfxMessageBox (szErrorText);
}
else if (FAILED(r1))
{
AfxMessageBox("无法创建同步数据组到服务器", MB_OK+MB_ICONERROR);
m_pIOPCServer->Release();
m_pIOPCServer = NULL;
CoUninitialize();
return;
}
}
//创建异步的数据组
if( (m_flag & ASYNC) == ASYNC)
{
r1=m_pIOPCServer->AddGroup(AsyncGroupName,FALSE,500,1,&TimeBias,&PercentDeadband,LOCALE_ID,\
&m_GrpSrvHandle,&RevisedUpdateRate,IID_IOPCItemMgt,(LPUNKNOWN*)&m_pIOPCItemMgt[1]);