feat():OPC UA环境

This commit is contained in:
Penknife
2025-08-18 10:41:44 +08:00
parent a53dc88795
commit 43eeef8089
10 changed files with 258 additions and 25 deletions

View File

@@ -47,12 +47,12 @@
<artifactId>ruoyi-framework</artifactId> <artifactId>ruoyi-framework</artifactId>
</dependency> </dependency>
<!-- 加密解密工具--> <!-- 加密解密工具 opc-->
<dependency> <!-- <dependency>-->
<groupId>org.bouncycastle</groupId> <!-- <groupId>org.bouncycastle</groupId>-->
<artifactId>bcprov-jdk15on</artifactId> <!-- <artifactId>bcprov-jdk15on</artifactId>-->
<version>1.70</version> <!-- <version>1.70</version>-->
</dependency> <!-- </dependency>-->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
@@ -108,6 +108,14 @@
<artifactId>spring-boot-starter-websocket</artifactId> <artifactId>spring-boot-starter-websocket</artifactId>
</dependency> </dependency>
<!-- OPC UA 集成 -->
<dependency>
<groupId>com.kangaroohy</groupId>
<artifactId>milo-spring-boot-starter</artifactId>
<version>3.1.4.0.6.15</version>
</dependency>
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>com.alibaba</groupId>--> <!-- <groupId>com.alibaba</groupId>-->
<!-- <artifactId>fastjson</artifactId>--> <!-- <artifactId>fastjson</artifactId>-->

View File

