【问题标题】:Implementing an OPC DA client from scratch从头开始实施 OPC DA 客户端
【发布时间】:2013-07-24 15:06:30
【问题描述】:

我想在不使用任何第三方的情况下从头开始实现我自己的 OPC DA 客户端(版本 2.02、2.05a、3.00)。此外,我想利用 OPCEnum.exe 服务来获取已安装 OPC 服务器的列表。是否有任何类型的文档详细说明了实现 OPC 客户端的过程?

【问题讨论】:

  • 你可以从 opcfoundation.org 获得规范,然后你需要了解 COM 才能实现接口。我还没有看到任何关于这方面的教程,而且大多数示例程序都依赖于一些工具包,因为使用 COM 可能会很痛苦。
  • 谢谢,但问题是并非所有规范都是免费的,我的意思是,我已注册但我不是会员,所以如果我想获得 2.05a 或 3.00 OPC DA 规范,我不能。非会员只能使用 2.02 规范。
  • 好吧,您在问题中没有提供太多信息,因此很难回答,例如如果您提及您的目标是哪个版本的 OPCDA,那就太好了。
  • 我已经编辑了帖子,我指的是版本 2.02、2.05a 和 3.00 OPC DA。
  • 对于 c# 客户端,您可以使用 opc Foundation .net api(免费)。对于c++客户端,做好噩梦的准备,你需要精通COM/OLE/ATL...

标签: opc


【解决方案1】:

我有一个 c# 实现,但实际上很难把它放在这里。我将尝试总结所需的步骤。

大多数情况下,您需要从 Opcfoundation.org 免费下载 OPC Core Components Redistributable 包中的 OpcRcw.Comn.dll 和 OpcRcw.Da.dll。安装后,文件位于 C:\Windows\assembly\GAC_MSIL。在您的项目中创建一个引用。

关于编码,这是你应该做的(你要实现三个对象,Server、Group和Item):

让我们从服务器开始:

 Type typeofOPCserver = Type.GetTypeFromProgID(serverName, computerName, true);
 m_opcServer = (IOPCServer)Activator.CreateInstance(typeofOPCserver);
 m_opcCommon = (IOPCCommon)m_opcServer;
 IConnectionPointContainer icpc = (IConnectionPointContainer)m_opcServer;
 Guid sinkGUID = typeof(IOPCShutdown).GUID;
 icpc.FindConnectionPoint(ref sinkGUID, out m_OPCCP);
 m_OPCCP.Advise(this, out m_cookie_CP);

我在这里做了很多检查以适应它,把它作为一个样本...... 然后你需要一个服务器上的方法来添加组:

 // Parameter as following:
 // [in] active, so do OnDataChange callback
 // [in] Request this Update Rate from Server
 // [in] Client Handle, not necessary in this sample
 // [in] No time interval to system UTC time
 // [in] No Deadband, so all data changes are reported
 // [in] Server uses english language to for text values
 // [out] Server handle to identify this group in later calls
 // [out] The answer from Server to the requested Update Rate
 // [in] requested interface type of the group object
 // [out] pointer to the requested interface
 m_opcServer.AddGroup(m_groupName, Convert.ToInt32(m_isActive), m_reqUpdateRate, m_clientHandle, pTimeBias, pDeadband, m_LocaleID, out m_serverHandle, out m_revUpdateRate, ref iid, out objGroup); 

 // Get our reference from the created group
 m_OPCGroupStateMgt = (IOPCGroupStateMgt)objGroup;

最后你需要创建项目:

 m_OPCItem = (IOPCItemMgt)m_OPCGroupStateMgt;
 m_OPCItem.AddItems(itemList.Length, GetAllItemDefs(itemList), out ppResults, out ppErrors);

