Set AnOPCServer = New OPCServer List1.Clear
AllOPCServers = AnOPCServer.GetOPCServers
For i = LBound(AllOPCServers) To UBound(AllOPCServers) List1.AddItem AllOPCServers(i) Next i
Set AnOPCServer = Nothing End Sub
试了一下果然可以。点击list中某一个opcserver,然后调用opcserver对象的connect方法就可以连接上去了。
我试着定时检查opcserver的serverstatus属性(服务器状态ServerState属性一共有OPCRunning、OPCFailed、OPCNoconfig、OPCSuspended、OPCTest和OPCDisconnected六个值,分别表示正在运行、失败、没有配置、暂停、测试和没有连接六种OPC服务器当前的状态。)连接成功后的却返回了opcrunning值,而断开后也能返回“opcdisconnected”值。 不过不知道在非正常中断连接(比如远程服务器网络中断等)的时候,该属性是否能真正反应状态?ms有人说不行,需要从标签变bad来判断。回头在虚拟机上装个opcserver来测试一下。 学习用VB编写OPC Client(3) 添加标签的方法。 首先看了看本机上一个opcserver运行起来以后,发现里面有好几个group,那么我总不能定义一摸一样的几个group吧?看了下网上有人写的帖子,说client里面无须指定跟server一样的group,我想这是很合理的,毕竟只要以标签名作为唯一标识就可以了(这里就应该隐含着opcserver上标签名称是唯一的,不管其在不在一个group中)
网上的例子里面是做了个示范,标签名用了个循环赋值,
For I = 1 To 17
strItemIDs(I) = \"Server.Group.TAG\" & I
lClientHandles(I) = I
Next ’ 添加OPC项
Call objItems.AddItems(17, strItemIDs, lClientHandles, lServerHandles, lErrors)
但是例子里面也承认“OPC客户端程序要按照用户指定的标签或者从组态文件里读取需要添加的OPC标签。”
那么,实际上来说,要么用browse标签的方法去一个一个选择标签,要么事先在程序里面指定好各个标签名字了(当然可以用一个类似与ini配置文件的方法存储好要各个标签的名字,这个以后再说吧)。从实际应用考虑,我觉得browse的方法没有必要,因为一般来说直接确定好哪些标签就行了,而且如果用参数文件的话也算比较容易后期进行配置(前提应该是tag名字要写正确)。
Items有个additems的方法,用于添加标签,参数比较多,认真看了下例子,还是有点技巧的,从第二个参数开始都是数组类型的参数,而前两个数组必须指定好长度,后面三个数组则无须指定长度,很怪啊,反正vb认可就ok了,难怪例子里面这么定义的几个数组。
下一步就是标签的读写了,里面牵涉的内容好像挺多,主要是同步和异步读写问题,后面再研究吧。 学习用VB编写OPC Client(4) 关于引用对象的问题 从下载的资料看,需要在工程里面引用一个opc对象。我看的是用opc访问wincc的文章,里面提到“先在“引用”将近 Siemens OPC DAAutomation 2.0加入”,试了下去掉这个引用,运行程序就报告“变量未定义”,指向的就是定义的几个opc对象变量。试着引用另外一个opc对象-OPC Automation 2.0,就可以了。看来引用只是为了能够声明和定义opc对象而已,所以随便找个就可以了。 关于同步读的问题 百度上搜到csdn上有同步读写的代码,看了下很奇怪
Private Sub Command_Read_Click()’同步读
Dim OutText As String
Dim myValue As Variant
Dim myQuality As Variant
Dim myTimeStamp As Variant
On Error GoTo ErrorHandler
OutText = \"读ITEM值\"
ItemObj.Read OPCDevice, myValue, myQuality, myTimeStamp
Edit_ReadVal = myValue
Edit_ReadQu = GetQualityText(myQuality)
Edit_ReadTS = myTimeStamp
Exit Sub
ErrorHandler:
MsgBox Err.Description + Chr(13) + _
OutText, vbCritical, \"ERROR\"
End Sub
其中GetQualityText是自定义函数,标签质量代码的解释
Private Function GetQualityText(Quality) As String
Select Case Quality
Case 0: GetQualityText = \"BAD\"
Case : GetQualityText = \"UNCERTAIN\"
Case 192: GetQualityText = \"GOOD\"
Case 8: GetQualityText = \"NOT_CONNECTED\"
Case 13: GetQualityText = \"DEVICE_FAILURE\"
Case 16: GetQualityText = \"SENSOR_FAILURE\"
Case 20: GetQualityText = \"LAST_KNOWN\"
Case 24: GetQualityText = \"COMM_FAILURE\"
Case 28: GetQualityText = \"OUT_OF_SERVICE\"
Case 132: GetQualityText = \"LAST_USABLE\"
Case 144: GetQualityText = \"SENSOR_CAL\"
Case 148: GetQualityText = \"EGU_EXCEEDED\"
Case 152: GetQualityText = \"SUB_NORMAL\"
Case 216: GetQualityText = \"LOCAL_OVERRIDE\"
Case Else: GetQualityText = \"UNKNOWN ERROR\"
End Select
End Function
问题关键在于上面代码的同步读采用的是item对象的read方法,而别的资料则是用group的syncread方法,显然后者效率高。形如:
objGroup.SyncRead OPCCache, UBound(lServerHandles), lServerHandles, vItemData, lErrors
其中opccache是个关键字,也可以用opcdevice关键字,在规范里面讲了一下两者的区别。运行了下,老是报告参数错误然后竟然vb出错了,以为是前面错误多了导致突然退出程序没有释放定义的对象,于是重启机器,还是依旧,看来不是这个原因了。换个引用的opc对象试试看?果然ok了,数据读上来了。想到csdn的代码里面有标签质量,那么group的syncread方法也有质量参数不过是可选的,于是加上一个参数,定义为variant即可,但是在显示这个质量参数的时候却出错了。我猜想应该是数组的形式来引用――――尽管其被调用的时候被定义为variant,果然如此。然后借用前面csdn的GetQualityText函数即可显示出标签的质量了,正常就是good ,否则是 bad。同样道理,时间标戳(TIME STAMP)也可以显示出来。
下一步是同步写的问题。
读的问题解决了,写就简单了,语法是:
SyncWrite(NumItems As Long, ServerHandles() As Long, Values() As Variant, ByRef Errors() As Long)
不过发现opc官方文档里面的例子居然有错,就是Values()这个数组在定义的时候必须指定长度否则vb报告下标错。我猜想这是因为在调用syncwrite方法之前,肯定要对values数组元素赋值,此时没有调用syncwrite造成系统不能划出values数组的大小,悖论啊,瞎猜瞎猜。
问题又来了,实际应用中,可能仅仅是指定某几个标签写下去,怎么办?
受到前面csdn的代码启示,用item的write方法,写了一个小函数:
Sub SingleWrite(Index As Long, Value As Variant) ’单个写
Dim objItem As OPCItem
If Not objItems Is Nothing Then
Set objItem = objItems.Item(Index)
objItem.Write Value End If
Set objItem = Nothing
End Sub 下一步研究下异步读写的问题。 学习用VB编写OPC Client(5) 关于异步读写所联想到的…… 粗粗的浏览了一下资料里面关于异步读写的描述,包括订阅更新,感觉是一种很好的做法,特别是数据量大的时候。因为同步读写必须等待服务器返回结果才能继续后面的代码。 看到这里,我想到了前面研究modbus通讯时,对于485总线多站点轮询,曾经想当然的考虑过一个方法:主站先把所有报文统统发送出去,然后等待各条报文返回来,或者是发送一条报文就接收一次再发送一次再接收一次如此循环,当然由于速度问题,每次接收的报文基本上不可能是上条发送的请求报文的返回了,我以为如此可以达到很高的效率。但是当实际动手开始编的时候,才发现这种想法是不能实现的,原因在于无法完全识别返回的报文是哪条发送报文的正确返回。例如第一条报文请求读取站号=2的从站的8个bool量,而第二条报文则是请求读取站号=2的另外8个bool量,那么主站收到返回报文后就无法识别读到的这8个bool量是从站中哪片地址区的(因为返回报文中只包含了功能码、站号、返回的字节数和数据内容)。所以只能是发送请求报文以后,“耐心”等待返回的报文。
这样看来,modbus是一种同步读写的机制,而我所设想的所谓方法是一种不能实现的“异步”读写机制。
说句瞎话,其实只要修改一下slave返回的报文格式就能够使得master识别出slave返回报文的归属,以读数据为例,把返回报文变成:
发送报文(不含crc码)+返回数据字节数+返回数据+crc码
这样的话,我前面胡乱想的那个所谓“异步”读写方法就能实现鸟。 可惜啊,当初制定modbus规约的时候没这么设计……\\^_^/ 关于异步读写
有了前面同步读写的基础,异步读写就轻松多了。同样是在定时器事件里面启动group的asynchread方法,但是数据接收则是在group的asynchreadcomplete事件中,所以异步读就显得比较效率高点,发完请求以后就等着数据来了再触发接收事件,而这期间可以干点其它事情了。
而所谓的订阅方式就更简单了,在group的datachange事件中等数据来就行了,根本就不用发数据请求出去,只要把group的isactive属性置为true就可以了。不过我测试中发现,有时候该事件接收不到数据,而切换到同步或者异步再切换回来,订阅的数据就变化了,不理解啊。看来还是异步的方式在三者中可靠且完善点。
关于异步写,看了写觉得差不多就偷懒不写代码测试了。毕竟碰到大批量数据写下去的情况几乎不会被我碰到吧。我的出发点还是立足于读数据出来转到其它地方去处理:或者入数据库,或者入excel去打印等等。
另外,顺便试了opcserver的几个属性:启动时间,运行时间和版本号,发现还有有用的,可以查看server的状态。而item的几个属性也试了下,比如质量、时标,数据类型,但是其中一个工程单位的属性(EUInfo)始终报错,很郁闷。换了opcserver都一样。可用标准的opcclient软件-Factroy OPC Client却能够读取该属性,不晓得人家是怎么弄的(当然人家肯定是用c之类写的吧,vb实在是太傻瓜化了,隐藏了太多的细节)
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务