提交 | 用户 | 时间
|
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 |
} |