From f4644873804165e9db88375f242421f00b0903f2 Mon Sep 17 00:00:00 2001
From: zhoushihao <zsh19950802@163.com>
Date: 星期五, 11 十月 2024 08:36:01 +0800
Subject: [PATCH] 1、opc订阅断电重连功能已实现 2、移除lang包下定义字典重复字段
---
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/service/MiloService.java | 553 ++++++++++++++++++
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectFactory.java | 155 +++++
hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring.factories | 3
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/CustomUtil.java | 114 +++
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/ReadValuesRunner.java | 60 ++
hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/opccallback/CacheGlassSubscriptionCallback.java | 35 +
hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/OpcCacheGlassTask.java | 31 +
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseRunner.java | 36 +
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/WriteValuesRunner.java | 50 +
hangzhoumesParent/moduleService/CacheGlassModule/src/main/resources/application.yml | 8
hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports | 1
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/EndPointNotFoundException.java | 18
hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/entity/CacheGlassInfo.java | 46 +
hangzhoumesParent/common/pom.xml | 1
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectPool.java | 25
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/ReadWriteEntity.java | 26
UI-Project/src/lang/en.js | 2
UI-Project/src/lang/zh.js | 10
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionRunner.java | 111 +++
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/KeyStoreLoader.java | 123 ++++
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/IdentityNotFoundException.java | 14
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloProperties.java | 85 ++
hangzhoumesParent/common/opcuaClient/pom.xml | 40 +
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/WriteEntity.java | 23
hangzhoumesParent/moduleService/yiwumes/src/main/resources/application.yml | 10
hangzhoumesParent/common/opcuaClient/src/main/resources/application.yml | 9
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloAutoConfiguration.java | 117 +++
hangzhoumesParent/moduleService/pom.xml | 5
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionCallback.java | 16
hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseNodeRunner.java | 59 +
30 files changed, 1,775 insertions(+), 11 deletions(-)
diff --git a/UI-Project/src/lang/en.js b/UI-Project/src/lang/en.js
index d80a9be..869758b 100644
--- a/UI-Project/src/lang/en.js
+++ b/UI-Project/src/lang/en.js
@@ -158,7 +158,6 @@
inglassgaps:'袙胁械写懈褌械 褋褌械泻谢芯 蟹邪蟹芯褉',
sure:'锌芯写褌胁械褉写',
cancel:'袨褌屑械薪邪',
- glassID:'小褌械泻谢褟薪薪褘泄 ID',
operate:'袨锌械褉邪褑懈褟',
breakage:'袩芯胁褉械卸写械薪懈械',
delete:'校写邪谢',
@@ -224,7 +223,6 @@
bindingshelves:'袧邪蟹薪邪褔械薪薪邪褟 褋褌械谢谢邪卸泻邪',
clear:'效懈褋褌芯.',
workstation:'小褌邪薪褑懈褟 薪芯屑械褉',
- shelfnumber:'袧芯屑械褉 褋褌芯泄泻懈 褉械谐懈褋褌褉邪褑懈懈锛�',
cardnumbera:'袧芯屑械褉 泻邪褉褌芯褔泻懈 锌褉芯褑械褋褋邪锛�',
incardnumber:'袩芯卸邪谢褍泄褋褌邪, 胁褘斜械褉懈褌械 薪芯屑械褉 锌褉芯谐褉邪屑屑褘',
clearglass:'袨褔懈褋褌懈褌褜 褋褌械泻谢芯 褋 锌芯谢泻懈',
diff --git a/UI-Project/src/lang/zh.js b/UI-Project/src/lang/zh.js
index 4f2483f..d1de9eb 100644
--- a/UI-Project/src/lang/zh.js
+++ b/UI-Project/src/lang/zh.js
@@ -216,12 +216,11 @@
specifytemperinga:'鏄惁鎸囧畾閽㈠寲璇ユ潯淇℃伅锛�',
temperedswitch:'閽㈠寲寮�鍏�',
dutyinformation:'鍊肩彮淇℃伅',
- line:'绾胯矾',
process:'宸ュ簭',
team:'鐝粍',
basic:'璁惧',
makesure:'纭淇濆瓨',
- cancel:'鍙栨秷',
+ // cancel:'鍙栨秷',
temperingtotal:'閽㈠寲鐐夋暟锛�',
glasstotal:'鐜荤拑鎬绘暟锛�',
},
@@ -452,11 +451,9 @@
allstatus: '鍏ㄩ儴鍏ュ簱鐘舵��',
completedquantity: '瀹屾垚鏁伴噺',
scrapquantity: '鎶ュ簾鏁伴噺',
- number: '鏁伴噺',
method: '鍔犲伐鏂瑰紡',
innumber: '宸插叆鏁伴噺',
productstatus: '鐢熶骇鐘舵��',
- projectnumber: '宸ョ▼鍙�',
right: '姝e父',
stop: '缁堟',
inquire: '鏌ヨ',
@@ -490,7 +487,6 @@
glassID :'鐜荤拑ID',
projectnumber :'宸ョ▼鍙�',
layoutID :'閽㈠寲鐗堝浘ID',
- productiontime :'鐢熶骇鏃堕棿',
type :'绫诲瀷',
state :'鐘舵��',
processcards :'娴佺▼鍗�',
@@ -525,7 +521,6 @@
startslot:'寮�濮嬪伐浣�',
endslot:'鐩爣宸ヤ綅',
slotid:'鏍煎瓙ID',
- slot:'鏍煎瓙鍙�',
width:'鍘熺墖瀹�',
widtha:'鍘熺墖瀹斤細',
inwidth:'璇疯緭鍏ュ師鐗囧',
@@ -545,7 +540,6 @@
quantitya:'鏁伴噺锛�',
enableid:'浠诲姟ID',
originateslot:'璧峰鏍煎瓙',
- endslot:'鐩爣鏍煎瓙',
patternquantity:'鍘熺墖鏁伴噺',
enabletype:'浠诲姟绫诲瀷',
enablestate:'宸ヤ綅鐘舵��',
@@ -563,10 +557,8 @@
taskstatus :'浠诲姟鐘舵��',
built :'鏂板缓',
execution :'鎵ц涓�',
- finish :'瀹屾垚',
tasktype :'浠诲姟绫诲瀷',
stocke :'鍏ュ簱',
- outbound :'鍑哄簱',
dispatch :'璋冨害',
inquire :'鏌ヨ',
station :'宸ヤ綅',
diff --git a/hangzhoumesParent/common/opcuaClient/pom.xml b/hangzhoumesParent/common/opcuaClient/pom.xml
new file mode 100644
index 0000000..cf5d20f
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>common</artifactId>
+ <groupId>com.mes</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>opcuaClient</artifactId>
+
+ <properties>
+ <maven.compiler.source>8</maven.compiler.source>
+ <maven.compiler.target>8</maven.compiler.target>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-autoconfigure</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-configuration-processor</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.milo</groupId>
+ <artifactId>sdk-client</artifactId>
+ <version>0.6.8</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-pool2</artifactId>
+ <version>2.6.2</version>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloAutoConfiguration.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloAutoConfiguration.java
new file mode 100644
index 0000000..5e56b39
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloAutoConfiguration.java
@@ -0,0 +1,117 @@
+package com.mes.milo.configuration;
+
+import com.mes.milo.pool.MiloConnectFactory;
+import com.mes.milo.pool.MiloConnectPool;
+import com.mes.milo.service.MiloService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.DependsOn;
+
+import javax.annotation.PreDestroy;
+
+/**
+ * @author mes
+ * @date 2020/4/25
+ * @desc milo-spring-boot-starter
+ * @since 0.0.1
+ */
+@Configuration
+@EnableConfigurationProperties(MiloProperties.class)
+@ConditionalOnClass({MiloService.class, MiloConnectPool.class})
+@ConditionalOnProperty(prefix = MiloProperties.PREFIX, value = "enabled", havingValue = "true", matchIfMissing = true)
+@Slf4j
+public class MiloAutoConfiguration {
+ private final MiloProperties properties;
+
+ private MiloConnectPool connectPool;
+
+ public MiloAutoConfiguration(MiloProperties properties) {
+ this.properties = properties;
+ }
+
+ @Bean(name = "miloConnectPool")
+ @ConditionalOnMissingBean({MiloConnectPool.class})
+ protected MiloConnectPool miloConnectPool() {
+ MiloConnectFactory objectFactory = new MiloConnectFactory(this.properties);
+ //璁剧疆瀵硅薄姹犵殑鐩稿叧鍙傛暟
+ GenericKeyedObjectPoolConfig<OpcUaClient> poolConfig = new GenericKeyedObjectPoolConfig<>();
+
+ MiloProperties.Pool pool = properties.getPool();
+ // 鏈�澶х┖闂叉暟
+ poolConfig.setMaxIdlePerKey(pool.getMaxIdle());
+ //鏈�灏忕┖闂�,璁剧疆涓�2琛ㄧず姹犲唴鑷冲皯瀛樻斁2涓┖闂插璞�(褰撴睜鍐呮湁2涓┖闂插璞℃椂璋冪敤borrowObject鍘诲璞℃椂浼氱珛鍗宠皟鐢ㄥ垱寤哄璞$殑鏂规硶淇濊瘉姹犲唴鏈�2涓┖闂插璞�)
+ poolConfig.setMinIdlePerKey(pool.getMinIdle());
+ //鏈�澶ф�绘暟 10
+ poolConfig.setMaxTotal(pool.getMaxTotal());
+ // 澶氫箙鎵ц涓�娆″璞℃壂鎻忥紝灏嗘棤鐢ㄧ殑瀵硅薄閿�姣侊紝榛樿-1涓嶆壂鎻�
+ // poolConfig.setTimeBetweenEvictionRuns(Duration.ofMinutes(1));
+ // 鍦ㄨ幏鍙栧璞$殑鏃跺�欐鏌ユ湁鏁堟��, 榛樿false
+ poolConfig.setTestOnBorrow(true);
+ // 鍦ㄥ綊杩樺璞$殑鏃跺�欐鏌ユ湁鏁堟��, 榛樿false
+ poolConfig.setTestOnReturn(false);
+ // 鍦ㄧ┖闂叉椂妫�鏌ユ湁鏁堟��, 榛樿false
+ poolConfig.setTestWhileIdle(false);
+ // 鏈�澶х瓑寰呮椂闂达紝 榛樿鐨勫�间负-1锛岃〃绀烘棤闄愮瓑寰呫��
+// poolConfig.setMaxWait(Duration.ofSeconds(1));
+ // 鏄惁鍚敤鍚庤繘鍏堝嚭, 榛樿true
+ poolConfig.setLifo(true);
+ // 杩炴帴鑰楀敖鏃舵槸鍚﹂樆濉�, false绔嬪嵆鎶涘紓甯�,true闃诲鐩村埌瓒呮椂, 榛樿true
+ poolConfig.setBlockWhenExhausted(true);
+ // 姣忔閫愬嚭妫�鏌ユ椂 閫愬嚭鐨勬渶澶ф暟鐩� 榛樿3
+ poolConfig.setNumTestsPerEvictionRun(3);
+
+ //涓�瀹氳鍏抽棴jmx锛屼笉鐒秙pringboot鍚姩浼氭姤宸茬粡娉ㄥ唽浜嗘煇涓猨mx鐨勯敊璇�
+ poolConfig.setJmxEnabled(false);
+
+ //鏂板缓涓�涓璞℃睜,浼犲叆瀵硅薄宸ュ巶鍜岄厤缃�
+ connectPool = new MiloConnectPool(objectFactory, poolConfig);
+
+ initPool(pool.getInitialSize(), pool.getMaxIdle());
+ return connectPool;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(MiloService.class)
+ @DependsOn("miloConnectPool")
+ public MiloService miloService(MiloConnectPool miloConnectPool) {
+ return new MiloService(miloConnectPool, properties);
+ }
+
+ /**
+ * 棰勫厛鍔犺浇testObject瀵硅薄鍒板璞℃睜涓�
+ *
+ * @param initialSize 鍒濆鍖栬繛鎺ユ暟
+ * @param maxIdle 鏈�澶х┖闂茶繛鎺ユ暟
+ */
+ private void initPool(int initialSize, int maxIdle) {
+ if (initialSize <= 0) {
+ return;
+ }
+
+ properties.getConfig().forEach((key, config) -> {
+ for (int i = 0; i < Math.min(initialSize, maxIdle); i++) {
+ try {
+ connectPool.addObject(config);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+
+ }
+
+ @PreDestroy
+ public void destroy() {
+ if (connectPool != null) {
+ connectPool.close();
+ log.info("all opcUaClients are closed");
+ }
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloProperties.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloProperties.java
new file mode 100644
index 0000000..1dac285
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/configuration/MiloProperties.java
@@ -0,0 +1,85 @@
+package com.mes.milo.configuration;
+
+import lombok.Data;
+import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * @author mes
+ * @date 2020/4/25
+ * @desc milo-spring-boot-starter
+ * @since 0.0.1
+ */
+@Data
+@ConfigurationProperties(prefix = MiloProperties.PREFIX)
+public class MiloProperties {
+ public static final String PREFIX = "mes.milo";
+
+ /**
+ * 鏄惁鍚敤缁勪欢
+ */
+ private Boolean enabled = true;
+
+ /**
+ * server 榛樿璇锋眰閰嶇疆锛屼笉鎸囧畾锛屽垯榛樿鍙� config涓涓�涓�
+ */
+ private String primary;
+
+ /**
+ * server 鍒楄〃
+ */
+ private Map<String, Config> config = new LinkedHashMap<>();
+
+ /**
+ * 杩炴帴姹犻厤缃�
+ */
+ private Pool pool = new Pool();
+
+ @Data
+ public static class Config {
+
+ /**
+ * OPC UA鍦板潃
+ */
+ private String endpoint;
+
+ /**
+ * 瀹夊叏绛栫暐
+ */
+ private SecurityPolicy securityPolicy = SecurityPolicy.None;
+
+ /**
+ * 鐢ㄦ埛鍚�
+ */
+ private String username;
+
+ /**
+ * 瀵嗙爜
+ */
+ private String password;
+ }
+
+ @Data
+ public static class Pool {
+ /**
+ * 鏈�澶х┖闂�
+ */
+ private int maxIdle = 5;
+ /**
+ * 鏈�澶ф�绘暟
+ */
+ private int maxTotal = 20;
+ /**
+ * 鏈�灏忕┖闂�
+ */
+ private int minIdle = 2;
+
+ /**
+ * 鍒濆鍖栬繛鎺ユ暟
+ */
+ private int initialSize = 3;
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/EndPointNotFoundException.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/EndPointNotFoundException.java
new file mode 100644
index 0000000..0388c2f
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/EndPointNotFoundException.java
@@ -0,0 +1,18 @@
+package com.mes.milo.exception;
+
+/**
+ * 绫� EndPointNotFoundException 鍔熻兘鎻忚堪锛�
+ *
+ * @author mes
+ * @version 0.0.1
+ * @date 2021/09/04 17:03
+ */
+public class EndPointNotFoundException extends RuntimeException {
+ public EndPointNotFoundException() {
+ super();
+ }
+
+ public EndPointNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/IdentityNotFoundException.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/IdentityNotFoundException.java
new file mode 100644
index 0000000..afd69f2
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/exception/IdentityNotFoundException.java
@@ -0,0 +1,14 @@
+package com.mes.milo.exception;
+
+/**
+ * 绫� IdentityNotFoundException 鍔熻兘鎻忚堪锛�
+ *
+ * @author mes
+ * @version 0.0.1
+ * @date 2021/09/15 09:35
+ */
+public class IdentityNotFoundException extends RuntimeException {
+ public IdentityNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/ReadWriteEntity.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/ReadWriteEntity.java
new file mode 100644
index 0000000..5276365
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/ReadWriteEntity.java
@@ -0,0 +1,26 @@
+package com.mes.milo.model;
+
+import lombok.*;
+import lombok.experimental.Accessors;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+
+/**
+ * @author mes
+ * @version 0.0.1
+ * @desc
+ * @since 2020/4/13
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+@ToString
+public class ReadWriteEntity {
+
+ private String identifier;
+
+ private Object value;
+
+ private DataValue dataValue;
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/WriteEntity.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/WriteEntity.java
new file mode 100644
index 0000000..748781c
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/model/WriteEntity.java
@@ -0,0 +1,23 @@
+package com.mes.milo.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
+
+/**
+ * @author mes
+ * @version 0.0.1
+ * @since 2020/4/13
+ */
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = true)
+public class WriteEntity {
+ private String identifier;
+ private Variant variant;
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectFactory.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectFactory.java
new file mode 100644
index 0000000..f99202a
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectFactory.java
@@ -0,0 +1,155 @@
+package com.mes.milo.pool;
+
+import com.mes.milo.configuration.MiloProperties;
+import com.mes.milo.exception.EndPointNotFoundException;
+import com.mes.milo.exception.IdentityNotFoundException;
+import com.mes.milo.utils.CustomUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.pool2.KeyedPooledObjectFactory;
+import org.apache.commons.pool2.PooledObject;
+import org.apache.commons.pool2.impl.DefaultPooledObject;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+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.stack.core.security.SecurityPolicy;
+import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
+import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
+
+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.Optional;
+
+/**
+ * 绫� MiloConnectFactory 鍔熻兘鎻忚堪锛�<br/>
+ *
+ * @author mes
+ * @version 0.0.1
+ * @date 2023/5/4 18:56
+ */
+@Slf4j
+public class MiloConnectFactory implements KeyedPooledObjectFactory<MiloProperties.Config, OpcUaClient> {
+
+ public MiloConnectFactory(MiloProperties properties) {
+ CustomUtil.verifyProperties(properties);
+ }
+
+ /**
+ * 鍒涘缓瀵硅薄
+ *
+ * @return
+ * @throws Exception
+ */
+ @Override
+ public PooledObject<OpcUaClient> makeObject(MiloProperties.Config key) throws Exception {
+ OpcUaClient client = null;
+ try {
+ client = createClient(key);
+ client.connect().get();
+ return new DefaultPooledObject<>(client);
+ } catch (Exception e) {
+ if (client != null) {
+ client.disconnect().get();
+ }
+ throw new InterruptedException(e.getMessage());
+ }
+ }
+
+ /**
+ * 瀵硅薄瑕佽閿�姣佹椂(validateObject鏂规硶杩斿洖false鎴栬�呰秴鏃�)鍚庤璋冪敤
+ *
+ * @param pooledObject
+ * @throws Exception
+ */
+ @Override
+ public void destroyObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) throws Exception {
+ OpcUaClient opcUaClient = pooledObject.getObject();
+ log.info("disconnect opcUaClient {}", opcUaClient.getConfig().getApplicationName().getText());
+ opcUaClient.disconnect().get();
+ }
+
+ /**
+ * 姣忔鑾峰彇瀵硅薄鍜岃繕鍥炲璞℃椂浼氳璋冪敤锛屽鏋滆繑鍥瀎alse浼氶攢姣佸璞�
+ */
+ @Override
+ public boolean validateObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) {
+ return true;
+ }
+
+ /**
+ * 璋冪敤鑾峰彇瀵硅薄鏂规硶鍓嶈璋冪敤
+ * 姝ゆ柟娉曚竴鑸繘琛屼竴浜涘墠缃搷浣�
+ */
+ @Override
+ public void activateObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) throws Exception {
+
+ }
+
+ /**
+ * 褰撹繕鍥炲璞″苟涓攙alidateObject鏂规硶杩斿洖true鍚庤璋冪敤
+ * 涓�鑸湪姝ゆ柟娉曚腑瀵瑰垰鍒氫娇鐢ㄥ畬鎴愮殑瀵硅薄杩涜閲嶇疆
+ */
+ @Override
+ public void passivateObject(MiloProperties.Config key, PooledObject<OpcUaClient> pooledObject) throws Exception {
+
+ }
+
+ private OpcUaClient createClient(MiloProperties.Config key) throws Exception {
+ Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
+ Files.createDirectories(securityTempDir);
+ if (!Files.exists(securityTempDir)) {
+ throw new Exception("unable to create security dir: " + securityTempDir);
+ }
+ return OpcUaClient.create(key.getEndpoint(),
+ endpoints -> {
+ final Optional<EndpointDescription> endpoint = endpoints
+ .stream()
+// .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
+ .findFirst();
+ EndpointDescription newEndpoint = new EndpointDescription(key.getEndpoint(), endpoint.get().getServer(), endpoint.get().getServerCertificate(),
+ endpoint.get().getSecurityMode(), endpoint.get().getSecurityPolicyUri(), endpoint.get().getUserIdentityTokens(),
+ endpoint.get().getTransportProfileUri(), endpoint.get().getSecurityLevel());
+ return Optional.of(newEndpoint);
+ },
+ configBuilder ->
+ configBuilder
+ .setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
+ .setApplicationUri("urn:eclipse:milo:examples:client")
+ //璁块棶鏂瑰紡
+ .setIdentityProvider(new UsernameProvider(key.getUsername(), key.getPassword()))
+ .setRequestTimeout(UInteger.valueOf(5000))
+ .build()
+ );
+ }
+
+ private URI getUri(MiloProperties.Config key) {
+ try {
+ return new URI(endpointUrl(key));
+ } catch (URISyntaxException e) {
+ throw new EndPointNotFoundException("endpoint 閰嶇疆寮傚父");
+ }
+ }
+
+ private String endpointUrl(MiloProperties.Config key) {
+ return key.getEndpoint();
+ }
+
+ private SecurityPolicy securityPolicy(MiloProperties.Config key) {
+ return key.getSecurityPolicy();
+ }
+
+ private IdentityProvider identityProvider(MiloProperties.Config key) {
+ if (securityPolicy(key).equals(SecurityPolicy.None)) {
+ return new AnonymousProvider();
+ }
+ if (key.getUsername() == null || key.getPassword() == null) {
+ throw new IdentityNotFoundException("杩炴帴淇℃伅鏈畬鍠�");
+ } else {
+ return new UsernameProvider(key.getUsername(), key.getPassword());
+ }
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectPool.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectPool.java
new file mode 100644
index 0000000..aa13b70
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/pool/MiloConnectPool.java
@@ -0,0 +1,25 @@
+package com.mes.milo.pool;
+
+import com.mes.milo.configuration.MiloProperties;
+import org.apache.commons.pool2.KeyedPooledObjectFactory;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
+import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+
+/**
+ * 绫� MiloConnectPool 鍔熻兘鎻忚堪锛�<br/>
+ *
+ * @author mes
+ * @version 0.0.1
+ * @date 2023/5/4 19:17
+ */
+public class MiloConnectPool extends GenericKeyedObjectPool<MiloProperties.Config, OpcUaClient> {
+
+ public MiloConnectPool(KeyedPooledObjectFactory<MiloProperties.Config, OpcUaClient> factory) {
+ super(factory);
+ }
+
+ public MiloConnectPool(KeyedPooledObjectFactory<MiloProperties.Config, OpcUaClient> factory, GenericKeyedObjectPoolConfig<OpcUaClient> config) {
+ super(factory, config);
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseNodeRunner.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseNodeRunner.java
new file mode 100644
index 0000000..6b3ba57
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseNodeRunner.java
@@ -0,0 +1,59 @@
+package com.mes.milo.runner;
+
+import com.mes.milo.utils.CustomUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.eclipse.milo.opcua.sdk.client.nodes.UaNode;
+import org.eclipse.milo.opcua.stack.core.UaException;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author mes
+ * @version 0.0.1
+ * @since 2020/4/14
+ */
+@Slf4j
+public class BrowseNodeRunner {
+ /**
+ * 瑕佽鐨勮妭鐐�
+ */
+ private final String browseRoot;
+
+ public BrowseNodeRunner(String browseRoot) {
+ this.browseRoot = browseRoot;
+ }
+
+ public List<String> run(OpcUaClient opcUaClient) {
+ NodeId nodeId = CustomUtil.parseNodeId(browseRoot);
+ return browseNode(browseRoot, opcUaClient, nodeId);
+ }
+
+ private List<String> browseNode(String prefix, OpcUaClient client, NodeId browseRoot) {
+ List<String> nodesList = new ArrayList<>();
+ try {
+ List<? extends UaNode> nodes = client.getAddressSpace().browseNodes(browseRoot);
+
+ nodes = nodes.stream().filter(item -> !Objects.requireNonNull(item.getBrowseName().getName()).startsWith("_")).collect(Collectors.toList());
+
+ for (UaNode node : nodes) {
+ String sub = prefix + "." + node.getBrowseName().getName();
+
+ // recursively browse to children
+ List<String> browseNode = browseNode(sub, client, node.getNodeId());
+ if (browseNode.isEmpty()) {
+ nodesList.add(sub);
+ } else {
+ nodesList.addAll(browseNode(sub, client, node.getNodeId()));
+ }
+ }
+ } catch (UaException e) {
+ log.error("Browsing nodeId={} failed: {}", browseRoot, e.getMessage(), e);
+ }
+ return nodesList;
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseRunner.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseRunner.java
new file mode 100644
index 0000000..2f93c84
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/BrowseRunner.java
@@ -0,0 +1,36 @@
+package com.mes.milo.runner;
+
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.eclipse.milo.opcua.sdk.client.nodes.UaNode;
+import org.eclipse.milo.opcua.stack.core.Identifiers;
+import org.eclipse.milo.opcua.stack.core.UaException;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author mes
+ * @version 0.0.1
+ * @since 2020/4/14
+ */
+@Slf4j
+public class BrowseRunner {
+
+ public List<String> run(OpcUaClient opcUaClient) {
+ List<String> nodesList = new ArrayList<>();
+ try {
+ List<? extends UaNode> nodes = opcUaClient.getAddressSpace().browseNodes(Identifiers.ObjectsFolder);
+
+ nodesList.addAll(nodes.stream().filter(item -> !Objects.requireNonNull(item.getBrowseName().getName()).startsWith("_")
+ && Objects.equals(item.getBrowseName().getNamespaceIndex(), UShort.valueOf(2)))
+ .map(item -> item.getBrowseName().getName()).collect(Collectors.toList()));
+ } catch (UaException e) {
+ log.error("閬嶅巻鏍硅妭鐐瑰紓甯革細{}", e.getMessage(), e);
+ }
+ return nodesList;
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/ReadValuesRunner.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/ReadValuesRunner.java
new file mode 100644
index 0000000..74772d5
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/ReadValuesRunner.java
@@ -0,0 +1,60 @@
+package com.mes.milo.runner;
+
+import com.mes.milo.model.ReadWriteEntity;
+import com.mes.milo.utils.CustomUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
+import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author mes
+ * @version 0.0.1
+ * @desc
+ * @since 2020/4/14
+ */
+@Slf4j
+public class ReadValuesRunner {
+ /**
+ * 瑕佽鐨勭偣浣峫ist
+ */
+ private final List<String> identifiers;
+
+ public ReadValuesRunner(List<String> identifiers) {
+ this.identifiers = identifiers;
+ }
+
+ public List<ReadWriteEntity> run(OpcUaClient opcUaClient) {
+ List<ReadWriteEntity> entityList = new ArrayList<>();
+ try {
+ List<NodeId> nodeIds = new ArrayList<>();
+ identifiers.forEach(identifier -> nodeIds.add(CustomUtil.parseNodeId(identifier)));
+ // 璇诲彇鎸囧畾鐐逛綅鐨勫�硷紝10s瓒呮椂
+ List<DataValue> dataValues = opcUaClient.readValues(10000, TimestampsToReturn.Both, nodeIds).get();
+ if (dataValues.size() == identifiers.size()) {
+ for (int i = 0; i < identifiers.size(); i++) {
+ String id = identifiers.get(i);
+ Object value = dataValues.get(i).getValue().getValue();
+ StatusCode status = dataValues.get(i).getStatusCode();
+ assert status != null;
+ if (status.isGood()) {
+ log.info("璇诲彇鐐逛綅 '{}' 鐨勫�间负 {}", id, value);
+ }
+ entityList.add(ReadWriteEntity.builder()
+ .identifier(id)
+ .value(value)
+ .dataValue(dataValues.get(i))
+ .build());
+ }
+ }
+ } catch (Exception e) {
+ log.error("璇诲�兼椂鍑虹幇浜嗗紓甯革細{}", e.getMessage(), e);
+ }
+ return entityList;
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/WriteValuesRunner.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/WriteValuesRunner.java
new file mode 100644
index 0000000..9ff7e71
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/WriteValuesRunner.java
@@ -0,0 +1,50 @@
+package com.mes.milo.runner;
+
+import com.mes.milo.model.WriteEntity;
+import com.mes.milo.utils.CustomUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author mes
+ * @version 0.0.1
+ * @since 2020/4/14
+ */
+@Slf4j
+public class WriteValuesRunner {
+ private final List<WriteEntity> entities;
+
+ public WriteValuesRunner(List<WriteEntity> entities) {
+ this.entities = entities;
+ }
+
+ public void run(OpcUaClient opcUaClient) {
+ try {
+ if (!entities.isEmpty()) {
+ List<NodeId> nodeIds = new LinkedList<>();
+ List<DataValue> dataValues = new LinkedList<>();
+ for (WriteEntity entity : entities) {
+ nodeIds.add(CustomUtil.parseNodeId(entity.getIdentifier()));
+ dataValues.add(new DataValue(entity.getVariant(), null, null));
+ }
+
+ List<StatusCode> statusCodeList = opcUaClient.writeValues(nodeIds, dataValues).join();
+ for (int i = 0; i < statusCodeList.size(); i++) {
+ if (statusCodeList.get(i).isGood()) {
+ log.info("灏嗗�� '{}' 鍐欏叆鍒扮偣浣嶏細{} 鎴愬姛", dataValues.get(i).getValue(), nodeIds.get(i));
+ } else {
+ log.error("鐐逛綅锛歿} 鍐欏叆鏃跺嚭鐜颁簡寮傚父锛歿}", nodeIds.get(i), statusCodeList.get(i));
+ }
+ }
+ }
+ } catch (Exception e) {
+ log.error("鎵归噺鍐欏�煎嚭鐜板紓甯稿嚭鐜颁簡寮傚父锛歿}", e.getMessage(), e);
+ }
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionCallback.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionCallback.java
new file mode 100644
index 0000000..a31cee0
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionCallback.java
@@ -0,0 +1,16 @@
+package com.mes.milo.runner.subscription;
+
+import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+
+/**
+ * 绫� SubscriptionCallback 鍔熻兘鎻忚堪锛�<br/>
+ *
+ * @author mes
+ * @version 0.0.1
+ * @date 2023/5/8 22:14
+ */
+public interface SubscriptionCallback {
+
+ void onSubscribe(ManagedDataItem dataItem, DataValue value) throws Exception;
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionRunner.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionRunner.java
new file mode 100644
index 0000000..3cc0db3
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/runner/subscription/SubscriptionRunner.java
@@ -0,0 +1,111 @@
+package com.mes.milo.runner.subscription;
+
+import com.mes.milo.utils.CustomUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
+import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscriptionManager;
+import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem;
+import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedSubscription;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * 绫� SubscriptionRunner 鍔熻兘鎻忚堪锛�
+ *
+ * @author mes
+ * @version 0.0.1
+ * @date 2022/01/01 23:49
+ */
+@Slf4j
+public class SubscriptionRunner {
+ /**
+ * 鐐逛綅list
+ */
+ private final List<String> identifiers;
+
+ private final double samplingInterval;
+
+ public SubscriptionRunner(List<String> identifiers) {
+ this.identifiers = identifiers;
+ this.samplingInterval = 1000.0D;
+ }
+
+ public SubscriptionRunner(List<String> identifiers, double samplingInterval) {
+ this.identifiers = identifiers;
+ this.samplingInterval = samplingInterval;
+ }
+
+ public void run(OpcUaClient opcUaClient, SubscriptionCallback callback) {
+
+ final CountDownLatch downLatch = new CountDownLatch(1);
+
+ //娣诲姞璁㈤槄鐩戝惉鍣紝鐢ㄤ簬澶勭悊鏂嚎閲嶈繛鍚庣殑璁㈤槄闂
+ opcUaClient.getSubscriptionManager().addSubscriptionListener(new CustomSubscriptionListener(opcUaClient, callback));
+
+ //澶勭悊璁㈤槄閫昏緫
+ handler(opcUaClient, callback);
+
+ try {
+ //鎸佺画鐩戝惉
+ downLatch.await();
+ } catch (Exception e) {
+ log.error("璁㈤槄鏃跺嚭鐜颁簡寮傚父锛歿}", e.getMessage(), e);
+ }
+ }
+
+ private void handler(OpcUaClient opcUaClient, SubscriptionCallback callback) {
+ try {
+ //鍒涘缓璁㈤槄
+ ManagedSubscription subscription = ManagedSubscription.create(opcUaClient, samplingInterval);
+ subscription.setDefaultSamplingInterval(samplingInterval);
+ subscription.setDefaultQueueSize(UInteger.valueOf(10));
+
+ List<NodeId> nodeIdList = new ArrayList<>();
+ for (String identifier : identifiers) {
+ nodeIdList.add(CustomUtil.parseNodeId(identifier));
+ }
+ List<ManagedDataItem> dataItemList = subscription.createDataItems(nodeIdList);
+ for (ManagedDataItem dataItem : dataItemList) {
+ dataItem.addDataValueListener((item) -> {
+ try {
+ callback.onSubscribe
+ (dataItem, item);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ });
+ }
+ } catch (Exception e) {
+ log.error("璁㈤槄鏃跺嚭鐜颁簡寮傚父锛歿}", e.getMessage(), e);
+ }
+ }
+
+ private class CustomSubscriptionListener implements UaSubscriptionManager.SubscriptionListener {
+ private final OpcUaClient client;
+ private final SubscriptionCallback callback;
+
+ public CustomSubscriptionListener(OpcUaClient client, SubscriptionCallback callback) {
+ this.client = client;
+ this.callback = callback;
+ }
+
+ /**
+ * 閲嶈繛鏃� 灏濊瘯鎭㈠涔嬪墠鐨勮闃呭け璐ユ椂 浼氳皟鐢ㄦ鏂规硶
+ *
+ * @param uaSubscription 璁㈤槄
+ * @param statusCode 鐘舵��
+ */
+ @Override
+ public void onSubscriptionTransferFailed(UaSubscription uaSubscription, StatusCode statusCode) {
+ log.debug("鎭㈠璁㈤槄澶辫触 闇�瑕侀噸鏂拌闃�");
+ //鍦ㄥ洖璋冩柟娉曚腑閲嶆柊璁㈤槄
+ handler(client, callback);
+ }
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/service/MiloService.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/service/MiloService.java
new file mode 100644
index 0000000..45c5658
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/service/MiloService.java
@@ -0,0 +1,553 @@
+package com.mes.milo.service;
+
+import com.mes.milo.configuration.MiloProperties;
+import com.mes.milo.model.ReadWriteEntity;
+import com.mes.milo.model.WriteEntity;
+import com.mes.milo.pool.MiloConnectPool;
+import com.mes.milo.runner.BrowseNodeRunner;
+import com.mes.milo.runner.BrowseRunner;
+import com.mes.milo.runner.ReadValuesRunner;
+import com.mes.milo.runner.WriteValuesRunner;
+import com.mes.milo.runner.subscription.SubscriptionCallback;
+import com.mes.milo.runner.subscription.SubscriptionRunner;
+import com.mes.milo.utils.CustomUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
+import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author mes
+ * @date 2020/4/25
+ * @desc milo-spring-boot-starter
+ * @since 0.0.1
+ */
+@Service
+@Slf4j
+public class MiloService {
+ private final MiloConnectPool connectPool;
+ private final MiloProperties properties;
+
+ public MiloService(MiloConnectPool connectPool, MiloProperties properties) {
+ this.connectPool = connectPool;
+ this.properties = properties;
+ }
+
+ /**
+ * 閬嶅巻OPC UA鏈嶅姟鍣ㄦ牴鑺傜偣
+ *
+ * @return 鏍硅妭鐐瑰垪琛�
+ */
+ public List<String> browseRoot() throws Exception {
+ return browseRoot(null);
+ }
+
+ /**
+ * 閬嶅巻OPC UA鏈嶅姟鍣ㄦ牴鑺傜偣
+ *
+ * @param clientName 閰嶇疆key
+ * @return 鏍硅妭鐐瑰垪琛�
+ */
+ public List<String> browseRoot(String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ BrowseRunner runner = new BrowseRunner();
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ return runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * 閬嶅巻OPC UA鏈嶅姟鍣ㄦ寚瀹氳妭鐐�
+ *
+ * @param browseRoot 鑺傜偣鍚嶇О
+ * @return 鎸囧畾鑺傜偣 tag鍒楄〃
+ */
+ public List<String> browseNode(String browseRoot) throws Exception {
+ return browseNode(browseRoot, null);
+ }
+
+ /**
+ * 閬嶅巻OPC UA鏈嶅姟鍣ㄦ寚瀹氳妭鐐�
+ *
+ * @param browseRoot 鑺傜偣鍚嶇О
+ * @param clientName 閰嶇疆key
+ * @return 鎸囧畾鑺傜偣 tag鍒楄〃
+ */
+ public List<String> browseNode(String browseRoot, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ BrowseNodeRunner runner = new BrowseNodeRunner(browseRoot);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ return runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * 鎸囧畾绫诲瀷 鍐欏叆kep鐐逛綅鍊�
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeSpecifyType(WriteEntity entity) throws Exception {
+ writeSpecifyType(Collections.singletonList(entity));
+ }
+
+ /**
+ * 鎸囧畾绫诲瀷 鍐欏叆kep鐐逛綅鍊�
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeSpecifyType(WriteEntity entity, String clientName) throws Exception {
+ writeSpecifyType(Collections.singletonList(entity), clientName);
+ }
+
+ /**
+ * 鎸囧畾绫诲瀷 鍐欏叆kep鐐逛綅鍊硷紝鍙壒閲忓啓鍏ヤ笉鍚岀被鍨嬬殑鍊�
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeSpecifyType(List<WriteEntity> entities) throws Exception {
+ writeSpecifyType(entities, null);
+ }
+
+ /**
+ * 鎸囧畾绫诲瀷 鍐欏叆kep鐐逛綅鍊硷紝鍙壒閲忓啓鍏ヤ笉鍚岀被鍨嬬殑鍊�
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeSpecifyType(List<WriteEntity> entities, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ WriteValuesRunner runner = new WriteValuesRunner(entities);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ }
+
+ /**
+ * 鍐欏叆kep鐐逛綅鍊�
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcUa(ReadWriteEntity entity) throws Exception {
+ writeToOpcUa(Collections.singletonList(entity));
+ }
+
+ /**
+ * 鍐欏叆kep鐐逛綅鍊�
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcUa(ReadWriteEntity entity, String clientName) throws Exception {
+ writeToOpcUa(Collections.singletonList(entity), clientName);
+ }
+
+ /**
+ * 鍐欏叆kep鐐逛綅鍊�
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcUa(List<ReadWriteEntity> entities) throws Exception {
+ writeToOpcUa(entities, null);
+ }
+
+ /**
+ * 鍐欏叆kep鐐逛綅鍊�
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcUa(List<ReadWriteEntity> entities, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ List<WriteEntity> writeEntityList = new ArrayList<>();
+ if (!entities.isEmpty()) {
+ for (ReadWriteEntity entity : entities) {
+ writeEntityList.add(WriteEntity.builder()
+ .identifier(entity.getIdentifier())
+ .variant(new Variant(entity.getValue()))
+ .build());
+ }
+ }
+ WriteValuesRunner runner = new WriteValuesRunner(writeEntityList);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Char<br/>
+ * 8浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcChar(ReadWriteEntity entity) throws Exception {
+ writeToOpcChar(Collections.singletonList(entity));
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Char<br/>
+ * 8浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcChar(ReadWriteEntity entity, String clientName) throws Exception {
+ writeToOpcChar(Collections.singletonList(entity), clientName);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Char<br/>
+ * 8浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcChar(List<ReadWriteEntity> entities) throws Exception {
+ writeToOpcChar(entities, null);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Char<br/>
+ * 8浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcChar(List<ReadWriteEntity> entities, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ List<WriteEntity> writeEntityList = new ArrayList<>();
+ if (!entities.isEmpty()) {
+ for (ReadWriteEntity entity : entities) {
+ writeEntityList.add(WriteEntity.builder()
+ .identifier(entity.getIdentifier())
+ .variant(new Variant(((Integer) entity.getValue()).byteValue()))
+ .build());
+ }
+ }
+ WriteValuesRunner runner = new WriteValuesRunner(writeEntityList);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Byte<br/>
+ * 8浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcByte(ReadWriteEntity entity) throws Exception {
+ writeToOpcByte(Collections.singletonList(entity));
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Byte<br/>
+ * 8浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcByte(ReadWriteEntity entity, String clientName) throws Exception {
+ writeToOpcByte(Collections.singletonList(entity), clientName);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Byte<br/>
+ * 8浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcByte(List<ReadWriteEntity> entities) throws Exception {
+ writeToOpcByte(entities, null);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Byte<br/>
+ * 8浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcByte(List<ReadWriteEntity> entities, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ List<WriteEntity> writeEntityList = new ArrayList<>();
+ if (!entities.isEmpty()) {
+ for (ReadWriteEntity entity : entities) {
+ writeEntityList.add(WriteEntity.builder()
+ .identifier(entity.getIdentifier())
+ .variant(new Variant(Unsigned.ubyte((Integer) entity.getValue())))
+ .build());
+ }
+ }
+ WriteValuesRunner runner = new WriteValuesRunner(writeEntityList);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Short<br/>
+ * 16浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcShort(ReadWriteEntity entity) throws Exception {
+ writeToOpcShort(Collections.singletonList(entity));
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Short<br/>
+ * 16浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcShort(ReadWriteEntity entity, String clientName) throws Exception {
+ writeToOpcShort(Collections.singletonList(entity), clientName);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Short<br/>
+ * 16浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcShort(List<ReadWriteEntity> entities) throws Exception {
+ writeToOpcShort(entities, null);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Short<br/>
+ * 16浣嶅甫绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcShort(List<ReadWriteEntity> entities, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ List<WriteEntity> writeEntityList = new ArrayList<>();
+ if (!entities.isEmpty()) {
+ for (ReadWriteEntity entity : entities) {
+ writeEntityList.add(WriteEntity.builder()
+ .identifier(entity.getIdentifier())
+ .variant(new Variant(((Integer) entity.getValue()).shortValue()))
+ .build());
+ }
+ }
+ WriteValuesRunner runner = new WriteValuesRunner(writeEntityList);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Word<br/>
+ * 16浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcWord(ReadWriteEntity entity) throws Exception {
+ writeToOpcWord(Collections.singletonList(entity));
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Word<br/>
+ * 16浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entity 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcWord(ReadWriteEntity entity, String clientName) throws Exception {
+ writeToOpcWord(Collections.singletonList(entity), clientName);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Word<br/>
+ * 16浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ */
+ public void writeToOpcWord(List<ReadWriteEntity> entities) throws Exception {
+ writeToOpcWord(entities, null);
+ }
+
+ /**
+ * kepware 鏁版嵁绫诲瀷涓猴細Word<br/>
+ * 16浣嶆棤绗﹀彿鏁存暟
+ *
+ * @param entities 寰呭啓鍏ユ暟鎹�
+ * @param clientName 閰嶇疆key
+ */
+ public void writeToOpcWord(List<ReadWriteEntity> entities, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ List<WriteEntity> writeEntityList = new ArrayList<>();
+ if (!entities.isEmpty()) {
+ for (ReadWriteEntity entity : entities) {
+ writeEntityList.add(WriteEntity.builder()
+ .identifier(entity.getIdentifier())
+ .variant(new Variant(Unsigned.ushort((Integer) entity.getValue())))
+ .build());
+ }
+ }
+ WriteValuesRunner runner = new WriteValuesRunner(writeEntityList);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ }
+
+ /**
+ * 璇诲彇kep鐐逛綅鍊�
+ *
+ * @param id 鐐逛綅id
+ * @return
+ */
+ public ReadWriteEntity readFromOpcUa(String id) throws Exception {
+ return readFromOpcUa(id, null);
+ }
+
+ /**
+ * 璇诲彇kep鐐逛綅鍊�
+ *
+ * @param id 鐐逛綅id
+ * @param clientName 閰嶇疆key
+ * @return
+ */
+ public ReadWriteEntity readFromOpcUa(String id, String clientName) throws Exception {
+ List<ReadWriteEntity> entityList = readFromOpcUa(Collections.singletonList(id), clientName);
+ if (!entityList.isEmpty()) {
+ return entityList.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * 璇诲彇kep鐐逛綅鍊�
+ *
+ * @param ids 鐐逛綅id鏁扮粍
+ * @return
+ */
+ public List<ReadWriteEntity> readFromOpcUa(List<String> ids) throws Exception {
+ return readFromOpcUa(ids, null);
+ }
+
+ /**
+ * 璇诲彇kep鐐逛綅鍊�
+ *
+ * @param ids 鐐逛綅id鏁扮粍
+ * @param clientName 閰嶇疆key
+ * @return
+ */
+ public List<ReadWriteEntity> readFromOpcUa(List<String> ids, String clientName) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ ReadValuesRunner runner = new ReadValuesRunner(ids);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ return runner.run(client);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ return new ArrayList<>();
+ }
+
+ /**
+ * 璁㈤槄kep鐐逛綅鍊�
+ *
+ * @param ids 鐐逛綅id鏁扮粍
+ * @return
+ */
+ public void subscriptionFromOpcUa(List<String> ids, SubscriptionCallback callback) throws Exception {
+ subscriptionFromOpcUa(ids, 1000.0, callback);
+ }
+
+ /**
+ * 璁㈤槄kep鐐逛綅鍊�
+ *
+ * @param ids 鐐逛綅id鏁扮粍
+ * @param clientName 閰嶇疆key
+ * @return
+ */
+ public void subscriptionFromOpcUa(List<String> ids, String clientName, SubscriptionCallback callback) throws Exception {
+ subscriptionFromOpcUa(ids, 1000.0, clientName, callback);
+ }
+
+ /**
+ * 璁㈤槄kep鐐逛綅鍊�
+ *
+ * @param ids 鐐逛綅id鏁扮粍
+ * @param samplingInterval 璁㈤槄鏃堕棿闂撮殧 榛樿1000 ms
+ * @return
+ */
+ public void subscriptionFromOpcUa(List<String> ids, double samplingInterval, SubscriptionCallback callback) throws Exception {
+ subscriptionFromOpcUa(ids, samplingInterval, null, callback);
+ }
+
+ /**
+ * 璁㈤槄kep鐐逛綅鍊�
+ *
+ * @param ids 鐐逛綅id鏁扮粍
+ * @param samplingInterval 璁㈤槄鏃堕棿闂撮殧 榛樿1000 ms
+ * @param clientName 閰嶇疆key
+ * @return
+ */
+ public void subscriptionFromOpcUa(List<String> ids, double samplingInterval, String clientName, SubscriptionCallback callback) throws Exception {
+ MiloProperties.Config config = CustomUtil.getConfig(properties, clientName);
+ SubscriptionRunner runner = new SubscriptionRunner(ids, samplingInterval);
+ OpcUaClient client = connectPool.borrowObject(config);
+ if (client != null) {
+ try {
+ runner.run(client, callback);
+ } finally {
+ connectPool.returnObject(config, client);
+ }
+ }
+ }
+
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/CustomUtil.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/CustomUtil.java
new file mode 100644
index 0000000..37df820
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/CustomUtil.java
@@ -0,0 +1,114 @@
+package com.mes.milo.utils;
+
+import com.google.common.collect.Sets;
+import com.mes.milo.configuration.MiloProperties;
+import com.mes.milo.exception.EndPointNotFoundException;
+import com.mes.milo.exception.IdentityNotFoundException;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.springframework.util.StringUtils;
+
+import java.net.*;
+import java.util.*;
+
+/**
+ * @author mes
+ * @version 0.0.1
+ * @desc
+ * @since 2020/4/13
+ */
+@Slf4j
+public class CustomUtil {
+
+ private static final String OPC_UA_NOT_CONFIG = "璇烽厤缃甇PC UA鍦板潃淇℃伅";
+
+ private CustomUtil() {
+ }
+
+ public static String getHostname() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException var1) {
+ return "localhost";
+ }
+ }
+
+ public static Set<String> getHostnames(String address) {
+ return getHostnames(address, true);
+ }
+
+ public static Set<String> getHostnames(String address, boolean includeLoopback) {
+ HashSet<String> hostnames = Sets.newHashSet();
+
+ try {
+ InetAddress inetAddress = InetAddress.getByName(address);
+ if (inetAddress.isAnyLocalAddress()) {
+ try {
+ Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();
+
+ for (NetworkInterface ni : Collections.list(nis)) {
+ Collections.list(ni.getInetAddresses()).forEach((ia) -> {
+ if (ia instanceof Inet4Address) {
+ boolean loopback = ia.isLoopbackAddress();
+ if (!loopback || includeLoopback) {
+ hostnames.add(ia.getHostName());
+ hostnames.add(ia.getHostAddress());
+ hostnames.add(ia.getCanonicalHostName());
+ }
+ }
+
+ });
+ }
+ } catch (SocketException var7) {
+ log.warn("Failed to NetworkInterfaces for bind address: {}", address, var7);
+ }
+ } else {
+ boolean loopback = inetAddress.isLoopbackAddress();
+ if (!loopback || includeLoopback) {
+ hostnames.add(inetAddress.getHostName());
+ hostnames.add(inetAddress.getHostAddress());
+ hostnames.add(inetAddress.getCanonicalHostName());
+ }
+ }
+ } catch (UnknownHostException var8) {
+ log.warn("Failed to get InetAddress for bind address: {}", address, var8);
+ }
+
+ return hostnames;
+ }
+
+ public static NodeId parseNodeId(String identifier) {
+ NodeId nodeId = new NodeId(2, identifier);
+ if (identifier.startsWith("ns=") && identifier.contains(";")) {
+ nodeId = NodeId.parseOrNull(identifier);
+ }
+ if (nodeId == null) {
+ throw new IdentityNotFoundException("NodeId 瑙f瀽澶辫触锛岃妫�鏌�");
+ }
+ return nodeId;
+ }
+
+ public static void verifyProperties(MiloProperties properties) {
+ if (properties.getConfig().isEmpty()) {
+ throw new EndPointNotFoundException(OPC_UA_NOT_CONFIG);
+ }
+ if (!StringUtils.hasText(properties.getPrimary())) {
+ Set<String> keySet = properties.getConfig().keySet();
+ properties.setPrimary(keySet.stream().findFirst().orElseThrow(() -> new EndPointNotFoundException(OPC_UA_NOT_CONFIG)));
+ }
+ properties.getConfig().forEach((key, config) -> {
+ if (!StringUtils.hasText(config.getEndpoint())) {
+ throw new EndPointNotFoundException(OPC_UA_NOT_CONFIG + ": " + key);
+ }
+ });
+ }
+
+ public static MiloProperties.Config getConfig(MiloProperties properties) {
+ return getConfig(properties, null);
+ }
+
+ public static MiloProperties.Config getConfig(MiloProperties properties, String clientName) {
+ Map<String, MiloProperties.Config> config = properties.getConfig();
+ return StringUtils.hasText(clientName) ? config.get(clientName) : config.get(properties.getPrimary());
+ }
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/KeyStoreLoader.java b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/KeyStoreLoader.java
new file mode 100644
index 0000000..ad4c6bb
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/java/com/mes/milo/utils/KeyStoreLoader.java
@@ -0,0 +1,123 @@
+package com.mes.milo.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.stack.client.security.DefaultClientCertificateValidator;
+import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager;
+import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder;
+import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.*;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.regex.Pattern;
+
+@Slf4j
+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 X509Certificate clientCertificate;
+ private X509Certificate[] clientCertificateChain;
+ private KeyPair clientKeyPair;
+ private DefaultClientCertificateValidator certificateValidator;
+
+ public KeyStoreLoader load() throws Exception {
+ Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
+ Files.createDirectories(securityTempDir);
+ if (!Files.exists(securityTempDir)) {
+ throw new Exception("unable to create security dir: " + securityTempDir);
+ }
+
+ File pkiDir = securityTempDir.resolve("pki").toFile();
+
+ log.info("security temp dir: {}", securityTempDir.toAbsolutePath());
+
+ KeyStore keyStore = KeyStore.getInstance("PKCS12");
+
+ Path serverKeyStore = securityTempDir.resolve("milo-client.pfx");
+
+ DefaultTrustListManager trustListManager = new DefaultTrustListManager(pkiDir);
+
+ certificateValidator = new DefaultClientCertificateValidator(trustListManager);
+
+ log.info("Loading KeyStore at {}", serverKeyStore);
+
+ if (!Files.exists(serverKeyStore)) {
+ keyStore.load(null, PASSWORD);
+
+ KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
+
+ SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair)
+ .setCommonName("Milo Client")
+ .setOrganization("kangaroohy")
+ .setOrganizationalUnit("dev")
+ .setLocalityName("Folsom")
+ .setStateName("CA")
+ .setCountryCode("US")
+ .setApplicationUri("urn:kangaroohy:milo: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 : CustomUtil.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 clientPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD);
+ if (clientPrivateKey instanceof PrivateKey) {
+ clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS);
+
+ clientCertificateChain = Arrays.stream(keyStore.getCertificateChain(CLIENT_ALIAS))
+ .map(X509Certificate.class::cast)
+ .toArray(X509Certificate[]::new);
+
+ PublicKey clientPublicKey = clientCertificate.getPublicKey();
+ clientKeyPair = new KeyPair(clientPublicKey, (PrivateKey) clientPrivateKey);
+ }
+
+ return this;
+ }
+
+ public X509Certificate getClientCertificate() {
+ return clientCertificate;
+ }
+
+ public X509Certificate[] getClientCertificateChain() {
+ return clientCertificateChain;
+ }
+
+ public DefaultClientCertificateValidator getCertificateValidator() {
+ return certificateValidator;
+ }
+
+ public KeyPair getClientKeyPair() {
+ return clientKeyPair;
+ }
+
+}
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring.factories b/hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..dfcfa97
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.mes.milo.configuration.MiloAutoConfiguration
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
new file mode 100644
index 0000000..eda2fe7
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -0,0 +1 @@
+com.mes.milo.configuration.MiloAutoConfiguration
\ No newline at end of file
diff --git a/hangzhoumesParent/common/opcuaClient/src/main/resources/application.yml b/hangzhoumesParent/common/opcuaClient/src/main/resources/application.yml
new file mode 100644
index 0000000..1ea573c
--- /dev/null
+++ b/hangzhoumesParent/common/opcuaClient/src/main/resources/application.yml
@@ -0,0 +1,9 @@
+mes:
+ milo:
+ primary: default
+ config:
+ default:
+ endpoint: opc.tcp://zidonghua:49320
+ security-policy: basic256sha256
+ username: zsh
+ password: 1qaz2wsx3edc4rfv
\ No newline at end of file
diff --git a/hangzhoumesParent/common/pom.xml b/hangzhoumesParent/common/pom.xml
index c8bb428..affa009 100644
--- a/hangzhoumesParent/common/pom.xml
+++ b/hangzhoumesParent/common/pom.xml
@@ -15,6 +15,7 @@
<modules>
<module>servicebase</module>
<module>springsecurity</module>
+ <module>opcuaClient</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/OpcCacheGlassTask.java b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/OpcCacheGlassTask.java
new file mode 100644
index 0000000..47b2fd0
--- /dev/null
+++ b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/OpcCacheGlassTask.java
@@ -0,0 +1,31 @@
+package com.mes.job;
+
+import com.mes.milo.runner.subscription.SubscriptionCallback;
+import com.mes.milo.service.MiloService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+
+/**
+ * @Author : zhoush
+ * @Date: 2024/10/10 8:05
+ * @Description:
+ */
+@Component
+@Slf4j
+public class OpcCacheGlassTask {
+
+ @Autowired
+ MiloService miloService;
+
+ @Autowired
+ SubscriptionCallback cacheGlassSubscriptionCallback;
+
+ @Scheduled(fixedDelay = Long.MAX_VALUE)
+ public void startOpcTask() throws Exception {
+ miloService.subscriptionFromOpcUa(Arrays.asList("my.device.x1", "my.device.x2"), cacheGlassSubscriptionCallback);
+ }
+}
diff --git a/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/entity/CacheGlassInfo.java b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/entity/CacheGlassInfo.java
new file mode 100644
index 0000000..1a5b591
--- /dev/null
+++ b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/job/entity/CacheGlassInfo.java
@@ -0,0 +1,46 @@
+package com.mes.job.entity;
+
+import lombok.Data;
+
+/**
+ * @Author : zhoush
+ * @Date: 2024/9/20 10:56
+ * @Description:
+ */
+@Data
+public class CacheGlassInfo {
+
+ /**
+ * 璇锋眰瀛�
+ */
+ private String requestWord;
+ /**
+ * 璇锋眰瀛�
+ */
+ private String confirmationWrod;
+
+ /**
+ * 鐜荤拑id
+ */
+ private String glassId;
+
+ /**
+ * 鏈�1鐘舵��
+ */
+ private String oneMachineState;
+
+ /**
+ * 鏈�2鐘舵��
+ */
+ private String twoMachineState;
+
+ /**
+ * 涓�鍙峰崸寮忓綋鍓嶆牸瀛�
+ */
+ private int oneCurrentSlot;
+
+ /**
+ * 浜屽彿鍗у紡褰撳墠鏍煎瓙
+ */
+ private int twoCurrentSlot;
+}
diff --git a/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/opccallback/CacheGlassSubscriptionCallback.java b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/opccallback/CacheGlassSubscriptionCallback.java
new file mode 100644
index 0000000..eaf7695
--- /dev/null
+++ b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/java/com/mes/opccallback/CacheGlassSubscriptionCallback.java
@@ -0,0 +1,35 @@
+package com.mes.opccallback;
+
+import com.mes.milo.model.ReadWriteEntity;
+import com.mes.milo.runner.subscription.SubscriptionCallback;
+import com.mes.milo.service.MiloService;
+import lombok.extern.slf4j.Slf4j;
+import org.eclipse.milo.opcua.sdk.client.subscriptions.ManagedDataItem;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @Author : zhoush
+ * @Date: 2024/10/10 14:13
+ * @Description:
+ */
+@Service
+@Slf4j
+public class CacheGlassSubscriptionCallback implements SubscriptionCallback {
+
+ @Autowired
+ MiloService miloService;
+
+ @Override
+ public void onSubscribe(ManagedDataItem dataItem, DataValue value) {
+ try {
+ log.info("{}___________{}" + dataItem, value);
+ ReadWriteEntity readWriteEntity = miloService.readFromOpcUa("my.device.x1");
+ log.info("{}___________{}" + readWriteEntity);
+ } catch (Exception e) {
+
+ }
+
+ }
+}
diff --git a/hangzhoumesParent/moduleService/CacheGlassModule/src/main/resources/application.yml b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/resources/application.yml
index b872b63..eaa6417 100644
--- a/hangzhoumesParent/moduleService/CacheGlassModule/src/main/resources/application.yml
+++ b/hangzhoumesParent/moduleService/CacheGlassModule/src/main/resources/application.yml
@@ -27,4 +27,12 @@
secondLength: 400
sequence:
order: false
+ milo:
+ primary: default
+ config:
+ default:
+ endpoint: opc.tcp://127.0.0.1:49320
+ security-policy: basic256sha256
+ username: zsh
+ password: 1qaz2wsx3edc4rfv
diff --git a/hangzhoumesParent/moduleService/pom.xml b/hangzhoumesParent/moduleService/pom.xml
index 55ea5f6..b043adf 100644
--- a/hangzhoumesParent/moduleService/pom.xml
+++ b/hangzhoumesParent/moduleService/pom.xml
@@ -55,6 +55,11 @@
<groupId>com.mes</groupId>
<version>1.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <artifactId>opcuaClient</artifactId>
+ <groupId>com.mes</groupId>
+ <version>1.0-SNAPSHOT</version>
+ </dependency>
<!--寮�鍙戣�呭伐鍏�-->
<!-- <dependency>-->
diff --git a/hangzhoumesParent/moduleService/yiwumes/src/main/resources/application.yml b/hangzhoumesParent/moduleService/yiwumes/src/main/resources/application.yml
index 3b841eb..1bcc515 100644
--- a/hangzhoumesParent/moduleService/yiwumes/src/main/resources/application.yml
+++ b/hangzhoumesParent/moduleService/yiwumes/src/main/resources/application.yml
@@ -15,4 +15,14 @@
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+mes:
+ milo:
+ primary: default
+ config:
+ default:
+ endpoint: opc.tcp://127.0.0.1:49320
+ security-policy: basic256sha256
+ username: zsh
+ password: 1qaz2wsx3edc4rfv
+
--
Gitblit v1.8.0