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());
|
}
|
}
|