公司项目中需要通过OPC-DA完成数据采集,其中踩了许多坑,记录一下
先引用一句名句,一下戳中了我这次的痛
环境 OPC服务端
Windows10(1809)
Matrikon模拟
OPC客户端
macOS(15.6)、Windows10(1809)
Java8_391
Utgard 1.5
示例代码
pom 如果不引入bcprov-jdk15on,有可能会报错,也可以在dependencyManagement里加上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <dependency > <groupId > org.openscada.utgard</groupId > <artifactId > org.openscada.opc.lib</artifactId > <version > 1.5.0</version > </dependency > <dependency > <groupId > org.openscada.utgard</groupId > <artifactId > org.openscada.opc.dcom</artifactId > <version > 1.5.0</version > </dependency > <dependency > <groupId > org.bouncycastle</groupId > <artifactId > bcprov-jdk15on</artifactId > <version > 1.50</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public class OpcTest { public static void main (String[] args) throws Exception { ConnectionInformation ci = new ConnectionInformation(); ci.setHost("127.0.0.1" ); ci.setDomain("" ); ci.setUser("Administrator" ); ci.setPassword("系统密码" ); ci.setClsid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" ); Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor()); server.connect(); AccessBase access = new SyncAccess(server, 1000 ); AccessBase access = new Async20Access(server, 1000 , true ); ab = new Async20Access(s, opcConfig.getPeriod(), false ); access.addItem("Random.int2" , (item, itemState) -> { String id = item.getId(); JIVariant jiv = itemState.getValue(); System.out.println("Value: " + jiv); }); access.bind(); Thread.sleep(10000 ); access.unbind(); server.disconnect(); } }
可能遇到的问题 代码量非常非常少,就一点点,但是实际调试过程非常复杂,到处都是坑
坑1
1 2 3 4 5 6 7 8 9 Exception in thread "main " org .jinterop .dcom .common .JIException : Message not found for errorCode : 0xC0000001 at org .jinterop .winreg .smb .JIWinRegStub .winreg_OpenHKLM (JIWinRegStub .java :121) at org .jinterop .dcom .core .JIProgId .getIdFromWinReg (JIProgId .java :145) at org .jinterop .dcom .core .JIProgId .getCorrespondingCLSID (JIProgId .java :181) at org .jinterop .dcom .core .JIComServer .<init >(JIComServer .java :483) at org .openscada .opc .lib .da .Server .connect (Server .java :114) at com .zjars .opccollection .OpcDaReader .main (OpcDaReader .java :36) Caused by : jcifs .smb .SmbException jcifs .util .transport .TransportException
原因 多半是使用了ProgID,会从注册表去查ClsID,但是查不到,导致J-Interop库连接失败(网上查的)
解决办法 不要用ProgID,换成ClsID,可以在DCOM属性里找到对应的值
坑2
1 2 3 4 5 6 7 8 9 10 11 Exception in thread "main " org .jinterop .dcom .common .JIException : Access is denied , please check whether the [domain-username-password] are correct . Also , if not already done please check the GETTING STARTED and FAQ sections in readme .htm . They provide information on how to correctly configure the Windows machine for DCOM access , so as to avoid such exceptions . [0x00000005] at org .jinterop .dcom .core .JIComServer .init (JIComServer .java :654) at org .jinterop .dcom .core .JIComServer .initialise (JIComServer .java :561) at org .jinterop .dcom .core .JIComServer .<init >(JIComServer .java :524) at org .openscada .opc .lib .da .Server .connect (Server .java :108) at com .zjars .opccollection .OpcDaReader .main (OpcDaReader .java :30) Caused by : rpc .FaultException : Received fault . (unknown ) at rpc .ConnectionOrientedEndpoint .call (ConnectionOrientedEndpoint .java :147) at rpc .Stub .call (Stub .java :134) at org .jinterop .dcom .core .JIComServer .init (JIComServer .java :649) ... 4 more
原因 这个问题困扰了我很久,在网上疯狂搜索,ChatGPT都要被我问得不耐烦了,试过:换Windows账号、换JDK位数、检查各种DCOM权限,均无果 最终在一篇文章里找到思路,通过Windows自带的日志发现,每次OPC发起连接时,日志中就会出现一条记录: ChatGPT是这样解释的 继续查,结果查到了一篇文章 这个方法试过也无济于事,最终某一篇文章的评论区找到的解决办法
解决办法1 如果是访问远程OPC服务器,可以尝试: 在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System中,创建一个DWORD(32位)的值LocalAccountTokenFilterPolicy,数据值设置为1
解决办法2 如果是访问本机OPC服务器,那只能系统降级😅,从22H2降到1809之后,终于不再报错也能访问到数据了,目前暂时未找到更佳解决方案
坑3
1 2 3 4 5 6 7 Caused by: org.jinterop .dcom .common .JIRuntimeException : Class not registered. If you are using a DLL/OCX , please make sure it has "DllSurrogate" flag set. Faq A(6 ) in readme.html . [0 x80040154] at org.jinterop .dcom .core .JIRemActivation .read(JIRemActivation.java :225 ) at ndr.NdrObject .decode(NdrObject.java :41 ) at rpc.ConnectionOrientedEndpoint .call(ConnectionOrientedEndpoint.java :141 ) at rpc.Stub .call(Stub.java :134 ) at org.jinterop .dcom .core .JIComServer .init(JIComServer.java :649 ) ... 5 more
原因 多半是ClsID配置错了
解决办法 去dcomcnfg里找到正确的ClsID
坑4
1 2 3 4 5 6 org .openscada .opc .lib .da .Server .connect (Server .java :132) - Failed to connect to server org .jinterop .dcom .common .JIException : An internal error occurred . [0x8001FFFF] at org .jinterop .dcom .core .JIComServer .init (JIComServer .java :659) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .jinterop .dcom .core .JIComServer .initialise (JIComServer .java :561) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .jinterop .dcom .core .JIComServer .<init >(JIComServer .java :524) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .openscada .opc .lib .da .Server .connect (Server .java :108) [org.openscada.opc.lib-1.5.0.jar:?]
原因 网络原因
解决办法 检查网络是否打通,ping一下IP,或者用OPCClient尝试读一下数据; 或者检查一下是不是用ProgID在连接,换成ClsID试试;
坑5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 org .jinterop .dcom .common .JIException : The RPC server is unavailable . Please check if the COM server is up and running and that route to the COM Server is accessible (A simple "Ping " to the Server machine would do ). Also please confirm if the Windows Firewall is not blocking DCOM access . [0x800706BA] at org .jinterop .dcom .core .JIComServer .call (JIComServer .java :1004) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .jinterop .dcom .core .JIComServer .call (JIComServer .java :951) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .jinterop .dcom .core .JIComObjectImpl .call (JIComObjectImpl .java :293) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .jinterop .dcom .core .JIComObjectImpl .call (JIComObjectImpl .java :159) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .jinterop .dcom .core .JIFrameworkHelper .attachEventHandler (JIFrameworkHelper .java :285) ~[org.openscada.jinterop.core-2.1.8.jar:?] at org .openscada .opc .dcom .da .impl .OPCGroupStateMgt .attach (OPCGroupStateMgt .java :179) ~[org.openscada.opc.dcom-1.5.0.jar:?] at org .openscada .opc .lib .da .Group .attach (Group .java :444) ~[org.openscada.opc.lib-1.5.0.jar:?] at org .openscada .opc .lib .da .Async20Access .start (Async20Access .java :58) ~[org.openscada.opc.lib-1.5.0.jar:?] at org .openscada .opc .lib .da .AccessBase .connectionStateChanged (AccessBase .java :181) [org.openscada.opc.lib-1.5.0.jar:?] at org .openscada .opc .lib .da .Server .addStateListener (Server .java :427) [org.openscada.opc.lib-1.5.0.jar:?] at org .openscada .opc .lib .da .AccessBase .bind (AccessBase .java :86) [org.openscada.opc.lib-1.5.0.jar:?] at com .zjars .opccollection .OpcClient .handlerCallback (OpcClient .java :189) [classes/:?] at com .zjars .opccollection .OpcClient .connect (OpcClient .java :104) [classes/:?] at com .zjars .opccollection .OpcCollectionApplication .main (OpcCollectionApplication .java :45) [classes/:?]
原因 原因有些复杂,似乎是因为频繁是用Async20Access订阅导致与服务端的通讯出问题?
解决办法 换成SyncAccess类访问数据
总结
OPC服务端Windows版本不能过高,否则会访问被拒(暂时不知如何解决)
OPC客户端Windows版本没有要求
使用ClsID连接