houzhongjian
2024-07-23 a6de490948278991e47952e90671ddba4555e9a2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
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<EndpointDescription> 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<? extends UaNode> 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());
    }
}