在PowerShell中读取OPC DA数据

目录

介绍

在PowerShell中通过OPC DA读取数据

绑定到接口

连接到服务器

连接到服务器

读取数据

将一切整合在一起

结论和兴趣点


介绍

如果您活跃在工业自动化领域,那么您很可能遇到过OPC DAOPC DA是从软件接口、PLC、测量设备等检索数据的通用标准。如果是这样,那么这篇文章就是为你写的。

OPC DA是一个相当古老的标准。它建立在DCOM之上,DCOM带有一系列安全和网络问题。但是,它仍然无处不在,能够连接到OPC DA服务器以检索数据仍然非常有用。

在大多数情况下,您不会自己建立连接。通常,将应用程序A配置为连接到OPC服务器B以读取某些值。即使你需要自己编程,这通常是用VBScript.NET语言(如VB.NETC#)完成的。有一些例子可以从那里开始。

但是,能够从Powershell脚本中的OPC服务器获取一些数据值可能会很有趣。我想编写一个脚本来帮助我进行一些诊断,然后发现没有一个示例来演示如何在PowerShell中执行此操作。从根本上说,一切都是可用的,在弄清楚之后,我决定写一个小教程。

本文无意成为有关OPC的教程。就本文而言,将假定对OPC有基本的了解。有关OPC DA的更多信息,您可以转到www.opcfoundation.org但请注意,有关OPC DA的信息不是免费提供的。作为非付费会员,您可以获得可再发行组件和API,但仅此而已。

PowerShell中通过OPC DA读取数据

总而言之,做到这一点并不难。我将展示每个步骤并解释每个步骤中发生的事情。

绑定到接口

OPC DA是其核心DCOM技术,虽然可以直接使用DCOM,但OPC基金会公开提供.NET可再发行组件,这些可公开发行组件围绕DCOM接口提供了一个很好的.NET shell。这就是我使用的,因为PowerShell很容易与.NET类集成。您可以从www.opcfoundation.org下载的可再发行组件。如果您对本文感兴趣,很可能您有OPC服务器来测试脚本。如果没有,许多OPC供应商都有OPC服务器可用于模拟和测试目的。www.kepware.com就是其中之一。

请务必注意,可再发行组件是针对.NET框架的32位版本构建的。将它们与32位版本的PowerShell一起使用非常重要。.NET包装器DLL将毫无问题地加载,但它们将无法正确绑定到DCOM接口。

对于此示例,我已将DLL放在名为c:\OPC 的文件夹中。在那里,为了能够运行此脚本,需要安装一个OPC服务器,或者至少安装一个用于必要类库的OPC DA客户端。

Add-type -Path C:\OPC\OpcComRcw.dll
Add-type -Path C:\OPC\OpcNetApi.Com.dll
Add-type -Path C:\OPC\OpcNetApi.dll 

连接到服务器

加载DLL后,我们现在可以连接到OPC服务器。这需要一个URL。在本文中,我们假设服务器在本地运行,但可以使用网络主机名或IP地址。

URL由协议名字对象(opcda://)、服务器的位置(本地主机)和服务器的名称组成。同一台计算机上可以有多个OPC服务器。创建服务器连接对象是通过DCOM类工厂完成的。这部分只是样板,除了URL之外始终相同。ConnectData用于设置将用于建立连接的DCOM安全设置。默认情况下,我们使用当前用户的凭据进行连接。但是,如果由于供应商特定的限制,需要专用服务帐户,则可以在此处设置该帐户。

[Opc.URL]$url = New-Object Opc.URL -ArgumentList "opcda://localhost/OPC.DeltaV.1"
[Opc.ConnectData]$connectdata = New-Object Opc.ConnectData `
         -ArgumentList (New-Object System.Net.NetworkCredential)
[OpcCom.Factory] $fact = new-object -TypeName OpcCom.Factory
[Opc.Da.Server]$server = New-Object -TypeName Opc.Da.Server -ArgumentList @($fact, $null) 

连接到服务器

读取数据需要活动连接。最好确保您打开的每个连接最终都关闭。OPC是基于DCOM的,所以最终,对象将被释放,连接无论如何都会被关闭,但依赖它并不是好的设计,所以我们做了如下的事情:

try{
    $server.Connect( $url, $connectdata)
    try {
        #Implement read operations in this section
    }
    finally{
        $server.Disconnect()
    }
}
catch{ 
    $_
}

读取数据

这部分相对简单。OPC DA支持检索订阅中的数据。如果您有一个重要的数据采集设置,那么您可以设置具有不同数据速率的不同订阅。有些数据可能每1秒读取一次,有些数据需要每5秒读取一次,等等。但是,对于此示例,我们只想临时读取数据以读取其当前值。除了术语订阅,您还将使用术语,有时会使用术语。

我们将有一个订阅组。组具有组状态,该状态设置为false因为我们不希望活动订阅获取持续的数据更新。我们只想将订阅用作定义要读取的项目的占位符。在这个例子中,我们只初始化一个包含两个项目的数组,然后填写我们要读取的2OPC DA变量的名称。

| out-null之所以放置在那里,是因为添加项目会导致该AddItems方法回显项目配置。这无害,但在运行脚本时有点混乱。

[Opc.Da.Subscription]$group
[Opc.Da.SubscriptionState]$groupState = New-Object Opc.Da.SubscriptionState
$groupState.Name = "Group"
$groupState.Active = $false
$group = $server.CreateSubscription($groupState);

[Opc.Da.Item[]] $items = New-Object Opc.Da.Item[] 2
$items[0] =  New-Object Opc.Da.Item
$items[0].ItemName = "CNT-TST301/COMM/PRI/CONGOOD"
$items[1] =  New-Object Opc.Da.Item
$items[1].ItemName = "CNT-TST301/COMM/SEC/CONGOOD"
$group.AddItems($items) |out-null
$result = $group.Read($group.Items)
$result

这就是它的一切!对于读取的每个项目,都会有一个OPCItemValue对象。请注意,每个值对象还具有一个GetType成员,可帮助您转换项目值并将其分配给变量。OPC接口使用变体来传递数据,因此值本身是字符串、布尔值、整数或浮点数,...

ResultID           : S_OK
DiagnosticInfo     : 
Value              : 7
Quality            : good
QualitySpecified   : True
Timestamp          : 7/3/2022 2:08:47 PM
TimestampSpecified : True
ItemName           : CNT-TST301/COMM/SEC/CONGOOD
ItemPath           : 
ClientHandle       : 
ServerHandle       : 2
Key                : CNT-TST301/COMM/SEC/CONGOOD
                     null

将一切整合在一起

现在我们已经有了所有必要的部分,我们可以将它们放在一起,并适当的错误处理到位。

# Copyright 2022 Bruno van Dooren
#
# Permission is hereby granted, free of charge, to any person 
# obtaining a copy of this software and associated documentation files (the "Software"), 
# to deal in the Software without restriction, including without limitation 
# the rights to use, copy, modify, merge, publish, distribute,
# sublicense, and/or sell copies of the Software, 
# and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included 
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#import the .NET OPC types
Add-type -Path C:\OPC\OpcComRcw.dll
Add-type -Path C:\OPC\OpcNetApi.Com.dll
Add-type -Path C:\OPC\OpcNetApi.dll

#create the OPC objects we will use for setting up the connection
[Opc.URL]$url = New-Object Opc.URL -ArgumentList "opcda://localhost/OPC.DeltaV.1"
if($url -eq $null){
    "Opc.URL is null";exit
}

[Opc.ConnectData]$connectdata = New-Object Opc.ConnectData `
    -ArgumentList (New-Object System.Net.NetworkCredential)
if($connectdata -eq $null){
    "Opc.ConnectData is null";exit
}

[OpcCom.Factory] $fact = new-object -TypeName OpcCom.Factory
if($fact -eq $null){
    "OpcCom.Factory is null";exit
}

[Opc.Da.Server]$server = New-Object -TypeName Opc.Da.Server `
    -ArgumentList @($fact, $null)
if($server -eq $null){
    "Opc.Da.Server is null";exit
}

try{
    $server.Connect( $url, $connectdata)
    try
    {
        #add some tags to a single subscription and read them
        [Opc.Da.Subscription]$group
        [Opc.Da.SubscriptionState]$groupState = New-Object Opc.Da.SubscriptionState
        $groupState.Name = "Group"
        $groupState.Active = $false
        $group = $server.CreateSubscription($groupState);

        [Opc.Da.Item[]] $items = New-Object Opc.Da.Item[] 2
        $items[0] =  New-Object Opc.Da.Item
        $items[0].ItemName = "CNT-TST301/COMM/PRI/CONGOOD"
        $items[1] =  New-Object Opc.Da.Item
        $items[1].ItemName = "CNT-TST301/COMM/SEC/CONGOOD"
        $group.AddItems($items) |out-null

        $result = $group.Read($group.Items)
        $result
    }
    finally{
        #ensure cleanup if we were able to create the connection
        $server.Disconnect()
    }
}
catch{
    $_
}   

结论和兴趣点

如您所见,如果您使用.NET接口并记住在需要时使用32位版本的PowerShell,则从OPC服务器读取数据是相当简单的。我的代码包含在MIT许可证下,因此请随意使用它。

关于读取OPC DA数据的最后一句话需要说。该ResultID参数告诉您值的状态,并且还有一个质量指示。这些参数告知您读数是否成功和准确。我不打算深入探讨不同的场景,但在执行此类即席查询时,有一件重要的事情要记住。

OPC DA是一个数据接口,允许客户端连接到服务器以检索数据。这并不意味着当您建立连接时,数据立即可用。在较大的系统中,可用的不同数据点总数为数万个。OPC服务器不会一直监视所有这些服务器。如果对尚未主动监视的值执行读取操作,则初始读取可能会在第一次尝试时返一个E_FAIL或一个quality-bad结果,并且仅在第二次读取尝试后返回有效值,当OPC DA服务器有机会建立要读取的值的内部数据通道时。

https://www.codeproject.com/Articles/5336579/Reading-OPC-DA-Data-in-PowerShell

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值