【OPCUA】使用Java开源项目milo连接KEPServerEX6服务器并读取值

服务器 0

准备工作:

开发环境: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博客

也许您对下面的内容还感兴趣: