package cn.stylefeng.guns.modular.opcua.client; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Predicate; import cn.stylefeng.guns.modular.opcua.cert.KeyStoreLoader; import cn.stylefeng.guns.modular.opcua.config.Properties; 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.AnonymousProvider; import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider; import org.eclipse.milo.opcua.stack.client.DiscoveryClient; import org.eclipse.milo.opcua.stack.core.Stack; import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy; import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText; import lombok.extern.slf4j.Slf4j; import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned; import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @ClassName: ClientRunner * @Description: 客户端启动类 * @author yyt * @date 2023年10月13日 */ @Slf4j @Component public class ClientRunner { private final CompletableFuture future = new CompletableFuture<>(); @Autowired private Properties properties; @Autowired private KeyStoreLoader keyStoreLoader; /** * @MethodName: run * @Description: 启动 * @return * @throws Exception * @CreateTime 2023年10月13日 */ public OpcUaClient run() throws Exception { OpcUaClient client = createClient(); future.whenCompleteAsync((c, ex) -> { if (ex != null) { log.error("Error running example: {}", ex.getMessage(), ex); } try { c.disconnect().get(); Stack.releaseSharedResources(); } catch (InterruptedException | ExecutionException e) { log.error("Error disconnecting:", e.getMessage(), e); } }); return client; } /** * @MethodName: createClient * @Description: 创建客户端 * @return * @throws Exception * @CreateTime 2023年10月13日 */ private OpcUaClient createClient() throws Exception { Path securityTempDir = Paths.get(properties.getCertPath(), "security"); Files.createDirectories(securityTempDir); if (!Files.exists(securityTempDir)) { log.error("unable to create security dir: " + securityTempDir); return null; } KeyStoreLoader loader = keyStoreLoader.load(securityTempDir); // 搜索OPC节点 List endpoints = null; try { //获取安全策略 endpoints = DiscoveryClient.getEndpoints(properties.getEndpointUrl()).get(); } catch (Throwable e) { // try the explicit discovery endpoint as well String discoveryUrl = properties.getEndpointUrl(); if (!discoveryUrl.endsWith("/")) { discoveryUrl += "/"; } discoveryUrl += "discovery"; log.info("Trying explicit discovery URL: {}", discoveryUrl); endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get(); } EndpointDescription endpoint = endpoints.stream() .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri())).filter(endpointFilter()) .findFirst().orElseThrow(() -> new Exception("no desired endpoints returned")); OpcUaClientConfig config = OpcUaClientConfig.builder() // opc ua自定义的名称 .setApplicationName(LocalizedText.english("plc")) // 地址 .setApplicationUri(properties.getEndpointUrl()) .setCertificate(loader.getClientCertificate()).setKeyPair(loader.getClientKeyPair()) // 安全策略等配置 //.setEndpoint(endpoint).setIdentityProvider(new UsernameProvider("OPCUA", "yyt@8888888888")) .setEndpoint(endpoint) // 匿名验证 .setIdentityProvider(new AnonymousProvider()) //等待时间 .setRequestTimeout(Unsigned.uint(5000)).build(); return OpcUaClient.create(config); } /** * @MethodName: endpointFilter * @Description: endpointFilter * @return * @CreateTime 2023年10月13日 */ private Predicate endpointFilter() { return e -> true; } /** * @return the future */ public CompletableFuture getFuture() { return future; } }