package com.iailab.module.data.channel.opcua.collector; import com.alibaba.fastjson.JSONObject; import com.iailab.module.data.channel.opcua.dto.ChannelOPCUADeviceDTO; import lombok.extern.slf4j.Slf4j; 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.IdentityProvider; import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider; import org.eclipse.milo.opcua.sdk.client.nodes.UaNode; import org.eclipse.milo.opcua.stack.client.DiscoveryClient; import org.eclipse.milo.opcua.stack.core.Identifiers; import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy; import org.eclipse.milo.opcua.stack.core.types.builtin.*; import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn; import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription; import org.springframework.stereotype.Component; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Objects; import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint; /** * @author PanZhibao * @Description * @createTime 2023年05月12日 15:36:00 */ @Slf4j @Component public class OpcUaUtils { private final String CONNECTION_TYPE_USER = "1"; private final String SECURITY_POLICY_BASIC256 = "Basic256"; private final String SECURITY_POLICY_BASIC128RSA15 = "Basic128Rsa15"; private final String SECURITY_POLICY_BASIC256SHA256 = "Basic256Sha256"; public synchronized OpcUaClient createClient(ChannelOPCUADeviceDTO configDto) throws Exception { OpcUaClient opcUaClient = null; try { log.info("OpcuaDTO" + JSONObject.toJSONString(configDto)); // 加载秘钥 // Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security"); log.info("加载秘钥"); Path securityTempDir = Paths.get("D:\\DLUT\\pfx"); Files.createDirectories(securityTempDir); if (!Files.exists(securityTempDir)) { throw new Exception("没有创建安全目录: " + securityTempDir); } KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir); // 安全策略 log.info("安全策略"); SecurityPolicy securityPolicy = getSecurityPolicy(configDto.getSecurityPolicy()); // 验证方式 log.info("验证方式"); IdentityProvider identityProvider = getIdentityProvider(configDto); // opc.tcp://DESKTOP-D7CDEIF:53530/OPCUA/SimulationServer // String endpointUrl = "opc.tcp://127.0.0.1:53530"; log.info("连接端点"); List endpoints = DiscoveryClient.getEndpoints(configDto.getEndpointUrl()).get(); endpoints.forEach(item -> { log.info("EndpointUrl=" + item.getEndpointUrl()); }); EndpointDescription endpoint = endpoints.stream() .filter(e -> e.getSecurityPolicyUri().equals(securityPolicy.getUri())) .findFirst() .orElseThrow(() -> new Exception("没有连接上端点")); EndpointDescription newEndpoint = updateEndpointUrl(endpoint, getHostFromEndPointUrl(configDto.getEndpointUrl())); log.info("NewEndpointUrl=" + newEndpoint.getEndpointUrl()); OpcUaClientConfig config = OpcUaClientConfig.builder() .setApplicationName(LocalizedText.english("eclipse milo opc-ua client")) .setApplicationUri("") .setCertificate(loader.getClientCertificate()) .setKeyPair(loader.getClientKeyPair()) .setEndpoint(newEndpoint) .setIdentityProvider(identityProvider) .setRequestTimeout(uint(5000)) .build(); opcUaClient = OpcUaClient.create(config); log.info("创建客户端完成"); } catch (Exception e) { e.printStackTrace(); log.error("创建客户端失败" + e.getMessage()); throw new Exception(e.getMessage()); } return opcUaClient; } private String getHostFromEndPointUrl (String endPointUrl) { int indexStart = endPointUrl.lastIndexOf("//"); int indexEnd = endPointUrl.lastIndexOf(":"); String host = endPointUrl.substring(indexStart, indexEnd).replace("//",""); log.info(host); return host; } private EndpointDescription updateEndpointUrl(EndpointDescription original, String hostname) throws URISyntaxException { URI uri = new URI(original.getEndpointUrl()).parseServerAuthority(); String endpointUrl = String.format( "%s://%s:%s%s", uri.getScheme(), hostname, uri.getPort(), uri.getPath() ); return new EndpointDescription( endpointUrl, original.getServer(), original.getServerCertificate(), original.getSecurityMode(), original.getSecurityPolicyUri(), original.getUserIdentityTokens(), original.getTransportProfileUri(), original.getSecurityLevel() ); } private IdentityProvider getIdentityProvider(ChannelOPCUADeviceDTO configDto) { switch (configDto.getConnectionType()) { case CONNECTION_TYPE_USER: return new UsernameProvider(configDto.getUserName(), configDto.getPassword()); default: return new AnonymousProvider(); } } private SecurityPolicy getSecurityPolicy(String type) { switch (type) { case SECURITY_POLICY_BASIC256: return SecurityPolicy.Basic256; case SECURITY_POLICY_BASIC128RSA15: return SecurityPolicy.Basic128Rsa15; case SECURITY_POLICY_BASIC256SHA256: return SecurityPolicy.Basic256Sha256; default: return SecurityPolicy.None; } } /** * 遍历树形节点 * * @param client OPC UA客户端 * @param uaNode 节点 * @throws Exception */ private static void browseNode(OpcUaClient client, UaNode uaNode) throws Exception { List nodes; if (uaNode == null) { nodes = client.getAddressSpace().browseNodes(Identifiers.ObjectsFolder); } else { nodes = client.getAddressSpace().browseNodes(uaNode); } for (UaNode nd : nodes) { //排除系统行性节点,这些系统性节点名称一般都是以"_"开头 if (Objects.requireNonNull(nd.getBrowseName().getName()).contains("_")) { continue; } System.out.println("Node= " + nd.getBrowseName().getName()); browseNode(client, nd); } } /** * 读取节点数据 * namespaceIndex可以通过UaExpert客户端去查询,一般来说这个值是2。 * identifier也可以通过UaExpert客户端去查询,这个值=通道名称.设备名称.标记名称 * * @param client OPC UA客户端 * @param nodeIdStr 标识符 * @throws Exception */ public static String readNode(OpcUaClient client, String nodeIdStr) throws Exception { String[] nodeIdArr = nodeIdStr.split(";"); int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]); String identifier = nodeIdArr[1].split("=")[1]; //节点 NodeId nodeId = new NodeId(namespaceIndex, identifier); //读取节点数据 DataValue value = client.readValue(0.0, TimestampsToReturn.Neither, nodeId).get(); identifier = String.valueOf(nodeId.getIdentifier()); System.out.println(identifier + ": " + String.valueOf(value.getValue().getValue())); return value.getValue().getValue().toString(); } /** * 写入节点数据 * * @param client * @param nodeIdStr * @param nodeValue */ public static void writeIntValue(OpcUaClient client, String nodeIdStr, Integer nodeValue) { String[] nodeIdArr = nodeIdStr.split(";"); int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]); String identifier = nodeIdArr[1].split("=")[1]; NodeId nodeId = new NodeId(namespaceIndex, identifier); DataValue newValue = new DataValue(new Variant(nodeValue), null, null); //写入节点数据 StatusCode statusCode = client.writeValue(nodeId, newValue).join(); System.out.println("结果:" + statusCode.isGood()); } /** * 写入节点数据 * * @param client * @param nodeIdStr * @param nodeValue */ public static void writeFloatValue(OpcUaClient client, String nodeIdStr, Float nodeValue) { String[] nodeIdArr = nodeIdStr.split(";"); int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]); String identifier = nodeIdArr[1].split("=")[1]; NodeId nodeId = new NodeId(namespaceIndex, identifier); DataValue newValue = new DataValue(new Variant(nodeValue), null, null); //写入节点数据 StatusCode statusCode = client.writeValue(nodeId, newValue).join(); System.out.println("结果:" + statusCode.isGood()); } /** * 写入节点数据 * * @param client * @param nodeIdStr * @param nodeValue */ public static void writeBooleanValue(OpcUaClient client, String nodeIdStr, Boolean nodeValue) { String[] nodeIdArr = nodeIdStr.split(";"); int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]); String identifier = nodeIdArr[1].split("=")[1]; NodeId nodeId = new NodeId(namespaceIndex, identifier); DataValue newValue = new DataValue(new Variant(nodeValue), null, null); //写入节点数据 StatusCode statusCode = client.writeValue(nodeId, newValue).join(); System.out.println("结果:" + statusCode.isGood()); } }