其中 itemlist 是一个 OPCITEMDEF[] 数组。我使用我的结构中的 GetAllItemDefs 构建上述内容。

    private static OPCITEMDEF[] GetAllItemDefs(params OpcItem[] opcItemList)
    {
        OPCITEMDEF[] opcItemDefs = new OPCITEMDEF[opcItemList.Length];
        for (int i = 0; i < opcItemList.Length; i++)
        {
            OpcItem opcItem = opcItemList[i];
            opcItemDefs[i].szAccessPath = "";
            opcItemDefs[i].bActive = Convert.ToInt32(opcItem.IsActive);
            opcItemDefs[i].vtRequestedDataType = Convert.ToInt16(opcItem.ItemType, CultureInfo.InvariantCulture);
            opcItemDefs[i].dwBlobSize = 0;
            opcItemDefs[i].pBlob = IntPtr.Zero;
            opcItemDefs[i].hClient = opcItem.ClientHandle;
            opcItemDefs[i].szItemID = opcItem.Id;
        }
        return opcItemDefs;
    }

最后,关于枚举Servers,我使用了这两个函数:

   /// <summary>
    /// Enumerates hosts that may be accessed for server discovery.
    /// </summary>
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    public string[] EnumerateHosts()
    {
        IntPtr pInfo;

        int entriesRead = 0;
        int totalEntries = 0;

        int result = NetServerEnum(
            IntPtr.Zero,
            LEVEL_SERVER_INFO_100,
            out pInfo,
            MAX_PREFERRED_LENGTH,
            out entriesRead,
            out totalEntries,
            SV_TYPE_WORKSTATION | SV_TYPE_SERVER,
            IntPtr.Zero,
            IntPtr.Zero);

        if (result != 0)
            throw new ApplicationException("NetApi Error = " + String.Format("0x{0,0:X}", result));

        string[] computers = new string[entriesRead];

        IntPtr pos = pInfo;
        for (int ii = 0; ii < entriesRead; ii++)
        {
            SERVER_INFO_100 info = (SERVER_INFO_100)Marshal.PtrToStructure(pos, typeof(SERVER_INFO_100));
            computers[ii] = info.sv100_name;
            pos = (IntPtr)(pos.ToInt32() + Marshal.SizeOf(typeof(SERVER_INFO_100)));
        }

        NetApiBufferFree(pInfo);
        return computers;
    }

    /// <summary>
    /// Returns a list of servers that support the specified specification on the specified host.
    /// </summary>
    [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
    public string[] GetAvailableServers(Specification specification)
    {
        lock (this)
        {
            // connect to the server.
            ArrayList servers = new ArrayList();
            MULTI_QI[] results = new MULTI_QI[1];
            GCHandle hIID = GCHandle.Alloc(IID_IUnknown, GCHandleType.Pinned);

            results[0].iid = hIID.AddrOfPinnedObject();
            results[0].pItf = null;
            results[0].hr = 0;

            try
            {
                // create an instance.
                Guid srvid = CLSID;
                CoCreateInstanceEx(srvid, null, CLSCTX.CLSCTX_LOCAL_SERVER, IntPtr.Zero, 1, results);

                m_server = (IOPCServerList2)results[0].pItf;

                // convert the interface version to a guid.
                Guid catid = new Guid(specification.ID);

                // get list of servers in the specified specification.
                IOPCEnumGUID enumerator = null;
                m_server.EnumClassesOfCategories(1, new Guid[] { catid }, 0, null, out enumerator);

                // read clsids.
                Guid[] clsids = ReadClasses(enumerator);

                // release enumerator
                if (enumerator != null && enumerator.GetType().IsCOMObject)
                    Marshal.ReleaseComObject(enumerator);

                // fetch class descriptions.
                foreach (Guid clsid in clsids)
                {
                    try
                    {
                        string url = CreateUrl(specification, clsid);
                        servers.Add(url);
                    }
                    catch (Exception) { }
                }
            }
            catch
            {
            }
            finally
            {
                if (hIID.IsAllocated) hIID.Free();
                if (m_server != null && m_server.GetType().IsCOMObject)
                    Marshal.ReleaseComObject(m_server);
            }
            return (string[])servers.ToArray(typeof(string));
        }
    }

我知道我已经删除了很多,但也许它仍然可以帮助你;) 如果您认为我说的很清楚,请将答案标记为正确;) 亲切的问候, D.

【讨论】:

  • 你有完整的实现在网上发布吗,比如github?
  • 如果能分享完整的源代码就好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多