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

准备工作:

开发环境: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,UnresolvedAddressException
java.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博客

版权声明:本文为博主作者:寻欢无罪¸原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/MarcherXL/article/details/134259584

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2024年1月16日
下一篇 2024年1月16日

相关推荐