Jay
2024-11-01 03e8aca3ad6201c0d74e00d4c8d7367cdaaa54f9
提交 | 用户 | 时间
a6de49 1 package com.iailab.module.data.channel.opcua.collector;
H 2
3 import com.alibaba.fastjson.JSONObject;
4 import com.iailab.module.data.channel.opcua.dto.ChannelOPCUADeviceDTO;
5 import lombok.extern.slf4j.Slf4j;
6 import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
7 import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
8 import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
9 import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider;
10 import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
11 import org.eclipse.milo.opcua.sdk.client.nodes.UaNode;
12 import org.eclipse.milo.opcua.stack.client.DiscoveryClient;
13 import org.eclipse.milo.opcua.stack.core.Identifiers;
14 import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
15 import org.eclipse.milo.opcua.stack.core.types.builtin.*;
16 import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
17 import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
18 import org.springframework.stereotype.Component;
19
20 import java.net.URI;
21 import java.net.URISyntaxException;
22 import java.nio.file.Files;
23 import java.nio.file.Path;
24 import java.nio.file.Paths;
25 import java.util.List;
26 import java.util.Objects;
27
28 import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;
29
30 /**
31  * @author PanZhibao
32  * @Description
33  * @createTime 2023年05月12日 15:36:00
34  */
35 @Slf4j
36 @Component
37 public class OpcUaUtils {
38
39     private final String CONNECTION_TYPE_USER = "1";
40
41     private final String SECURITY_POLICY_BASIC256 = "Basic256";
42
43     private final String SECURITY_POLICY_BASIC128RSA15 = "Basic128Rsa15";
44
45     private final String SECURITY_POLICY_BASIC256SHA256 = "Basic256Sha256";
46
47     public synchronized OpcUaClient createClient(ChannelOPCUADeviceDTO configDto) throws Exception {
48         OpcUaClient opcUaClient = null;
49         try {
50             log.info("OpcuaDTO" + JSONObject.toJSONString(configDto));
51
52             // 加载秘钥
53             // Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
54             log.info("加载秘钥");
55             Path securityTempDir = Paths.get("D:\\DLUT\\pfx");
56             Files.createDirectories(securityTempDir);
57             if (!Files.exists(securityTempDir)) {
58                 throw new Exception("没有创建安全目录: " + securityTempDir);
59             }
60             KeyStoreLoader loader = new KeyStoreLoader().load(securityTempDir);
61
62             // 安全策略
63             log.info("安全策略");
64             SecurityPolicy securityPolicy = getSecurityPolicy(configDto.getSecurityPolicy());
65
66             // 验证方式
67             log.info("验证方式");
68             IdentityProvider identityProvider = getIdentityProvider(configDto);
69
70             // opc.tcp://DESKTOP-D7CDEIF:53530/OPCUA/SimulationServer
71             // String endpointUrl = "opc.tcp://127.0.0.1:53530";
72             log.info("连接端点");
73             List<EndpointDescription> endpoints = DiscoveryClient.getEndpoints(configDto.getEndpointUrl()).get();
74             endpoints.forEach(item -> {
75                 log.info("EndpointUrl=" + item.getEndpointUrl());
76             });
77             EndpointDescription endpoint = endpoints.stream()
78                     .filter(e -> e.getSecurityPolicyUri().equals(securityPolicy.getUri()))
79                     .findFirst()
80                     .orElseThrow(() -> new Exception("没有连接上端点"));
81
82             EndpointDescription newEndpoint = updateEndpointUrl(endpoint, getHostFromEndPointUrl(configDto.getEndpointUrl()));
83             log.info("NewEndpointUrl=" + newEndpoint.getEndpointUrl());
84
85             OpcUaClientConfig config = OpcUaClientConfig.builder()
86                     .setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
87                     .setApplicationUri("")
88                     .setCertificate(loader.getClientCertificate())
89                     .setKeyPair(loader.getClientKeyPair())
90                     .setEndpoint(newEndpoint)
91                     .setIdentityProvider(identityProvider)
92                     .setRequestTimeout(uint(5000))
93                     .build();
94
95             opcUaClient = OpcUaClient.create(config);
96             log.info("创建客户端完成");
97         } catch (Exception e) {
98             e.printStackTrace();
99             log.error("创建客户端失败" + e.getMessage());
100             throw new Exception(e.getMessage());
101         }
102         return opcUaClient;
103     }
104
105     private String getHostFromEndPointUrl (String endPointUrl) {
106         int indexStart = endPointUrl.lastIndexOf("//");
107         int indexEnd = endPointUrl.lastIndexOf(":");
108         String host = endPointUrl.substring(indexStart, indexEnd).replace("//","");
109         log.info(host);
110         return host;
111     }
112
113     private EndpointDescription updateEndpointUrl(EndpointDescription original, String hostname) throws URISyntaxException {
114
115         URI uri = new URI(original.getEndpointUrl()).parseServerAuthority();
116
117         String endpointUrl = String.format(
118                 "%s://%s:%s%s",
119                 uri.getScheme(),
120                 hostname,
121                 uri.getPort(),
122                 uri.getPath()
123         );
124
125         return new EndpointDescription(
126                 endpointUrl,
127                 original.getServer(),
128                 original.getServerCertificate(),
129                 original.getSecurityMode(),
130                 original.getSecurityPolicyUri(),
131                 original.getUserIdentityTokens(),
132                 original.getTransportProfileUri(),
133                 original.getSecurityLevel()
134         );
135     }
136
137     private IdentityProvider getIdentityProvider(ChannelOPCUADeviceDTO configDto) {
138         switch (configDto.getConnectionType()) {
139             case CONNECTION_TYPE_USER:
140                 return new UsernameProvider(configDto.getUserName(), configDto.getPassword());
141             default:
142                 return new AnonymousProvider();
143         }
144     }
145
146     private SecurityPolicy getSecurityPolicy(String type) {
147         switch (type) {
148             case SECURITY_POLICY_BASIC256:
149                 return SecurityPolicy.Basic256;
150             case SECURITY_POLICY_BASIC128RSA15:
151                 return SecurityPolicy.Basic128Rsa15;
152             case SECURITY_POLICY_BASIC256SHA256:
153                 return SecurityPolicy.Basic256Sha256;
154             default:
155                 return SecurityPolicy.None;
156         }
157     }
158
159     /**
160      * 遍历树形节点
161      *
162      * @param client OPC UA客户端
163      * @param uaNode 节点
164      * @throws Exception
165      */
166     private static void browseNode(OpcUaClient client, UaNode uaNode) throws Exception {
167         List<? extends UaNode> nodes;
168         if (uaNode == null) {
169             nodes = client.getAddressSpace().browseNodes(Identifiers.ObjectsFolder);
170         } else {
171             nodes = client.getAddressSpace().browseNodes(uaNode);
172         }
173         for (UaNode nd : nodes) {
174             //排除系统行性节点,这些系统性节点名称一般都是以"_"开头
175             if (Objects.requireNonNull(nd.getBrowseName().getName()).contains("_")) {
176                 continue;
177             }
178             System.out.println("Node= " + nd.getBrowseName().getName());
179             browseNode(client, nd);
180         }
181     }
182
183     /**
184      * 读取节点数据
185      * namespaceIndex可以通过UaExpert客户端去查询,一般来说这个值是2。
186      * identifier也可以通过UaExpert客户端去查询,这个值=通道名称.设备名称.标记名称
187      *
188      * @param client    OPC UA客户端
189      * @param nodeIdStr 标识符
190      * @throws Exception
191      */
192     public static String readNode(OpcUaClient client, String nodeIdStr) throws Exception {
193         String[] nodeIdArr = nodeIdStr.split(";");
194
195         int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]);
196         String identifier = nodeIdArr[1].split("=")[1];
197         //节点
198         NodeId nodeId = new NodeId(namespaceIndex, identifier);
199         //读取节点数据
200         DataValue value = client.readValue(0.0, TimestampsToReturn.Neither, nodeId).get();
201         identifier = String.valueOf(nodeId.getIdentifier());
202         System.out.println(identifier + ": " + String.valueOf(value.getValue().getValue()));
203         return value.getValue().getValue().toString();
204     }
205
206     /**
207      * 写入节点数据
208      *
209      * @param client
210      * @param nodeIdStr
211      * @param nodeValue
212      */
213     public static void writeIntValue(OpcUaClient client, String nodeIdStr, Integer nodeValue) {
214         String[] nodeIdArr = nodeIdStr.split(";");
215
216         int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]);
217         String identifier = nodeIdArr[1].split("=")[1];
218         NodeId nodeId = new NodeId(namespaceIndex, identifier);
219         DataValue newValue = new DataValue(new Variant(nodeValue), null, null);
220         //写入节点数据
221         StatusCode statusCode = client.writeValue(nodeId, newValue).join();
222         System.out.println("结果:" + statusCode.isGood());
223     }
224
225     /**
226      * 写入节点数据
227      *
228      * @param client
229      * @param nodeIdStr
230      * @param nodeValue
231      */
232     public static void writeFloatValue(OpcUaClient client, String nodeIdStr, Float nodeValue) {
233         String[] nodeIdArr = nodeIdStr.split(";");
234
235         int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]);
236         String identifier = nodeIdArr[1].split("=")[1];
237         NodeId nodeId = new NodeId(namespaceIndex, identifier);
238         DataValue newValue = new DataValue(new Variant(nodeValue), null, null);
239         //写入节点数据
240         StatusCode statusCode = client.writeValue(nodeId, newValue).join();
241         System.out.println("结果:" + statusCode.isGood());
242     }
243
244     /**
245      * 写入节点数据
246      *
247      * @param client
248      * @param nodeIdStr
249      * @param nodeValue
250      */
251     public static void writeBooleanValue(OpcUaClient client, String nodeIdStr, Boolean nodeValue) {
252         String[] nodeIdArr = nodeIdStr.split(";");
253
254         int namespaceIndex = Integer.parseInt(nodeIdArr[0].split("=")[1]);
255         String identifier = nodeIdArr[1].split("=")[1];
256         NodeId nodeId = new NodeId(namespaceIndex, identifier);
257         DataValue newValue = new DataValue(new Variant(nodeValue), null, null);
258         //写入节点数据
259         StatusCode statusCode = client.writeValue(nodeId, newValue).join();
260         System.out.println("结果:" + statusCode.isGood());
261     }
262 }