公司项目中需要通过OPC-DA完成数据采集,其中踩了许多坑,记录一下
先引用一句名句,一下戳中了我这次的痛
环境 OPC服务端
Windows10(1809)
Matrikon模拟
OPC客户端
macOS(15.6)、Windows10(1809)
Java8_391
Utgard 1.5
示例代码
pom 如果不引入bcprov-jdk15on
,有可能会报错,也可以在dependencyManagement里加上
1 2 3 <dependency > 4 <groupId > org.openscada.utgard</groupId > 5 <artifactId > org.openscada.opc.lib</artifactId > 6 <version > 1.5.0</version > 7 </dependency > 8 9 10 <dependency > 11 <groupId > org.openscada.utgard</groupId > 12 <artifactId > org.openscada.opc.dcom</artifactId > 13 <version > 1.5.0</version > 14 </dependency > 15 16 17 <dependency > 18 <groupId > org.bouncycastle</groupId > 19 <artifactId > bcprov-jdk15on</artifactId > 20 <version > 1.50</version > 21 </dependency >
1 public class OpcTest {2 public static void main (String[] args) throws Exception { 3 ConnectionInformation ci = new ConnectionInformation(); 4 5 ci.setHost("127.0.0.1" ); 6 7 ci.setDomain("" ); 8 9 ci.setUser("Administrator" ); 10 11 ci.setPassword("系统密码" ); 12 13 14 ci.setClsid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" ); 15 16 Server server = new Server(ci, Executors.newSingleThreadScheduledExecutor()); 17 server.connect(); 18 19 20 AccessBase access = new SyncAccess(server, 1000 ); 21 AccessBase access = new Async20Access(server, 1000 , true ); 22 ab = new Async20Access(s, opcConfig.getPeriod(), false ); 23 24 access.addItem("Random.int2" , (item, itemState) -> { 25 String id = item.getId(); 26 JIVariant jiv = itemState.getValue(); 27 System.out.println("Value: " + jiv); 28 }); 29 30 access.bind(); 31 Thread.sleep(10000 ); 32 access.unbind(); 33 server.disconnect(); 34 } 35 }
可能遇到的问题 代码量非常非常少,就一点点,但是实际调试过程非常复杂,到处都是坑
坑1
1 Exception in thread "main " org .jinterop .dcom .common .JIException : Message not found for errorCode : 0xC0000001 2 at org .jinterop .winreg .smb .JIWinRegStub .winreg_OpenHKLM (JIWinRegStub .java :121) 3 at org .jinterop .dcom .core .JIProgId .getIdFromWinReg (JIProgId .java :145) 4 at org .jinterop .dcom .core .JIProgId .getCorrespondingCLSID (JIProgId .java :181) 5 at org .jinterop .dcom .core .JIComServer .<init >(JIComServer .java :483) 6 at org .openscada .opc .lib .da .Server .connect (Server .java :114) 7 at com .zjars .opccollection .OpcDaReader .main (OpcDaReader .java :36) 8 Caused by : jcifs .smb .SmbException 9 jcifs .util .transport .TransportException
原因 多半是使用了ProgID,会从注册表去查ClsID,但是查不到,导致J-Interop库连接失败(网上查的)
解决办法 不要用ProgID,换成ClsID,可以在DCOM属性里找到对应的值
坑2
1 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] 2 at org .jinterop .dcom .core .JIComServer .init (JIComServer .java :654) 3 at org .jinterop .dcom .core .JIComServer .initialise (JIComServer .java :561) 4 at org .jinterop .dcom .core .JIComServer .<init >(JIComServer .java :524) 5 at org .openscada .opc .lib .da .Server .connect (Server .java :108) 6 at com .zjars .opccollection .OpcDaReader .main (OpcDaReader .java :30) 7 Caused by : rpc .FaultException : Received fault . (unknown )8 at rpc .ConnectionOrientedEndpoint .call (ConnectionOrientedEndpoint .java :147) 9 at rpc .Stub .call (Stub .java :134) 10 at org .jinterop .dcom .core .JIComServer .init (JIComServer .java :649) 11 ... 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 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] 2 at org.jinterop .dcom .core .JIRemActivation .read(JIRemActivation.java :225 ) 3 at ndr.NdrObject .decode(NdrObject.java :41 ) 4 at rpc.ConnectionOrientedEndpoint .call(ConnectionOrientedEndpoint.java :141 ) 5 at rpc.Stub .call(Stub.java :134 ) 6 at org.jinterop .dcom .core .JIComServer .init(JIComServer.java :649 ) 7 ... 5 more
原因 多半是ClsID配置错了
解决办法 去dcomcnfg里找到正确的ClsID
坑4
1 org .openscada .opc .lib .da .Server .connect (Server .java :132) - Failed to connect to server 2 org .jinterop .dcom .common .JIException : An internal error occurred . [0x8001FFFF] 3 at org .jinterop .dcom .core .JIComServer .init (JIComServer .java :659) ~[org.openscada.jinterop.core-2.1.8.jar:?] 4 at org .jinterop .dcom .core .JIComServer .initialise (JIComServer .java :561) ~[org.openscada.jinterop.core-2.1.8.jar:?] 5 at org .jinterop .dcom .core .JIComServer .<init >(JIComServer .java :524) ~[org.openscada.jinterop.core-2.1.8.jar:?] 6 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 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] 2 at org .jinterop .dcom .core .JIComServer .call (JIComServer .java :1004) ~[org.openscada.jinterop.core-2.1.8.jar:?] 3 at org .jinterop .dcom .core .JIComServer .call (JIComServer .java :951) ~[org.openscada.jinterop.core-2.1.8.jar:?] 4 at org .jinterop .dcom .core .JIComObjectImpl .call (JIComObjectImpl .java :293) ~[org.openscada.jinterop.core-2.1.8.jar:?] 5 at org .jinterop .dcom .core .JIComObjectImpl .call (JIComObjectImpl .java :159) ~[org.openscada.jinterop.core-2.1.8.jar:?] 6 at org .jinterop .dcom .core .JIFrameworkHelper .attachEventHandler (JIFrameworkHelper .java :285) ~[org.openscada.jinterop.core-2.1.8.jar:?] 7 at org .openscada .opc .dcom .da .impl .OPCGroupStateMgt .attach (OPCGroupStateMgt .java :179) ~[org.openscada.opc.dcom-1.5.0.jar:?] 8 at org .openscada .opc .lib .da .Group .attach (Group .java :444) ~[org.openscada.opc.lib-1.5.0.jar:?] 9 at org .openscada .opc .lib .da .Async20Access .start (Async20Access .java :58) ~[org.openscada.opc.lib-1.5.0.jar:?] 10 at org .openscada .opc .lib .da .AccessBase .connectionStateChanged (AccessBase .java :181) [org.openscada.opc.lib-1.5.0.jar:?] 11 at org .openscada .opc .lib .da .Server .addStateListener (Server .java :427) [org.openscada.opc.lib-1.5.0.jar:?] 12 at org .openscada .opc .lib .da .AccessBase .bind (AccessBase .java :86) [org.openscada.opc.lib-1.5.0.jar:?] 13 at com .zjars .opccollection .OpcClient .handlerCallback (OpcClient .java :189) [classes/:?] 14 at com .zjars .opccollection .OpcClient .connect (OpcClient .java :104) [classes/:?] 15 at com .zjars .opccollection .OpcCollectionApplication .main (OpcCollectionApplication .java :45) [classes/:?]
原因 原因有些复杂,似乎是因为频繁是用Async20Access
订阅导致与服务端的通讯出问题?
解决办法 换成SyncAccess
类访问数据
总结
OPC服务端Windows版本不能过高,否则会访问被拒(暂时不知如何解决)
OPC客户端Windows版本没有要求
使用ClsID连接