本篇文章小编给大家分享一下使用java的milo框架访问OPCUA服务的过程代码,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看。
本次采用KEPServerEX5模拟服务端,使用milo开发的程序作为客户端
一、搭建服务端,KEPServerEX5的安装省略掉,下面是配置过程
设置通道、设备、标签
设置访问的用户名和密码
设置通过opc-ua访问的节点
二、使用milo的框架,开发客户端访问opcua服务
1、在pom文件中追击以下依赖
org.eclipse.milo sdk-client 0.2.4 org.bouncycastle bcpkix-jdk15on 1.57 org.eclipse.milo sdk-server 0.2.4
2、OPC UA协议对象接口
package com.jndj.platform.common.milo; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider; import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider; import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy; import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription; import java.util.concurrent.CompletableFuture; import java.util.function.Predicate; /** * @author yaohj * @date 2020/7/30 * OPC UA协议对象接口 */ public interface OpcUAClientService { /** * OPC UA服务器地址和接口 */ default String getEndpointUrl() { return "opc.tcp://127.0.0.1:49320"; } /** * 过滤返回的server endpoint */ default PredicateendpointFilter() { return e -> true; } /** * 连接服务器的安全策略 * None、Basic128Rsa15、Basic256、Basic256Sha256、Aes128_Sha256_RsaOaep、Aes256_Sha256_RsaPss */ default SecurityPolicy getSecurityPolicy() { return SecurityPolicy.None; } /** * 提供身份验证 */ default IdentityProvider getIdentityProvider() { return new AnonymousProvider(); } /** * 实际操作服务、由实现类重写实现 */ void run(OpcUaClient client, CompletableFuture future) throws Exception; }
3、OPC UA协议对象实体类
package com.jndj.platform.common.milo; import com.google.common.collect.ImmutableList; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.stack.core.types.builtin.*; import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.CompletableFuture; @Service("OpcUAClientService") public class OpcUAClientServiceImpl implements OpcUAClientService { /** * 覆盖接口的方法,建立和OPC UA的服务 */ @Override public void run(OpcUaClient client, CompletableFuturefuture) throws Exception { // 同步建立连接 client.connect().get(); // 异步读取数据 readTagData(client).thenAccept(values -> { DataValue nodeId_Tag1 = values.get(0); DataValue nodeId_Tag2 = values.get(1); System.out.println("#########Tag1=" + nodeId_Tag1.getValue().getValue()); System.out.println("#########Tag2=" + nodeId_Tag2.getValue().getValue()); future.complete(client); }); } /** * 读取标签点的数据 */ private CompletableFuture > readTagData(OpcUaClient client) { NodeId nodeId_Tag1 = new NodeId(2, "Channel1.Device1.Tag1"); NodeId nodeId_Tag2 = new NodeId(2, "Channel1.Device1.Tag2"); List
nodeIds = ImmutableList.of(nodeId_Tag1, nodeId_Tag2); return client.readValues(0.0, TimestampsToReturn.Both, nodeIds); } }
4、OPC UA协议运行对象
package com.jndj.platform.common.milo; import org.eclipse.milo.opcua.sdk.client.OpcUaClient; import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig; import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider; import org.eclipse.milo.opcua.stack.client.UaTcpStackClient; import org.eclipse.milo.opcua.stack.core.Stack; import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; @Service("OpcUAClientRunner") public class OpcUAClientRunner { private final Logger logger = LoggerFactory.getLogger(getClass()); private final CompletableFuturefuture = new CompletableFuture<>(); private final OpcUAClientService opcUAClientService; public OpcUAClientRunner(OpcUAClientService opcUAClientService) { this.opcUAClientService = opcUAClientService; } /** * OPC UA的运行入口程序 */ public void run() { try { // 创建OPC UA客户端 OpcUaClient opcUaClient = createClient(); // future执行完毕后,异步判断状态 future.whenCompleteAsync((c, ex) -> { if (ex != null) { logger.error("连接OPC UA服务错误: {}", ex.getMessage(), ex); } // 关闭OPC UA客户端 try { opcUaClient.disconnect().get(); Stack.releaseSharedResources(); } catch (InterruptedException | ExecutionException e) { logger.error("OPC UA服务关闭错误: {}", e.getMessage(), e); } }); try { // 获取OPC UA服务器的数据 opcUAClientService.run(opcUaClient, future); future.get(5, TimeUnit.SECONDS); } catch (Throwable t) { logger.error("OPC UA客户端运行错误: {}", t.getMessage(), t); future.completeExceptionally(t); } } catch (Throwable t) { logger.error("OPC UA客户端创建错误: {}", t.getMessage(), t); future.completeExceptionally(t); } } /** * 创建OPC UA的服务连接对象 */ private OpcUaClient createClient() throws Exception { Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security"); Files.createDirectories(securityTempDir); if (!Files.exists(securityTempDir)) { throw new Exception("不能够创建安全路径: " + securityTempDir); } KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir); // 获取OPC UA的服务器端节点 EndpointDescription[] endpoints = UaTcpStackClient.getEndpoints(opcUAClientService.getEndpointUrl()).get(); EndpointDescription endpoint = Arrays.stream(endpoints) .filter(e -> e.getEndpointUrl().equals(opcUAClientService.getEndpointUrl())) .findFirst().orElseThrow(() -> new Exception("没有节点返回")); // 设置OPC UA的配置信息 OpcUaClientConfig config = OpcUaClientConfig.builder() .setApplicationName(LocalizedText.english("OPC UA SCREEN")) .setApplicationUri("urn:DATA-TRANSFER:OPC UA SCREEN") .setCertificate(loader.getClientCertificate()) .setKeyPair(loader.getClientKeyPair()) .setEndpoint(endpoint) .setIdentityProvider(new UsernameProvider("Administrator", "123456")) .setRequestTimeout(uint(5000)) .build(); // 创建OPC UA客户端 return new OpcUaClient(config); } }
5、OPC UA访问证书类
package com.jndj.platform.common.milo; import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder; import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.security.*; import java.security.cert.X509Certificate; import java.util.regex.Pattern; class KeyStoreLoader { private final Logger logger = LoggerFactory.getLogger(getClass()); private static final Pattern IP_ADDR_PATTERN = Pattern.compile( "^(([01]?dd?|2[0-4]d|25[0-5]).){3}([01]?dd?|2[0-4]d|25[0-5])$"); // 证书别名 private static final String CLIENT_ALIAS = "client-ai"; // 获取私钥的密码 private static final char[] PASSWORD = "password".toCharArray(); // 证书对象 private X509Certificate clientCertificate; // 密钥对对象 private KeyPair clientKeyPair; KeyStoreLoader load(Path baseDir) throws Exception { // 创建一个使用`PKCS12`加密标准的KeyStore。KeyStore在后面将作为读取和生成证书的对象。 KeyStore keyStore = KeyStore.getInstance("PKCS12"); // PKCS12的加密标准的文件后缀是.pfx,其中包含了公钥和私钥。 // 而其他如.der等的格式只包含公钥,私钥在另外的文件中。 Path serverKeyStore = baseDir.resolve("example-client.pfx"); logger.info("Loading KeyStore at {}", serverKeyStore); // 如果文件不存在则创建.pfx证书文件。 if (!Files.exists(serverKeyStore)) { keyStore.load(null, PASSWORD); // 用2048位的RAS算法。`SelfSignedCertificateGenerator`为Milo库的对象。 KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048); // `SelfSignedCertificateBuilder`也是Milo库的对象,用来生成证书。 // 中间所设置的证书属性可以自行修改。 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 serverPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD); if (serverPrivateKey instanceof PrivateKey) { // 获取对应别名的证书对象。 clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS); // 获取公钥 PublicKey serverPublicKey = clientCertificate.getPublicKey(); // 创建Keypair对象。 clientKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey); } return this; } // 返回证书 X509Certificate getClientCertificate() { return clientCertificate; } // 返回密钥对 KeyPair getClientKeyPair() { return clientKeyPair; } }
6、业务service类
package com.jndj.platform.phase2.service.impl; import com.jndj.platform.common.milo.OpcUAClientRunner; import com.jndj.platform.common.milo.OpcUAClientService; import com.jndj.platform.phase2.service.Phase2Service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @author yaohj * @date 2020/7/22 * 获取二期发电数据(三、四号机组) */ @Service("phase2Service") public class Phase2ServiceImpl implements Phase2Service { @Autowired private OpcUAClientService opcUAClientService; /** * 获取二期发电数据(三、四号机组),保存到数据库中 */ @Override public void searchPhase2ElectricData() { new OpcUAClientRunner(opcUAClientService).run(); } }
7、业务Controller类、定时调度
package com.jndj.platform.phase2.controller; import com.jndj.platform.phase2.service.Phase2Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Controller; import java.text.SimpleDateFormat; import java.util.Date; /** * @author yaohj * @date 2020/7/22 * 获取二期发电数据(三、四号机组) */ @Controller public class Phase2Controller { private static final Logger logger = LoggerFactory.getLogger(Phase2Controller.class); private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy:mm:dd HH:mm:ss"); @Autowired private Phase2Service phase2Service; /** * 获取二期发电数据(三、四号机组),保存到数据库中(x分钟调度一次) */ @Scheduled(initialDelay = 30000, fixedRate = 30000) public void searchGasData() { logger.info("####获取二期发电数据(三、四号机组) - 定时任务执行时间:"+ dateFormat.format(new Date())); phase2Service.searchPhase2ElectricData(); } }
8、运行结果、定时获取到opcua服务中的数据
忍者必须死34399账号登录版 最新版v1.0.138v2.0.72
下载勇者秘境oppo版 安卓版v1.0.5
下载忍者必须死3一加版 最新版v1.0.138v2.0.72
下载绝世仙王官方正版 最新安卓版v1.0.49
下载Goat Simulator 3手机版 安卓版v1.0.8.2
Goat Simulator 3手机版是一个非常有趣的模拟游
Goat Simulator 3国际服 安卓版v1.0.8.2
Goat Simulator 3国际版是一个非常有趣的山羊模
烟花燃放模拟器中文版 2025最新版v1.0
烟花燃放模拟器是款仿真的烟花绽放模拟器类型单机小游戏,全方位
我的世界动漫世界 手机版v友y整合
我的世界动漫世界模组整合包是一款加入了动漫元素的素材整合包,
我的世界贝爷生存整合包 最新版v隔壁老王
我的世界MITE贝爷生存整合包是一款根据原版MC制作的魔改整