1.关于本次使用的opc工具包
<!--opc ua client sdk-->
<dependency>
<groupId>org.eclipse.milo</groupId>
<artifactId>sdk-client</artifactId>
<version>0.6.3</version>
</dependency>
<!--opc ua server sdk-->
<dependency>
<groupId>org.eclipse.milo</groupId>
<artifactId>sdk-server</artifactId>
<version>0.6.3</version>
</dependency>
2.本次读取数据所需参数
下面的实体类为一个标签读取数据所需参数
public class Tag{
private Long id;
// 标签名称
private String name;
// opc服务器内的名字 通过此名字来读取对应的标签的数据
private String opcName;
}
3.本次读取数据所需配置参数
public class OpcServer {
/**
* opc地址
*/
private String opcUrl;
/**
* opc账号名
*/
private String opcUsername;
/**
* opc密码
*/
private String opcPassword;
/**
* opc cls id
*/
private String opcClsId;
/**
* opc类型(OPCDA,OPCUA)
*/
private String opcType;
}
4.加密证书解析
public class KeyStoreLoader {
private static final Pattern IP_ADDR_PATTERN = Pattern.compile(
"^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
/**
* 证数别名
*/
private static final String CLIENT_ALIAS = "client-ai";
/**
* 获取私钥得密码
*/
private static final char[] PASSWORD = "password".toCharArray();
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 证书对象
*/
private X509Certificate clientCertificate;
/**
* 密钥对对象
*/
private KeyPair clientKeyPair;
KeyStoreLoader load(Path baseDir) throws Exception {
//创建一个使用`PKCS12`加密标准的KeyStore。KeyStore在后面将作为读取和生成证书的对象。
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//PKCS12的加密标准的文件后缀是.pfx,其中包含了公钥和私钥。
//而其他如.der等的格式只包含公钥,私钥在另外的文件中。
Path serverKeyStore = baseDir.resolve("example-client.pfx");
logger.info("Loading KeyStore at {}", serverKeyStore);
//如果文件不存在则创建.pfx证书文件。
if (!Files.exists(serverKeyStore)) {
keyStore.load(null, PASSWORD);
//用2048位的RAS算法。`SelfSignedCertificateGenerator`为Milo库的对象。
KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
//`SelfSignedCertificateBuilder`也是Milo库的对象,用来生成证书。
//中间所设置的证书属性可以自行修改。
SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair)
.setCommonName("Eclipse Milo Example Client")
.setOrganization("digitalpetri")
.setOrganizationalUnit("dev")
.setLocalityName("Folsom")
.setStateName("CA")
.setCountryCode("US")
.setApplicationUri("urn:eclipse:milo:examples:client")
.addDnsName("localhost")
.addIpAddress("127.0.0.1");
// Get as many hostnames and IP addresses as we can listed in the certificate.
for (String hostname : HostnameUtil.getHostnames("0.0.0.0")) {
if (IP_ADDR_PATTERN.matcher(hostname).matches()) {
builder.addIpAddress(hostname);
} else {
builder.addDnsName(hostname);
}
}
//创建证书
X509Certificate certificate = builder.build();
//设置对应私钥的别名,密码,证书链
keyStore.setKeyEntry(CLIENT_ALIAS, keyPair.getPrivate(), PASSWORD, new X509Certificate[]{certificate});
try (OutputStream out = Files.newOutputStream(serverKeyStore)) {
//保存证书到输出流
keyStore.store(out, PASSWORD);
}
} else {
try (InputStream in = Files.newInputStream(serverKeyStore)) {
//如果文件存在则读取
keyStore.load(in, PASSWORD);
}
}
//用密码获取对应别名的私钥。
Key serverPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD);
if (serverPrivateKey instanceof PrivateKey) {
//获取对应别名的证书对象。
clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS);
//获取公钥
PublicKey serverPublicKey = clientCertificate.getPublicKey();
//创建Keypair对象。
clientKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey);
}
return this;
}
/**
* 返回证书
* @return
*/
X509Certificate getClientCertificate() {
return clientCertificate;
}
/**
* 返回密钥对
* @return
*/
KeyPair getClientKeyPair() {
return clientKeyPair;
}
}
5. opc客户端创建及连接断开
public OpcUaClient createClient(){
OpcUaClient opcUaClient = null;
// 连接地址端口号
try {
// 安全策略选择 此处使用的是无安全策略 可根据具体项目情况来对安全策略进行修改过滤
List<EndpointDescription> endpointDescriptions = DiscoveryClient.getEndpoints(url).get();
EndpointDescription endpoint = endpointDescriptions.stream().filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
.findFirst().orElseThrow(() -> new Exception("no desired endpoints returned"));
OpcUaClientConfig config = OpcUaClientConfig.builder()
// opc ua定义的名
.setApplicationName(endpoint.getServer().getApplicationName())
// 地址
.setApplicationUri(endpoint.getServer().getApplicationUri())
// 安全策略配置
.setEndpoint(endpoint)
.setProductUri(endpoint.getServer().getProductUri())
.setIdentityProvider(getIdentityProvider())
// 等待时间
.setRequestTimeout(UInteger.valueOf(50000))
.build();
opcUaClient = OpcUaClient.create(config);
logger.info("初始化OPC UA Client......成功");
}catch (Exception e){
logger.error("初始化OPC UA Client失败, {}", e.getMessage());
return null;
}
if (!opcUaClient.getSession().isDone()) {
try {
// synchronous connect
opcUaClient.connect().get();
logger.info("OPC UA Client连接connect成功");
} catch (Exception e) {
logger.error("OPC UA Client连接connect失败, {}", e.getMessage());
opcUaClient.disconnect();
return null;
}
}
return opcUaClient;
}
public void disconnect() {
runningState = false;
try {
client.disconnect().get();
Stack.releaseSharedResources();
} catch (InterruptedException | ExecutionException e) {
logger.error("设备断开连接的时候发生了错误: {}", e.getMessage(), e);
}
}
/**
* 获取登录验证,没有输入用户名密码就匿名登录
* @return
*/
private IdentityProvider getIdentityProvider(String userName,String passWord) {
if (userName != null && passWord != null) {
return new UsernameProvider(userName, passWord);
}
return new AnonymousProvider();
}
6. 使用opc对实时数据进行读取
/**
* OPC对象
*/
public class Opc extends SuperEntity {
/**
* opc地址
*/
@TableField(value = "opc_url")
@ApiModelProperty(value = "opc地址")
private String opcUrl;
/**
* opc账号名
*/
@TableField(value = "opc_username")
@ApiModelProperty(value = "opc用户名")
private String opcUsername;
/**
* opc密码
*/
@TableField(value = "opc_password")
@ApiModelProperty(value = "opc密码")
private String opcPassword;
/**
* opc cls id
*/
@TableField(value = "opc_cls_id")
@ApiModelProperty(value = "opcClsId")
private String opcClsId;
/**
* opc类型(OPCDA,OPCUA)
*/
@TableField(value = "opc_type")
@ApiModelProperty(value = "opc类型")
private String opcType;
/**
* 标签id
*/
@TableField(value = "tag_id")
@ApiModelProperty(value = "标签id")
private Long tagId;
/**
* 标签名
*/
@TableField(value = "tag_name")
@ApiModelProperty(value = "标签名")
private String tagName;
/**
* opc地址
*/
@TableField(value = "opc_name")
@ApiModelProperty(value = "opc地址")
private String opcName;
}
@Slf4j
public class OpcUtils {
/**
* 读取数据
* @param tags
* @param client
* @return
*/
public static List<DataValue> getValue(List<Opc> tags, OpcUaClient client,int nameSpaceIndex){
List<DataValue> values = new ArrayList<>();
try {
List<NodeId> nodeIds = new ArrayList<>();
for (Opc tag : tags) {
NodeId nodeId = new NodeId(nameSpaceIndex, tag.getOpcName());
nodeIds.add(nodeId);
}
CompletableFuture<List<DataValue>> future = client.readValues(0.0, TimestampsToReturn.Both, nodeIds);
values = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return values;
}
public 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("_")) {
System.out.println(nd.getBrowseName().getName());
continue;
}
System.out.println(nd.getBrowseName().getName());
browseNode(client, nd);
}
}
/**
* 订阅
* @param opcUaClient
*/
public static void subscribe(OpcUaClient opcUaClient) throws ExecutionException, InterruptedException {
OpcUaSubscriptionManager subscriptionManager = opcUaClient.getSubscriptionManager();
opcUaClient.getSubscriptionManager()
.createSubscription(1000.0)
.thenAccept( t -> {
//节点
NodeId nodeId = new NodeId(2, "OPCUA.DeviceGroup.CX_001");
ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
// 创建监控参数
MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(1), 1000.0, null, UInteger.valueOf(10), true);
// 创建监控项请求
// 该请求最后用于创建订阅
MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);
List<MonitoredItemCreateRequest> requests = new ArrayList<>();
requests.add(request);
//创建监控项,并且注册变量值改变时候的回调函数
t.createMonitoredItems(TimestampsToReturn.Both,requests
,(item,id) ->item.setValueConsumer((it,val) -> {
System.out.println(new Date());
System.out.println("=====订阅nodeid====== :" + it.getReadValueId().getNodeId());
System.out.println("=====订阅value====== :" + val.getValue().getValue());
}));
})
.get();
// 持续订阅
Thread.sleep(2000);
}
}