@@ -36,14 +36,14 @@ public class AppMeasureEntryMessage extends OpcMessage {
@Schema(description = "钢带速度 (m/min)") @Schema(description = "钢带速度 (m/min)")
private BigDecimal stripSpeed; private BigDecimal stripSpeed;
@Schema(description = "入口活套位置最大值 (m)") @Schema(description = "入口活套位置(m)")
private BigDecimal entryLooperPositionMax; private BigDecimal celLength;
@Schema(description = "入口活套位置最小值 (m)") @Schema(description = "入口活套百分比(m)")
private BigDecimal entryLooperPositionMin; private BigDecimal celCapacity;
@Schema(description = "当前实际活套位置 (m)") @Schema(description = "入口活套张力(m)")
private BigDecimal entryLooperPositionCurrent; private BigDecimal tensionCel ;
@Schema(description = "清洗电压 (V)") @Schema(description = "清洗电压 (V)")
private BigDecimal cleaningVoltage; private BigDecimal cleaningVoltage;

View File

@@ -21,17 +21,14 @@ public class AppMeasureExitMessage extends OpcMessage {
@Schema(description = "钢带张力 BR8 BR9 (daN)") @Schema(description = "钢带张力 BR8 BR9 (daN)")
private BigDecimal tensionBr8Br9; private BigDecimal tensionBr8Br9;
@Schema(description = "口活套位置 (m)") @Schema(description = "口活套位置(m)")
private BigDecimal deliveryLooperPosition; private BigDecimal cxlLength;
@Schema(description = "口活套最大可用百分比 (%)") @Schema(description = "口活套百分比(m)")
private BigDecimal exitLooperMaxPercent; private BigDecimal cxlCapacity;
@Schema(description = "口活套最小可用百分比 (%)") @Schema(description = "口活套张力(m)")
private BigDecimal exitLooperMinPercent; private BigDecimal tensionCxl ;
@Schema(description = "出口活套当前百分比 (%)")
private BigDecimal exitLooperCurrentPercent;
@Schema(description = "涂油标志 (0=no, 1=yes)") @Schema(description = "涂油标志 (0=no, 1=yes)")
private Integer oilingFlag; private Integer oilingFlag;

View File

@@ -0,0 +1,74 @@
package com.fizz.business.scheduled;
import com.fizz.business.utils.RedisLockUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.stereotype.Component;
import java.util.function.Consumer;
@Slf4j
@Async("threadPoolTaskExecutor")
@Component
@EnableScheduling
public abstract class BaseSchedule {
/**
* 默认key过期时间 1800秒
*/
private static final int DEFAULT_EXPIRE_SECONDS = 1800;
protected void execute(String taskName, int cacheSeconds, String cacheKey, Runnable func) {
// 执行完2分钟 删除键值
int delayedSeconds = Math.min(120, cacheSeconds / 10);
execute(taskName, cacheSeconds, delayedSeconds, cacheKey, k -> func.run());
}
/**
* 定时任务执行 多服务防止同时执行
*
* @param taskName: 任务名称
* @param cacheSeconds: <= 0时默认时间
* @param delayedSeconds: 任务执行完延时删除key; <0时默认 cacheSeconds/10
* @param cacheKey:
* @param func: 执行的任务 key: 处理后的cacheKey
*/
protected void execute(String taskName, int cacheSeconds, int delayedSeconds, String cacheKey, Consumer<String> func) {
String threadName = Thread.currentThread().getName();
cacheKey = "scheduling:" + cacheKey;
if (cacheSeconds <= 0) {
cacheSeconds = DEFAULT_EXPIRE_SECONDS;
}
if (delayedSeconds < 0) {
delayedSeconds = cacheSeconds / 10;
}
// 不存在键值时执行
String value = "1";
boolean success = RedisLockUtil.setIfAbsent(cacheKey, value, cacheSeconds);
if (!success) {
log.debug("定时任务: {}, 取消执行. {}", taskName, threadName);
return;
}
log.debug("定时任务: {}, 开始执行. {}", taskName, threadName);
func.accept(cacheKey);
// 执行完后 延时时间内不可重复执行
if (delayedSeconds == 0) {
RedisLockUtil.delLock(cacheKey, value);
} else {
RedisLockUtil.setIfPresent(cacheKey, value, delayedSeconds);
}
log.debug("定时任务: {}, 执行结束. {}", taskName, threadName);
}
public void actionCatchException(Runnable func) {
String threadName = Thread.currentThread().getName();
try {
func.run();
} catch (Exception e) {
log.error("定时任务执行出错 " + threadName, e);
}
}
}

View File

@@ -0,0 +1,33 @@
package com.fizz.business.scheduled;
import cn.hutool.json.JSONUtil;
import com.kangaroohy.milo.model.ReadWriteEntity;
import com.kangaroohy.milo.service.MiloService;
import jdk.nashorn.internal.runtime.Debug;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
@Slf4j
//@Component
@AllArgsConstructor
public class LineMeasureSchedule extends BaseSchedule{
@Resource
MiloService miloService;
@Scheduled(fixedDelay = 200)
public void L1L2LineMeasure() {
try {
ReadWriteEntity node = miloService.readFromOpcUa("ns=2;s=通道 2.LockStautsRead.lockStauts1");
log.debug(node.toString());
node.getValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,82 @@
package com.fizz.business.utils;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import javax.validation.constraints.NotNull;
import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@Component
public class RedisLockUtil {
private static final Logger log = LoggerFactory.getLogger(RedisLockUtil.class);
public static final RedisScript<Long> UNLOCK_SCRIPT = new DefaultRedisScript("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", Long.class);
private static final String UNLOCK_LUA = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
private static RedisTemplate<String, String> REDIS_TEMPLATE;
@Autowired
public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
REDIS_TEMPLATE = redisTemplate;
}
private RedisLockUtil() {
}
public static boolean setIfAbsent(@NotNull String key, @NotNull String value, int seconds) {
Boolean result = REDIS_TEMPLATE.opsForValue().setIfAbsent(key, value, (long)seconds, TimeUnit.SECONDS);
return result != null && result;
}
public static boolean setIfPresent(@NotNull String key, @NotNull String value, int seconds) {
Boolean result = REDIS_TEMPLATE.opsForValue().setIfPresent(key, value, (long)seconds, TimeUnit.SECONDS);
return result != null && result;
}
public static void delLock(@NotNull String key, @NotNull String value) {
REDIS_TEMPLATE.execute(UNLOCK_SCRIPT, Collections.singletonList(key), new Object[]{value});
}
public static <V> Pair<Boolean, V> lockHandle(@NotNull String key, @NotNull Callable<V> callable, @NotNull Function<Exception, Pair<Boolean, V>> exceptionHandle) {
String threadName = Thread.currentThread().getName();
String msg = "RedisLockUtil " + threadName + " {}:" + key;
String value = IdUtil.simpleUUID();
boolean success = setIfAbsent(key, value, 6000);
if (!success) {
log.debug(msg, "锁获取失败");
return new Pair(false, (Object)null);
} else {
log.debug(msg, "锁获取成功");
Pair var8;
try {
V v = callable.call();
log.debug(msg, "执行完成");
var8 = new Pair(true, v);
return var8;
} catch (Exception var12) {
log.error(StrUtil.format(msg, new Object[]{"成功获取锁但执行失败"}), var12);
var8 = (Pair)exceptionHandle.apply(var12);
} finally {
delLock(key, value);
}
return var8;
}
}
public static <V> Pair<Boolean, V> lockHandle(@NotNull String key, @NotNull Callable<V> callable) {
return lockHandle(key, callable, (e) -> {
throw new RuntimeException(key + " 成功获取锁但执行失败:" + e.getMessage());
});
}
}

View File

@@ -34,6 +34,9 @@
<knife4j.version>4.5.0</knife4j.version> <knife4j.version>4.5.0</knife4j.version>
<springdoc.version>1.7.0</springdoc.version> <springdoc.version>1.7.0</springdoc.version>
<mybatis.version>3.5.2</mybatis.version> <mybatis.version>3.5.2</mybatis.version>
<milo.version>0.6.3</milo.version>
<commons-pool2.version>2.12.0</commons-pool2.version>
<!-- 2.12.0-->
</properties> </properties>
<!-- 依赖声明 --> <!-- 依赖声明 -->
@@ -212,6 +215,12 @@
<!-- <version>3.5.2</version>--> <!-- <version>3.5.2</version>-->
</dependency> </dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>${commons-pool2.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

View File

@@ -101,6 +101,10 @@ spring:
enabled: true enabled: true
max-attempts: 1 max-attempts: 1
max-interval: 200 max-interval: 200
task:
scheduling:
pool:
size: 8 #配置Scheduled定时任务为多线程
# token配置 # token配置
token: token:
@@ -160,4 +164,33 @@ knife4j:
language: zh-CN # 中文界面 language: zh-CN # 中文界面
enable-swagger-model: true # 显示模型 enable-swagger-model: true # 显示模型
enable-document-manage: true # 启用文档管理 enable-document-manage: true # 启用文档管理
cors: true # 允许跨域 cors: true # 允许跨域
kangaroohy:
milo:
primary: default
config:
default:
endpoint: opc.tcp://127.0.0.1:49320
security-policy: none
pool:
max-idle: 5
max-total: 20
min-idle: 2
initial-size: 3
#kangaroohy:
# milo:
# primary: default
# config:
# default:
# endpoint: opc.tcp://127.0.0.1:49320
# security-policy: basic256sha256
# username: OPCUA
# password: 123456
# test:
# endpoint: opc.tcp://127.0.0.1:49321
# security-policy: basic256sha256
# username: OPCUA
# password: 123456

View File

@@ -149,8 +149,6 @@
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId> <artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
</dependency> </dependency>
<!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 --> <!-- 防止进入swagger页面报类型转换错误排除3.0.0中的引用手动增加1.6.2版本 -->
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>io.swagger</groupId>--> <!-- <groupId>io.swagger</groupId>-->