OPC是一个工业标准,它是由一些世界上著名的自动化系统和硬件、软件公司和Microsoft(微软)紧密合作而建立的。〔O代表OLE(对象链接和嵌入),P (process过程),C (control控制)。OLE已从面向对象重新定义为基于对象并更名为Active X〕。@H_301_58@
WinCC是西门子公司在自动化领域采用最先进的技术与微软公司在共同开发的居于世界领先地位的工控软件。WinCC即WINDOWS CONTROL CENTER(视窗控制中心)。WinCC是一个功能强大的全面开放的监控系统,既可以用来完成小规模的简单的过程监控应用,也可以用来完成复杂的应用。在任何情况下WinCC都可以生成漂亮而便捷的人机对话接口,使操作员能够清晰地管理和优化生产过程。它集成的OPC(OLE for process control)服务器使得过程数据可由其它应用程序(OPC客户机)访问。@H_301_58@
WinCC在安装时提供了OPC的客户端控件: Siemens OPC DAAutomation 2.0( SOPCDAAuto.dll),这个控件就是我们在VB中要用到的控件,我们也可以使用通用的OPC客户端控件: OPC Automation 2.0.@H_301_58@
在WINCC的帮助中,有Siemens OPC DAAutomation 2.0使用的简略帮助,但说得不很详细,我在使用中碰到不少问题,现一并写出来,与大家共享。@H_301_58@
一、OPC的连接
先在“引用”将近 Siemens OPC DAAutomation 2.0加入,然后开始定义全局变量。在本程序中,我使用了两个OPC组进行OPC访问,所以定义了全局变量。我们要首先定义OPC服务类型与计算机结点名。定义OPC组与OPC标签组。并定义OPC的标签数组与值数,注意,值数组一定要设为Variant。@H_301_58@
'OPC处理:只对WINCC@H_301_58@
Const ServerName = "OPCServer.WinCC" ‘OPC的类型@H_301_58@
Const NodeName = "GUK" ‘结点名,即计算机名@H_301_58@
‘Dim NodeName As String@H_301_58@
Dim WithEvents MyOPCServer As OPCServer ‘OPC服务 @H_301_58@
Dim MyOPCGroupColl As OPCGroups ‘@H_301_58@
Dim WithEvents MyOPCGroupOut As OPCGroup ‘OPC组,本程序用两个组进行OPC连接@H_301_58@
Dim WithEvents MyOPCGroupIn As OPCGroup@H_301_58@
Dim MyOPCItemCollIn As OPCItems@H_502_253@ ‘OPC标签组@H_301_58@
Dim MyOPCItemCollOut As OPCItems@H_301_58@
Dim ServerHandlesIn() As Long ‘句柄@H_301_58@
Dim ServerHandlesOut() As Long@H_301_58@
Dim ErrorsIn() As Long ‘错误句柄@H_301_58@
Dim ErrorsOut() As Long@H_301_58@
Dim WatchDataReadItem(100) As String '记录OPC的标签@H_301_58@
Dim WatchDataReadValue(100) As Variant '存放OPC的值@H_301_58@
@H_301_58@
Dim WatchDataWriteItem(100) As String '记录OPC的标签@H_301_58@
Dim WatchDataWriteValue(100) As Variant '存放OPC的值@H_301_58@
@H_301_58@
在定义所有变量后,我们就要进行OPC连接了,要进行OPC连接之前,先要配置要访问的OPC标签名,我们WatchDataReadItem、WatchDataWriteItem中加入相应的标签名,注意:这两个数组必须由1开始,不能由0开始。 @H_301_58@
配置好标签后就要进行OPC连接了。如下面子程序:@H_301_58@
1、 ClientHandles1先配置名柄索引,这将在读取OPC标签的值时可要用到@H_301_58@
至此:OPC连接就成功了,我们可以对OPC进行读与写的操作了。@H_301_58@
'---------------------------------------------------------------------@H_301_58@
' Sub StartClient()@H_301_58@
' 目的:连接至OPC_server,创建组和添加条目@H_301_58@
'---------------------------------------------------------------------@H_301_58@
Private Sub StartClient()@H_301_58@
Dim ItemNum As Integer@H_301_58@
Dim TarnscationID As Long@H_301_58@
Dim CanceID As Long@H_301_58@
Dim ClientHandles1(100) As Long@H_301_58@
Dim ii As Integer@H_301_58@
@H_301_58@
On Error GoTo HANDLEeRROR@H_301_58@
For ii = 0 To 100@H_301_58@
ClientHandles1(ii) = ii 先配置名柄索引,这将在读取OPC标签的值时可要用到 @H_301_58@
Next ii@H_301_58@
@H_301_58@
TarnscationID = 1@H_301_58@
‘ NodeName = xProfile.GetValue("SYSTEM","NodeName")@H_301_58@
@H_301_58@
Set MyOPCServer = New OPCServer@H_301_58@
MyOPCServer.Connect ServerName,NodeName@H_301_58@
Set MyOPCGroupColl = MyOPCServer.OPCGroups@H_301_58@
MyOPCGroupColl.DefaultGroupIsActive = True@H_301_58@
Set MyOPCGroupIn = MyOPCGroupColl.Add("MYGROUPIN")@H_301_58@
Set MyOPCGroupOut = MyOPCGroupColl.Add("MYGROUPOUT")@H_301_58@
Set MyOPCItemCollIn = MyOPCGroupIn.OPCItems@H_301_58@
Set MyOPCItemCollOut = MyOPCGroupOut.OPCItems@H_301_58@
@H_301_58@
If WriteItemIdex > 0 Then@H_301_58@
MyOPCItemCollOut.AddItems WriteItemIdex,WatchDataWriteItem,ClientHandles1,ServerHandlesOut,ErrorsOut '初始化OCP连接@H_301_58@
MyOPCGroupOut.IsSubscribed = True@H_301_58@
End If@H_301_58@
@H_301_58@
If ReadItemIdex > 0 Then@H_301_58@
MyOPCItemCollIn.AddItems ReadItemIdex,WatchDataReadItem,ServerHandlesIn,ErrorsIn '初始化OCP连接@H_301_58@
MyOPCGroupIn.IsSubscribed = True@H_301_58@
End If@H_301_58@
@H_301_58@
Exit Sub@H_301_58@
HANDLEeRROR:@H_301_58@
needOPCRestart = True@H_301_58@
xLog1.log "OPCl连接发生错误"@H_301_58@
End Sub@H_301_58@
二、OPC的标签读写
对OPC标签的读可以通过MyOPCGroupIn组与MyOPCGroupOut的DataChange事件来读取。该事件有多个参数:其中NumItems是指标签改变值的个数,ClientHandles是改变值的标签索引,ItemValues为改变值的数据,具体的意思是ClientHandles(1)的值是其对应的标签数组的索引,其所指的OPC标签的值在ItemValues(1)中。一般来说,刚连接上时,该事件会把全部所要求访问的OPC标签值全部读取过来(顺序不一,要通过ClientHandles索引),此后只有数据发生变化时才会触发该事件。也只会传输发生了变化的数据,没有变化的数据不会出现在本事件的ItemValues中。@H_301_58@
@H_301_58@
Private Sub MyOPCGroupOut_DataChange(ByVal TransactionID As Long,ByVal NumItems As Long,ClientHandles() As Long,ItemValues() As Variant,Qualities() As Long,TimeStamps() As Date)@H_301_58@
'产生要通知下一级的数据变化,根椐不再的控件有不同的处理@H_301_58@
@H_301_58@
For ii = 1 To NumItems@H_301_58@
WatchDataWriteValue(ClientHandles(ii) - 1) = ItemValues(ii) '对改变的值读入本数组@H_301_58@
Next ii@H_301_58@
End Sub@H_301_58@
对OPC的写可以有同步与异步之分,对于大量的数据传输,异步是更佳的选择,但对少量的数据传输,同步表现得更好。@H_301_58@
要进行数据传输,先要将值数据进行赋值,注意:值数据要由0开始,也就是说,值数组与标签数据不是一、一对应,值要比标签前一位,这一点,在WINCC说明中没有,但在我的实际的使用中一直要这样,不然数据就产生错位,看下面程序。@H_301_58@
这是一个拔号完毕后返回的数据进行OPC传递的程序。包含解包过程,@H_301_58@
Private Sub showSuccess(msg As String)@H_301_58@
Dim location As String@H_301_58@
Dim nowTime As String@H_301_58@
Dim logStr As String@H_301_58@
Dim Value() As String@H_301_58@
Dim ii,temp As Integer@H_301_58@
Dim isPack As Boolean@H_301_58@
Dim sHead,sDelimited,sTail As String@H_301_58@
@H_301_58@
location = xProfile.GetValue(WatchPoint(nowRunID),"LOCATION")@H_301_58@
nowTime = Now@H_301_58@
logStr = "拔" & location & "取数成功" & msg@H_301_58@
xLog1.log logStr@H_301_58@
logStr = " " & msg@H_301_58@
xLog2.log logStr '记录数据@H_301_58@
isPack = xProfile.GetValue(WatchPoint(nowRunID),"ISRECHEAD")@H_301_58@
If WatchPointRBegin(nowRunID) < 0 Then Exit Sub@H_301_58@
If isPack Then@H_301_58@
sHead = xProfile.GetValue(WatchPoint(nowRunID),"RECHEAD")@H_301_58@
sDelimited = xProfile.GetValue(WatchPoint(nowRunID),"RECDELIMITER")@H_301_58@
sTail = xProfile.GetValue(WatchPoint(nowRunID),"RECEND")@H_301_58@
Value = Split(msg,sDelimited)@H_301_58@
For ii = 0 To UBound(Value) - 1@H_301_58@
temp = WatchPointRBegin(nowRunID) + ii@H_301_58@
If temp > WatchPointREnd(nowRunID) Then Exit For@H_301_58@
WatchDataReadValue(temp - 1) = Value(ii + 1) 'VALUE要从0开始,比ITEM少1,所以减一。 有包头,占去一位,向后延一@H_301_58@
Next ii@H_301_58@
Else@H_301_58@
WatchDataReadValue(WatchPointREnd(nowRunID) - 1) = msg@H_301_58@
End If@H_301_58@
MyOPCGroupIn.SyncWrite ReadItemIdex,WatchDataReadValue,ErrorsIn '数据上传@H_301_58@
'记录上次成功执行的时间@H_301_58@
xProfile.SetValue WatchPoint(nowRunID),"LASTTIME",nowTime@H_301_58@
End Sub@H_301_58@
三、OPC连接断开。
OPC客户端连接后要占用服务器资源,所以如果不需要使用OPC时,必须进行OPC连接断开。@H_301_58@
断开的程序相当简单,释放资源即可。如下,@H_301_58@
Sub StopClient()@H_301_58@
On Error Resume Next@H_301_58@
@H_301_58@
'----------- 释放组和服务器对象@H_301_58@
@H_301_58@
MyOPCGroupColl.RemoveAll@H_301_58@
@H_301_58@
'----------- 与服务器断开连接并且清除@H_301_58@
@H_301_58@
MyOPCServer.Disconnect@H_301_58@
@H_301_58@
Set MyOPCItemCollIn = Nothing@H_301_58@
Set MyOPCItemCollOut = Nothing@H_301_58@
@H_301_58@
Set MyOPCGroupIn = Nothing@H_301_58@
Set MyOPCGroupOut = Nothing@H_301_58@
@H_301_58@
Set MyOPCGroupColl = Nothing@H_301_58@
@H_301_58@
Set MyOPCServer = Nothing@H_301_58@
@H_301_58@
End Sub@H_301_58@
但在实际的使用中发现,频繁的连接与断开,将使服务器的资源被大量的消耗,最终让服务器出错。所以尽量减少无谓的OPC连接与断开。@H_301_58@
结语:
OPC的使用是作为一个DCOM在使用,所以OPC客户端可以网络上任一计算机运行,但你必须配置DCOM的访问权限,如果你不想费神,把服务器与客户端都用相同的用户名与密码登录就成了。如果想配置DCOM,请参看DCOM的配置。@H_301_58@