准备工作:
开发环境:KEPServerEX-6.x,JDK-8,milo-0.2.4
KEPServerEX下载:https://pan.baidu.com/s/1kbAh46cCYV0JGeXm3kuX5A?pwd=hy71
提取码:hy71(非破解版,可以自行去官网下载,安装过程一直点下一步就好了)
项目代码:UseMilo
参考文章:使用java的milo框架访问OPCUA服务的方法
文章简介:
使用KEPServerEX6,新建通道选择Simulator,进行仿真具体的OPCUA服务器,编写的Java项目是客户端,建立的TCP连接是Java-----KEPServerEX6软件的OPCUA端口号(默认49320),进行读取值。
同时对于服务器还具有其他调式的可能,运行Java项目是其一,或者下载UaExpert、使用KEPServerEX6的
milo项目地址,https://github.com/eclipse/milo
milo组件库,分为三个大部分:
- milo-examples:写了很多可运行类,介绍具体如何调用本项目的接口。分成客户端和服务器两部分。
- opc-ua-sdk:OPCUA的软件开发工具包(对外可被调用。
- sdk-client可以用来模拟服务器,我们服务器使用了KEPServerEX6。
- sdk-server可以用来模拟客户端,我们用的大多数类都是这个包内的。
- opc-ua-stack:是项目的底层支持,里面定义了很多通道、节点、数据类型、认证方式、错误类型等
KEPServerEX6服务器配置:
1、添加新用户
安装完成后,找到运行图标,右击打开设置---用户管理器---在Administrators下添加新用户,
记下账户名、密码(14位),后续代码使用用户认证需要用到。
2、OPC UA配置安全策略、URL
回到桌面任务栏,继续右击运行图标,打开OPC UA配置---服务器端点---双击黑色的服务器端点
本地测试时,网络适配器选择仅限本地主机。
访问地址(使用默认的就好),和安全策略(推荐像下图配置)都会具体在代码中体现。
3、新建通道、设备、标记
打开KEPServerEX6界面
通道类型选择Simulator
自由设置通道名字、设备名字,但注意使用英文,否则可能出现连接上了读取不到tag值的问题。
之后一直点击下一步即可。
在创立出来的通道(你设置的名字)上,右击选择新建设备。
之后一直点击下一步即可。
单击新建的设备,在空白界面右击选择新建标记。
填入标记名称,地址
4、新建客户端尝试连接服务器,看看是否成功。
值的Quality应该显示为良好。
可以右击具体的标签项,选择Synchronous Write进行修改值内容。
需要注意的是,使用自己KEPServerEX6的客户端连接服务器,可能也需要进行客户端信任。
右击运行图标----OPCUA配置,受信任的客户端,点击列表中被红色叉标记的项,点击信任。
5、打开项目的匿名登录
回到KEPServerEX6软件的主界面,右击项目,选择属性。
6、补充
使用KEPServerEX6创建的通道我们选择了Simulator,作为服务器的通道。他的默认的地址空间为2,即项目中读取时填入的NodeID的namespaceindex为2。我们通过下面的操作也可以看得到。
我们当然也可以右击项目的连接性,再新建一个通道,通道类型选择OPCUA Client,之后依旧使用英文通道名、设备名,不过需要注意的是:
创建OPCUA Client这个通道作为的是一个客户端,因此需要写入连接的服务器的url,我们连接到就是另一个通道,所以使用,opc.tcp://localhost:49320。安全策略也需要跟服务器的保持一致。
同时还需要输入我们在前面第一节新建的用户名账号和密码,进行用户认证,让服务器放行。
之后下一步、完成。
之后对该通道新建设备,输入英文设备名,之后一直下一步,直到可以导入项。
点击导入,就会自动使用刚刚新建通道的时候填入的rul、安全策略、用户名认证进行连接,之后读取里面的值,你可以具体选择一些项进行导入。之后我们就可以在新建的设备上看到他们。注意看,默认的ns确实是2,具体的indentifers名组成为:通道名.设备名.标签名。
这时候就会生成一个客户端,需要驱动进行运行,保持客户端的活性,这是软件官方的付费功能,只能免费运行两小时,之后就会被取消链接。
运行项目
项目架构:
项目运行入口在Controller层的RunDemo。
入口函数内,会创建一个OpcUAClientRunner对象,并将IOC容器中的opcUAClientService传进去,并调用前者的run方法。
进入OpcUAClientRunner类的run方法后,又会调用同类createClient()方法,这是具体的创建连接的方法,在里面会创建安全证书保存路径、加载安全证书(使用到了KeyStoreLoader类)、寻找策略,最终返回连接client。
回到run()方法,利用该链接和刚刚传进来的opcUAClientService,调用后者的run()方法,进去后,调用业务层的代码。
因此如果需要对业务做出改变,只需要更改业务层的代码和类即可。
1、clone项目
地址:UseMilo
2、修改用户
修改OpcUAClientRunner类、OpcUAClientService接口里面的密码为我们设置的用户密码,使用用户名和密码认证。
3、修改端口URL
在OpcUAClientService接口中修改服务器端口,没有更改的话,使用默认的即可。
4、修改安全策略
在OpcUAClientService接口修改项目里的安全策略,选择KEPServerex6里面我们勾选的其中一个。
5、运行生成证书
运行一次项目,生成证书文件,打开KEPServerEX6的OPCUA配置,选择受信任的客户端,信任我们刚刚生成的证书。
6、信任客户端
修改项目的业务代码,具体将读值方法的identifier属性修改成自己创建的tag标签名。
7、直接运行RunDemo。
项目代码解读:
1、KeyStoreLoader类
该类用于生成一个客户端访问的证书,提交给服务器,该类内容无需更改。我们只需要运行一遍代码之后生成证书,之后在KEPServerEX6中的OPCUA配置中信任客户端,即信任该证书即可。
这里的PASSWORD = password,是证书的私钥,我们不用管。
class KeyStoreLoader { private static final Pattern IP_ADDR_PATTERN = Pattern.compile( "^(([01]?//d//d?|2[0-4]//d|25[0-5])//.){3}([01]?//d//d?|2[0-4]//d|25[0-5])$"); private static final String CLIENT_ALIAS = "client-ai"; private static final char[] PASSWORD = "password".toCharArray(); private final Logger logger = LoggerFactory.getLogger(getClass()); private X509Certificate[] clientCertificateChain; private X509Certificate clientCertificate; private KeyPair clientKeyPair; KeyStoreLoader load(Path baseDir) throws Exception { KeyStore keyStore = KeyStore.getInstance("PKCS12"); Path serverKeyStore = baseDir.resolve("example-client.pfx"); logger.info("Loading KeyStore at {}", serverKeyStore); if (!Files.exists(serverKeyStore)) { keyStore.load(null, PASSWORD); KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048); SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair) .setCommonName("Eclipse Milo Example Client") .setOrganization("digitalpetri") .setOrganizationalUnit("dev") .setLocalityName("Folsom") .setStateName("CA") .setCountryCode("US") .setApplicationUri("urn:eclipse:milo:examples:client") .addDnsName("localhost") .addIpAddress("127.0.0.1"); // Get as many hostnames and IP addresses as we can listed in the certificate. for (String hostname : HostnameUtil.getHostnames("0.0.0.0")) { if (IP_ADDR_PATTERN.matcher(hostname).matches()) { builder.addIpAddress(hostname); } else { builder.addDnsName(hostname); } } X509Certificate certificate = builder.build(); keyStore.setKeyEntry(CLIENT_ALIAS, keyPair.getPrivate(), PASSWORD, new X509Certificate[]{certificate}); try (OutputStream out = Files.newOutputStream(serverKeyStore)) { keyStore.store(out, PASSWORD); } } else { try (InputStream in = Files.newInputStream(serverKeyStore)) { keyStore.load(in, PASSWORD); } } Key clientPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD); if (clientPrivateKey instanceof PrivateKey) { clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS); clientCertificateChain = Arrays.stream(keyStore.getCertificateChain(CLIENT_ALIAS)) .map(X509Certificate.class::cast) .toArray(X509Certificate[]::new); PublicKey serverPublicKey = clientCertificate.getPublicKey(); clientKeyPair = new KeyPair(serverPublicKey, (PrivateKey) clientPrivateKey); } return this; } X509Certificate getClientCertificate() { return clientCertificate; } public X509Certificate[] getClientCertificateChain() { return clientCertificateChain; } KeyPair getClientKeyPair() { return clientKeyPair; }}
如果你在跑了一次代码之后,修改了相应的内容,发现证书信任了同样连接不上,你可以尝试到你保存证书的目录删除,因为存在证书就不会再次生成证书进行覆盖。
C:/Users/用户名/AppData/Local/Temp/security(注意填充系统用户名)
2、Endpoints端口
在OpcUAClientRunner类中创建客户端的时候,需要传入客户端的配置,就需要设置具体的端口,
获取端口的时候,会利用给的url和端口号进去查找,查找的是milo项目内原本的安全策略项,一般有四个,是全部获取。我们提供的一个URL会和4个安全策略,生成4哥endpoint。
之后根据我们在前面<运行代码--2修改安全策略>做出的修改进行筛选,最后选取我们在代码中写入的安全策略,将该endpoint过滤出来(过滤方式有两种)。
3、地址空间AdressSpace、节点Node
假设你认识OPCUA协议,我们应该知道,协议的重点是节点和引用,它们共同组成地址空间。它们都会采用对象模型作为信息模型,在Java中,我们表现为一个具体的类。
也就是说我们的值存在节点里面,通过地址空间可以找到节点。所以在对节点进行操作的时候,我们需要标识一个节点,就通过两个属性,第一个是namespaceIndex,也就是地址空间索引,我们使用KEPServerEX6的话,默认是2,因此基本都填2。
NodeId(int namespaceIndex, String identifier)
indentifers是标识,它用来确定服务器器内具体的tag,由通道名.设备名.标签名构成。
可能出现的错误总结:
1、证书没有被信任
运行一遍代码之后才生成证书,才能在KEPServerEX6中的OPCUA配置中信任客户端。
右击运行图标----OPCUA配置,受信任的客户端,点击列表中被红色叉标记的项,点击信任。
如果没有信任证书,就会报错:
[org.eclipse.milo.opcua.stack.client.ClientChannelManager]-Channel bootstrap failed: An error occurred verifying security.UaException: status=Bad_SecurityChecksFailed, message=An error occurred verifying security. at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.onError(UaTcpClientMessageHandler.java:716) at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.decodeMessage(UaTcpClientMessageHandler.java:411) at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.decode(UaTcpClientMessageHandler.java:392) at io.netty.handler.codec.ByteToMessageCodec$1.decode(ByteToMessageCodec.java:42) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:387) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:245) at io.netty.handler.codec.ByteToMessageCodec.channelRead(ByteToMessageCodec.java:103) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:962) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:485) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:399) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:371) at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112) at java.lang.Thread.run(Thread.java:750)[com.milo.OpcUAClientRunner]-OPC UA客户端运行错误: UaException: status=Bad_SecurityChecksFailed, message=An error occurred verifying security.java.util.concurrent.ExecutionException: UaException: status=Bad_SecurityChecksFailed, message=An error occurred verifying security. at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908) at com.milo.OpcUAClientServiceImpl.run(OpcUAClientServiceImpl.java:25) at com.milo.OpcUAClientRunner.run(OpcUAClientRunner.java:61) at com.controller.RunDemo.main(RunDemo.java:39)Caused by: UaException: status=Bad_SecurityChecksFailed, message=An error occurred verifying security. at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.onError(UaTcpClientMessageHandler.java:716) at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.decodeMessage(UaTcpClientMessageHandler.java:411) at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.decode(UaTcpClientMessageHandler.java:392) at io.netty.handler.codec.ByteToMessageCodec$1.decode(ByteToMessageCodec.java:42) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:387) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:245) at io.netty.handler.codec.ByteToMessageCodec.channelRead(ByteToMessageCodec.java:103) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:962) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:485) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:399) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:371) at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112) at java.lang.Thread.run(Thread.java:750)[com.milo.OpcUAClientRunner]-连接OPC UA服务错误: UaException: status=Bad_SecurityChecksFailed, message=An error occurred verifying security.java.util.concurrent.ExecutionException: UaException: status=Bad_SecurityChecksFailed, message=An error occurred verifying security. at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908) at com.milo.OpcUAClientServiceImpl.run(OpcUAClientServiceImpl.java:25) at com.milo.OpcUAClientRunner.run(OpcUAClientRunner.java:61) at com.controller.RunDemo.main(RunDemo.java:39)Caused by: UaException: status=Bad_SecurityChecksFailed, message=An error occurred verifying security. at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.onError(UaTcpClientMessageHandler.java:716) at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.decodeMessage(UaTcpClientMessageHandler.java:411) at org.eclipse.milo.opcua.stack.client.handlers.UaTcpClientMessageHandler.decode(UaTcpClientMessageHandler.java:392) at io.netty.handler.codec.ByteToMessageCodec$1.decode(ByteToMessageCodec.java:42) at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:387) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:245) at io.netty.handler.codec.ByteToMessageCodec.channelRead(ByteToMessageCodec.java:103) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:292) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:278) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:962) at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:131) at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528) at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:485) at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:399) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:371) at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:112) at java.lang.Thread.run(Thread.java:750)
2、ApplicationURL不匹配
此外,在证书生成类中的,setApplicationUri中的值必须跟创建创建客户端createClient()的时候填入的setApplicationUri的值保持一致,因为创建的客户端连接,就是通过证书被服务器放行的。
如果不这么做就会导致报错误:
java.util.concurrent.CompletionException: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate. at java.util.concurrent.CompletableFuture.encodeRelay(CompletableFuture.java:326) at java.util.concurrent.CompletableFuture.completeRelay(CompletableFuture.java:338) at java.util.concurrent.CompletableFuture.uniRelay(CompletableFuture.java:925) at java.util.concurrent.CompletableFuture$UniRelay.tryFire(CompletableFuture.java:913) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488) at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:1990) at org.eclipse.milo.opcua.stack.client.UaTcpStackClient.lambda$receiveResponse$16(UaTcpStackClient.java:367) at org.eclipse.milo.opcua.stack.core.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:107) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:750)Caused by: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate. ... 7 more[org.eclipse.milo.opcua.sdk.client.session.SessionFsm]-S(Creating)xE(CreateSessionFailureEvent) = S(Inactive)[com.milo.OpcUAClientRunner]-OPC UA客户端运行错误: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate.java.util.concurrent.ExecutionException: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate. at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908) at com.milo.OpcUAClientServiceImpl.run(OpcUAClientServiceImpl.java:25) at com.milo.OpcUAClientRunner.run(OpcUAClientRunner.java:61) at com.controller.RunDemo.main(RunDemo.java:39)Caused by: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate. at org.eclipse.milo.opcua.stack.client.UaTcpStackClient.lambda$receiveResponse$16(UaTcpStackClient.java:367) at org.eclipse.milo.opcua.stack.core.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:107) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:750)[com.milo.OpcUAClientRunner]-连接OPC UA服务错误: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate.java.util.concurrent.ExecutionException: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate. at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357) at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1908) at com.milo.OpcUAClientServiceImpl.run(OpcUAClientServiceImpl.java:25) at com.milo.OpcUAClientRunner.run(OpcUAClientRunner.java:61) at com.controller.RunDemo.main(RunDemo.java:39)Caused by: UaServiceFaultException: status=Bad_CertificateUriInvalid, message=The URI specified in the ApplicationDescription does not match the URI in the Certificate. at org.eclipse.milo.opcua.stack.client.UaTcpStackClient.lambda$receiveResponse$16(UaTcpStackClient.java:367) at org.eclipse.milo.opcua.stack.core.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:107) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:750)
3、获取不到端点值
代码配置的安全策略和KEPServerEX6中服务器配置的安全策略不一致时,会报错:
请及时修改代码中的安全策略,或者修改服务器中的安全策略,并重新初始化服务器。
java.util.concurrent.CompletionException: UaServiceFaultException: status=Bad_Shutdown, message=The operation was cancelled because the application is shutting down. at java.util.concurrent.CompletableFuture.encodeRelay(CompletableFuture.java:326) at java.util.concurrent.CompletableFuture.completeRelay(CompletableFuture.java:338) at java.util.concurrent.CompletableFuture.uniRelay(CompletableFuture.java:925) at java.util.concurrent.CompletableFuture$UniRelay.tryFire(CompletableFuture.java:913) at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:488) at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:1990) at org.eclipse.milo.opcua.stack.client.UaTcpStackClient.lambda$receiveResponse$16(UaTcpStackClient.java:367) at org.eclipse.milo.opcua.stack.core.util.ExecutionQueue$PollAndExecute.run(ExecutionQueue.java:107) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at java.lang.Thread.run(Thread.java:750)Caused by: UaServiceFaultException: status=Bad_Shutdown, message=The operation was cancelled because the application is shutting down. ... 7 more[org.eclipse.milo.opcua.sdk.client.session.SessionFsm]-S(Creating) x E(CreateSessionFailureEvent) = S'(Inactive)
4、连接上了读取不到值、读空值null
该问题可能性比较多。
- 可能是因为安全策略不匹配,但是服务器允许“无”,即匿名登陆,导致连接的上,但是没有权限获取得到值。
- 可能是因为地址空间索引ns填错,如果没有进行特殊的修改,服务器中默认的ns为2。
- 可能是因为节点的identifier标识使用了中文,安装KEPServerEX6的时候,可以让我们选择简体中文,但是连接的时候,设备的标识使用了中文,却连不上。本人因为这个问题调了2天的Bug。猜测是我这个版本的KEPServerEX6内核只能支持英文。
- 或许更直接一点,可能是因为节点的identifier节点填错了
5、忽略channel...错误
本项目跑起来会用日志抛出两个一样的异常:org.eclipse.milo.opcua.stack.client.ClientChannelManager.connect:
第一个出现在:
java.lang.Exception at org.eclipse.milo.opcua.stack.client.ClientChannelManager.connect(ClientChannelManager.java:67) at org.eclipse.milo.opcua.stack.client.UaTcpStackClient.connect(UaTcpStackClient.java:127) at org.eclipse.milo.opcua.stack.client.UaTcpStackClient.getEndpoints(UaTcpStackClient.java:577) at com.milo.OpcUAClientRunner.createClient(OpcUAClientRunner.java:88) at com.milo.OpcUAClientRunner.run(OpcUAClientRunner.java:43) at com.controller.RunDemo.main(RunDemo.java:39)
第二个出现在:
java.lang.Exception at org.eclipse.milo.opcua.stack.client.ClientChannelManager.connect(ClientChannelManager.java:67) at org.eclipse.milo.opcua.stack.client.UaTcpStackClient.connect(UaTcpStackClient.java:127) at org.eclipse.milo.opcua.sdk.client.OpcUaClient.connect(OpcUaClient.java:312) at com.milo.OpcUAClientServiceImpl.run(OpcUAClientServiceImpl.java:25) at com.milo.OpcUAClientRunner.run(OpcUAClientRunner.java:61) at com.controller.RunDemo.main(RunDemo.java:39)
这两个错误都不会影响我们进行连接、读取。
我的理解是,由客户端主动发起的连接关闭,两次都像是一种确认,确认服务器可达。第一次确定后获得服务器的安全策略,第二次确认后获得服务器的连接。
6、获取不到端点,地址解析失败
0PC UA客户端运行误: java,nio.channels,UnresolvedAddressExceptionjava.util.concurrent.ExecutionException Create breakpoint:java.nio.channels.UnresolvedAddressException
通过以下方式排查:
1、URL、安全策略、用户认证填写正确,在Client.connect().get()打断点,判断有没有成功生成Client。
2、打开Client属性,下滑到最后一个个找到config,依序打开,stackClientConfig---endpoint---查看endpointURl。
3、如果你看到的是域名,而不是ip,但是你在填写url却填入的是ip。这是因为主机连接内网之后,本地域名服务器刷新,无法解析域名。
4、我们只需要 用管理员身份 运行记事本,然后打开C:/windows/system32/drivers/etc的hosts文件,填入最后一行,格式为:IP地址 域名
5、注意中间有一个tab符。
6、测试成功。
更新订阅功能:
已经实现订阅功能:
private void subscribe(OpcUaClient client) throws Exception { AtomicInteger atomic = new AtomicInteger(1); //创建发布间隔1000ms的订阅对象 client .getSubscriptionManager() .createSubscription(1000.0) .thenAccept(t -> { //节点1 NodeId nodeId1 = new NodeId(2,"my.device.x1"); ReadValueId readValueId1 = new ReadValueId(nodeId1, AttributeId.Value.uid(), null, null); //节点2 NodeId nodeId2 = new NodeId(2,"my.device.x2"); ReadValueId readValueId2 = new ReadValueId(nodeId2, AttributeId.Value.uid(), null, null); //创建监控的参数 MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(atomic.getAndIncrement()), 1000.0, null, UInteger.valueOf(10), true); //创建监控项请求 //该请求最后用于创建订阅。 MonitoredItemCreateRequest request1 = new MonitoredItemCreateRequest(readValueId1, MonitoringMode.Reporting, parameters); MonitoredItemCreateRequest request2 = new MonitoredItemCreateRequest(readValueId2, MonitoringMode.Reporting, parameters); List<MonitoredItemCreateRequest> requests = new ArrayList<>(); requests.add(request1); requests.add(request2); //创建监控项,并且注册变量值改变时候的回调函数。 t.createMonitoredItems( TimestampsToReturn.Both, requests, (item, id) -> item.setValueConsumer((it, val) -> { System.out.println("标识为" + it.getReadValueId().getNodeId()+"的项的值被更新为:"+ val.getValue().getValue()); }) ); }).get(); //持续订阅 Thread.sleep(Long.MAX_VALUE); }
订阅功能参考文献:SpringBoot集成Milo库实现OPC UA客户端:连接、遍历节点、读取、写入、订阅与批量订阅-CSDN博客