zoukankan      html  css  js  c++  java
  • 枚举目标机器已注册的OPC服务器

    1、基本原理

    OPC服务器是以COM组件的形式存在和管理的,OPC规范着诸如DA Server、HDA Server和A&E Server等不同类型的服务器,相同类型的服务器随着时间推移,又会衍生出升级版本,比如DA1.0、2.0乃至3.0。这些服务器类型是该如何区分呢?COM组件的分类技术解决了这个问题。(参见:理解COM编程中的“种类”(Category)概念

    在OPC规范中,明确定义不同种类服务器的CATID,CATID实质上就是GUID。比如在《OPC Data Access Custom Interface Specification 2.05A》 中有如下定义:

    5.1 Component Categories
    The OPC Data Access Interface defines the following Component Catagories. Listed below are the CATIDs, Descriptors and Symbolic Equates to be used for Data Access.
    "OPC Data Access Servers Version 1.0"
    CATID_OPCDAServer10 = {63D5F430-CFE4-11d1-B2C8-0060083BA1FB}
    "OPC Data Access Servers Version 2.0"
    CATID_OPCDAServer20 = {63D5F432-CFE4-11d1-B2C8-0060083BA1FB}
    It

    上述定义分别指明了DA2.0和1.0的CATID。那么,有些客户端程序是针对DA2.0做的,有些是针对DA1.0做的,还有些是针对HDA做的,但只要知道CATID,客户端就可以枚举出它可以连接到的OPC服务器类型。

    实际上,不同厂商的OPC服务器的注册,就是在目标机器上把OPC服务器注册到类别中,而要删除服务器,也需要从类别中移除注册信息。

    一些常见的CATID如下:

        [ComImport, InterfaceType((short) 1), Guid("63D5F430-CFE4-11D1-B2C8-0060083BA1FB")]
        public interface CATID_OPCDAServer10
        {
        }
        [ComImport, Guid("63D5F432-CFE4-11D1-B2C8-0060083BA1FB"), InterfaceType((short) 1)]
        public interface CATID_OPCDAServer20
        {
        }
        [ComImport, Guid("CC603642-66D7-48F1-B69A-B625E73652D7"), InterfaceType((short) 1)]
        public interface CATID_OPCDAServer30
        {
        }
        [ComImport, InterfaceType((short) 1), Guid("3098EDA4-A006-48B2-A27F-247453959408")]
        public interface CATID_XMLDAServer10
        {
        }
    OPCSERVER CATID

    其它的可以到相应的规范文档中查询。

    2、IOPCServerList接口

    OPC规范提供了接口IOPCServerList用于枚举特定种类的服务器,该接口的EnumClassesOfCategories方法传入的参数就是上述CATID。

    安装了OPC基金会的OPC核心组件,会在“C:\Program Files\OPC Foundation\Core Components 2.00\Include”路径下找到OPCEnum.h头文件,它包含了IOPCServerList接口的定义:

     MIDL_INTERFACE("13486D50-4821-11D2-A494-3CB306C10000")
        IOPCServerList : public IUnknown
        {
        public:
            virtual HRESULT __stdcall EnumClassesOfCategories( 
                /* [in] */ unsigned long cImplemented,
                /* [in] */ GUID __RPC_FAR *rgcatidImpl,
                /* [in] */ unsigned long cRequired,
                /* [in] */ GUID __RPC_FAR *rgcatidReq,
                /* [out] */ IEnumGUID __RPC_FAR *__RPC_FAR *ppenumClsid) = 0;
            
            virtual HRESULT __stdcall GetClassDetails( 
                /* [in] */ GUID __RPC_FAR *clsid,
                /* [out] */ LPWSTR __RPC_FAR *ppszProgID,
                /* [out] */ LPWSTR __RPC_FAR *ppszUserType) = 0;
            
            virtual HRESULT __stdcall CLSIDFromProgID( 
                /* [in] */ LPWSTR szProgId,
                /* [out] */ GUID __RPC_FAR *clsid) = 0;
            
        };
    OPCEnum.h

    如果在.NET环境下引用该接口(前提是使用OPC基金会提供的OpcRcw.comn.dll),应如下定义:

        [ComImport, InterfaceType((short) 1), Guid("13486D50-4821-11D2-A494-3CB306C10000")]
        public interface IOPCServerList
        {
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            void EnumClassesOfCategories([In] int cImplemented,
                                         [In,
                                          MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct,
                                             SizeParamIndex = 0)] Guid[] rgcatidImpl, [In] int cRequired,
                                         [In,
                                          MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Struct,
                                             SizeParamIndex = 2)] Guid[] rgcatidReq,
                                         [MarshalAs(UnmanagedType.Interface)] out IEnumGUID ppenumClsid);
    
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            void GetClassDetails([In] ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)] out string ppszProgID,
                                 [MarshalAs(UnmanagedType.LPWStr)] out string ppszUserType);
    
            [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
            void CLSIDFromProgID([In, MarshalAs(UnmanagedType.LPWStr)] string szProgId, out Guid clsid);
        }
    OpcRcw.comn.IOPCServerList

     3、OPCENUM.exe服务程序

    OpcEnum.exe是OPC基金会提供的实现了IOPCServerList接口的Windows 服务程序。在我当前电脑,它的路径位于C:\Windows\System32下。

    下载:OPC Proxy Stub (包含OPCENUM.exe)

    4、使用OPCENUM.exe枚举服务器

    下面是要用到的在opcenum_i.c中定义的两个GUID:

    const IID IID_IOPCServerList = {0x13486D50,0x4821,0x11D2,{0xA4,0x94,0x3C,0xB3,0x06,0xC1,0x00,0x00}};
    
    const CLSID CLSID_OpcServerList = {0x13486D51,0x4821,0x11D2,{0xA4,0x94,0x3C,0xB3,0x06,0xC1,0x00,0x00}};
    opcenum_i.c

    这两个GUID作为参数传入CoCreateInstance()方法,用来获取可以浏览OPC服务器的对象实例。调用对象的EnumClassesOfCategories(),传入服务器的CATID,即可达成目的。

    主要代码如下:

    //main.cpp
    //************************************************************************************************
    //浏览本地OPC服务器
    //************************************************************************************************
    
    #define _WIN32_DCOM  // Needed in order to call CoInitializeEx()
    
    #include <iostream>
    #include <objbase.h>
    #include <comdef.h>
    
    #import "C:\Windows\system32\OpcEnum.exe" no_namespace    //根据OpcEnum.exe更改
    #include "opcda.h"
    #include "opcenum_i.c"
    
    //--------------------------------------------------------------------------------------------------------------
    //版本号----可以从规范中查到
    static const CLSID CATID_OPCDAServer10 = 
    { 0x63d5f430, 0xcfe4, 0x11d1, { 0xb2, 0xc8, 0x0, 0x60, 0x8, 0x3b, 0xa1, 0xfb } };
    // {63D5F430-CFE4-11d1-B2C8-0060083BA1FB}
    
    static const CLSID CATID_OPCDAServer20 = 
    { 0x63d5f432, 0xcfe4, 0x11d1, { 0xb2, 0xc8, 0x0, 0x60, 0x8, 0x3b, 0xa1, 0xfb } };
    // {63D5F432-CFE4-11d1-B2C8-0060083BA1FB}
    
    //[uuid(CC603642-66D7-48f1-B69A-B625E73652D7)] interface CATID_OPCDAServer30
    //--------------------------------------------------------------------------------------------------------------
    
    void OPCServerList();
    
    int main(int argc, char* argv[])
    {
      HRESULT hr;
      int n_return = 0;
    
      try
      {
        // Initialize COM
        if(  
          FAILED( hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) )
        )
          _com_issue_error(hr);
      }
      catch(_com_error e)
      {
        std::cout << "ERROR(" << e.Error() << "):  ";
        if( e.Description().length() > 0)
          std::cout << (TCHAR*)e.Description() << std::endl;
        else
          std::cout << e.ErrorMessage() << std::endl;
        n_return = 1;
      }
    
      OPCServerList();
    
      // Uninitialize COM
      CoUninitialize();
      std::cin.get();
      return 0;
    }
    
    //使用CoCreateInstance
    void OPCServerList()
    {
        CLSID clsid;
        clsid = CLSID_OpcServerList;    //在opcenum_i.c中定义
        IOPCServerList *gpOPC = NULL;
    
        DWORD clsctx;
        clsctx = CLSCTX_LOCAL_SERVER;    //本地服务
        IID IIDOPCServerList=IID_IOPCServerList;    //在opcenum_i.c中定义
        // 创建OPC服务器的浏览器对象-----
        HRESULT hr = CoCreateInstance(clsid, NULL, clsctx,IIDOPCServerList ,(void**)&gpOPC);    
        
        // 查询OPC DA 2.0 组件目录接口指针
        CLSID catid;    
        catid=CATID_OPCDAServer20;    //= CATID_OPCDAServer20;    //OPC数据访问服务器2.0组件目录
        IOPCEnumGUID *pEnumGUID;
    
        hr = gpOPC->EnumClassesOfCategories( 1, &catid, 1, &catid, (IEnumGUID**)&pEnumGUID); 
        if(S_OK!=hr)
            return;
    
        //获得支持OPC DA2.0数据服务器的CLSID
        unsigned long c;
        while (S_OK == pEnumGUID->Next(1, &clsid, &c)) 
        {
            LPOLESTR pszProgID;
            LPOLESTR pszUserType;
            hr = gpOPC->GetClassDetails(&clsid, &pszProgID, &pszUserType);
            //打印OPC数据服务器的有关信息
            printf("ProgID = %ls, UserType = %ls\n", pszProgID, pszUserType);
            CoTaskMemFree(pszProgID);
            CoTaskMemFree(pszUserType);
        }
        
        //释放接口
        if(gpOPC)
            gpOPC->Release();
    }
    Main.cpp

    下载源码(VC/VS2010)

  • 相关阅读:
    Redis string
    java 是 传值还是传址 Pass-by-value or Pass-by-reference
    IDEA 适用技巧
    测试 MD
    pyqt5 学习总结
    win10 安装anaconda 无法使用pip 报错缺少SSL模块
    Hadoop datanode无法启动
    Ansible 安装jdk
    java 安装后 不能 java javac 说找不到命令 -bash: javac: command not found
    如何去掉MapReduce输出的默认分隔符
  • 原文地址:https://www.cnblogs.com/gmth/p/2988847.html
Copyright © 2011-2022 走看看