Sfoglia il codice sorgente

1.调整出铁诊断模型参数中参数为区间值
2.IHD管理添加是否重复推送、过滤无效值
3.IHD Tag点编辑修改触发推送即时更新
4.分拆出渣状态为出渣状态、冲渣状态

wangxiaofei 2 mesi fa
parent
commit
16d33191e8

+ 16 - 2
taphole-iron/src/main/java/com/sckj/iron/constant/ExpressionConstants.java

@@ -7,18 +7,32 @@ package com.sckj.iron.constant;
 public class ExpressionConstants {
     //标准流速
     public static final String stdSpeed = "stdSpeed";
+    //标准流速Min
+    public static final String stdSpeedMin = "stdSpeedMin";
+    //标准流速Max
+    public static final String stdSpeedMax = "stdSpeedMax";
     //标准压差阈值
     public static final String stdPressureDiff = "stdPressureDiff";
     //标准出铁时间
     public static final String stdIronTime = "stdIronTime";
+    //标准出铁时间Min
+    public static final String stdIronTimeMin = "stdIronTimeMin";
+    //标准出铁时间Max
+    public static final String stdIronTimeMax = "stdIronTimeMax";
     //标准服务器地址
     public static final String stdServerUrl = "stdServerUrl";
-    //标准温差阈值
-    public static final String stdTempDiff = "stdTempDiff";
+    //标准温度
+    public static final String stdTemp = "stdTemp";
+    //标准温度Min
+    public static final String stdTempMin = "stdTempMin";
+    //标准温度Max
+    public static final String stdTempMax = "stdTempMax";
     //标准开口耗时
     public static final String stdOpenHour = "stdOpenHour";
     //标准出铁量
     public static final String stdIronWeight = "stdIronWeight";
+    public static final String stdIronWeightMin = "stdIronWeightMin";
+    public static final String stdIronWeightMax = "stdIronWeightMax";
 
 
     //实时数据

+ 24 - 8
taphole-iron/src/main/java/com/sckj/iron/constant/SubscribeTagConstants.java

@@ -62,29 +62,45 @@ public class SubscribeTagConstants {
         return "WZ540201.PV";
     }
 
-    //1TH-1号车总
+    //四高炉1TH-1号铁水净
     public static final String TAG_IRON_WEIGHT11(String type) {
         if (Objects.equals(type, "3")) {
-            return "BF4_1TH_1_TPC_TWT";
+            return "BF4_1TH_1_MIR_NWT";
         }
-        return "WI5401.PV";
+        return "WI5401.PV";//这个是OPC采集点是用来模拟的
     }
 
-    //1TH-2号车TPC总
+    //四高炉1TH-2号铁水净
     public static final String TAG_IRON_WEIGHT12(String type) {
         if (Objects.equals(type, "3")) {
-            return "BF4_1TH_2_TPC_TWT";
+            return "BF4_1TH_2_MIR_NWT";
         }
-        return "WI5402.PV";
+        return "WI5402.PV";//这个是OPC采集点是用来模拟的
     }
 
-    //渣状态
-    public static final String TAG_FLUSH_STATUS(String type) {
+    //渣状态
+    public static final String TAG_CHUZ_STATUS(String type) {
         if (Objects.equals(type, "3")) {
             return "BF4_1TH_SLAG_TAPPING_SIGNAL";
         }
         return "AOD25607.PV";
     }
 
+    //水渣泵(粒化给水泵)是否开启
+    public static final String TAG_SZB_STATUS(String type) {
+        if (Objects.equals(type, "3")) {
+            return "BF4SSZ_I_GRANUL_PUMP_RNG";
+        }
+        return "AOD25607.PV";
+    }
+
+    //冲渣阀(吹制阀)是否开启
+    public static final String TAG_CZF_STATUS(String type) {
+        if (Objects.equals(type, "3")) {
+            return "BF4SSZ_1_SLAGFLV_OIP";
+        }
+        return "AOD25607.PV";
+    }
+
 
 }

+ 3 - 0
taphole-iron/src/main/java/com/sckj/iron/entity/TIronModel.java

@@ -59,4 +59,7 @@ public class TIronModel implements Serializable {
     @TableField(updateStrategy = FieldStrategy.IGNORED)
     private Long audioId;
 
+    @ApiModelProperty(value = "满足公式条件多少次以后发出并且记录预警")
+    private Integer triggerCondCount;
+
 }

+ 107 - 58
taphole-iron/src/main/java/com/sckj/iron/socketio/DeviceEventListener.java

@@ -17,6 +17,7 @@ import com.sckj.iron.dto.*;
 import com.sckj.iron.entity.*;
 import com.sckj.iron.service.impl.*;
 import com.sckj.iron.util.LocalDateUtils;
+import com.sckj.iron.util.RangeUtils;
 import com.sckj.iron.vo.IronStepVO;
 import com.sckj.l2.entity.TL2Data;
 import com.sckj.l2.service.impl.TL2DataServiceImpl;
@@ -130,13 +131,17 @@ public class DeviceEventListener extends EventListener { //
     private static final String IRON_STATUS = "ironStatus";
 
     //鱼雷罐车
-    private static final String CAR_STATUS = "ylgc";
-    //拔炮
-    private static final String GUN_STATUS = "npkkj";
+    private static final String YLGC_STATUS = "ylgc";
+    //泥炮机
+    private static final String NPJ_STATUS = "npj";
+    //开口机状态
+    private static final String KKJ_STATUS = "kkj";
     //摆动溜咀
-    private static final String MOUTH_STATUS = "bdlz";
+    private static final String BDLZ_STATUS = "bdlz";
     //冲渣状态
-    private static final String FLUSH_STATUS = "flushStatus";
+    private static final String CHONGZ_STATUS = "chongz";
+    //出渣状态
+    private static final String CHUZ_STATUS = "chuz";
 
     // 1.创建表达式解析器
     private static final SpelExpressionParser mParser = new SpelExpressionParser();
@@ -180,38 +185,39 @@ public class DeviceEventListener extends EventListener { //
     private static final String NODE = "node";
 
     //实时数据
-    Map<String, Object> mRealtimeData = new ConcurrentHashMap<>();
+    private Map<String, Object> mRealtimeData = new ConcurrentHashMap<>();
 
     //实时状态
-    Map<String, Object> mRealtimeStatus = new ConcurrentHashMap<>();
+    private Map<String, Object> mRealtimeStatus = new ConcurrentHashMap<>();
 
     private AtomicDouble speed1 = new AtomicDouble(0);
 
     private AtomicDouble speed2 = new AtomicDouble(0);
 
     //标准流速
-    private AtomicDouble STANDARD_SPEED = new AtomicDouble(0);
+    private String STANDARD_SPEED = "";
 
     //标准压差阈值
     private AtomicDouble STANDARD_PRESSURE_DIFF = new AtomicDouble(0);
 
     //标准出铁时间
-    private AtomicDouble STANDARD_IRON_TIME = new AtomicDouble(0);
+    private String STANDARD_IRON_TIME = "";
 
     //标准温差阈值
-    private AtomicInteger STANDARD_TEMP_DIFF = new AtomicInteger(0);
+    private String STANDARD_TEMP = "";
 
     //标准开口耗时
     private AtomicDouble STANDARD_OPEN_HOUR = new AtomicDouble(0);
 
-    private AtomicDouble STANDARD_IRON_WEIGHT = new AtomicDouble(0);
+    //标注出铁量
+    private String STANDARD_IRON_WEIGHT = "";
 
 
     //实时温度最大值
-    private AtomicDouble tempMax = new AtomicDouble(0);
+    private AtomicDouble rtTempMax = new AtomicDouble(0);
 
     //实时温度最小值
-    private AtomicDouble tempMin = new AtomicDouble(0);
+    private AtomicDouble rtTempMin = new AtomicDouble(0);
 
     private String SERVER_URL = "";
 
@@ -339,28 +345,35 @@ public class DeviceEventListener extends EventListener { //
         if (ObjectUtils.isNotEmpty(mIronParams)) {
             for (TIronParam mIronParam : mIronParams) {
                 if (Objects.equals(mIronParam.getParamName(), ParamsConstants.iron_speed)) {
-                    STANDARD_SPEED = new AtomicDouble(Double.parseDouble(mIronParam.getParamValue()));
+                    STANDARD_SPEED = mIronParam.getParamValue();
                 } else if (Objects.equals(mIronParam.getParamName(), ParamsConstants.pressure_diff_value)) {
                     STANDARD_PRESSURE_DIFF = new AtomicDouble(Double.parseDouble(mIronParam.getParamValue()));
                 } else if (Objects.equals(mIronParam.getParamName(), ParamsConstants.iron_time)) {
-                    STANDARD_IRON_TIME = new AtomicDouble(Double.parseDouble(mIronParam.getParamValue()));
+                    STANDARD_IRON_TIME = mIronParam.getParamValue();
                 } else if (Objects.equals(mIronParam.getParamName(), ParamsConstants.server_url)) {
                     SERVER_URL = mIronParam.getParamValue();
                 } else if (Objects.equals(mIronParam.getParamName(), ParamsConstants.ironwater_temp)) {
-                    STANDARD_TEMP_DIFF = new AtomicInteger(Integer.parseInt(mIronParam.getParamValue()));
+                    STANDARD_TEMP = mIronParam.getParamValue();
                 } else if (Objects.equals(mIronParam.getParamName(), ParamsConstants.open_hour)) {
                     STANDARD_OPEN_HOUR = new AtomicDouble(Double.parseDouble(mIronParam.getParamValue()));
                 } else if (Objects.equals(mIronParam.getParamName(), ParamsConstants.iron_weight)) {
-                    STANDARD_IRON_WEIGHT = new AtomicDouble(Double.parseDouble(mIronParam.getParamValue()));
+                    STANDARD_IRON_WEIGHT = mIronParam.getParamValue();
                 }
-
-                mContext.setVariable(ExpressionConstants.stdSpeed, STANDARD_SPEED.get());
+//                mContext.setVariable(ExpressionConstants.stdSpeed, STANDARD_SPEED.get());
+                mContext.setVariable(ExpressionConstants.stdSpeedMin, Double.parseDouble(STANDARD_SPEED.split("-")[0]));
+                mContext.setVariable(ExpressionConstants.stdSpeedMax, Double.parseDouble(STANDARD_SPEED.split("-")[1]));
                 mContext.setVariable(ExpressionConstants.stdPressureDiff, STANDARD_PRESSURE_DIFF.get());
-                mContext.setVariable(ExpressionConstants.stdIronTime, STANDARD_IRON_TIME.get());
+//                mContext.setVariable(ExpressionConstants.stdIronTime, STANDARD_IRON_TIME.get());
+                mContext.setVariable(ExpressionConstants.stdIronTimeMin, Double.parseDouble(STANDARD_IRON_TIME.split("-")[0]));
+                mContext.setVariable(ExpressionConstants.stdIronTimeMax, Double.parseDouble(STANDARD_IRON_TIME.split("-")[1]));
                 mContext.setVariable(ExpressionConstants.stdServerUrl, SERVER_URL);
-                mContext.setVariable(ExpressionConstants.stdTempDiff, STANDARD_TEMP_DIFF);
+//                mContext.setVariable(ExpressionConstants.stdTemp, STANDARD_TEMP_DIFF);
+                mContext.setVariable(ExpressionConstants.stdTempMin, Double.parseDouble(STANDARD_TEMP.split("-")[0]));
+                mContext.setVariable(ExpressionConstants.stdTempMax, Double.parseDouble(STANDARD_TEMP.split("-")[1]));
                 mContext.setVariable(ExpressionConstants.stdOpenHour, STANDARD_OPEN_HOUR);
-                mContext.setVariable(ExpressionConstants.stdIronWeight, STANDARD_IRON_WEIGHT);
+//                mContext.setVariable(ExpressionConstants.stdIronWeight, STANDARD_IRON_WEIGHT);
+                mContext.setVariable(ExpressionConstants.stdIronWeightMin, Double.parseDouble(STANDARD_IRON_WEIGHT.split("-")[0]));
+                mContext.setVariable(ExpressionConstants.stdIronWeightMax, Double.parseDouble(STANDARD_IRON_WEIGHT.split("-")[1]));
             }
             log.info("STANDARD_SPEED: {},PRESSURE_DIFF_VALUE:{}", STANDARD_SPEED, STANDARD_PRESSURE_DIFF);
             log.info("IRON_TIME: {},SERVER_URL:{}", STANDARD_IRON_TIME, SERVER_URL);
@@ -422,11 +435,38 @@ public class DeviceEventListener extends EventListener { //
 
         //异步保存OPC数据
         taskExecutor.submit(() -> {
-            if (ObjectUtils.isNotEmpty(mTIronData)) {
-                //关联实时出铁信息
-                opcData.setIronDataId(mTIronData.getId());
+            if (opcData.getPointName().contains(SubscribeTagConstants.TAG_TAPHOLE1_STATUS(opcData.getServerType()))) {
+                double currentVal = Double.parseDouble(opcData.getData().toString());
+                if (currentVal > 0) {
+                    //开始出铁
+                    mTIronData = new TIronData();
+                    mTIronData.setId(ToolUtils.makeUUID());
+                    mTIronData.setIronStarttime(LocalDateUtils.formatDate(opcData.getSourceTime()));
+                    ironDataService.saveOrUpdate(mTIronData);
+                    opcData.setIronDataId(mTIronData.getId());
+                    opcDataService.save(opcData);
+                } else if (currentVal <= 0) {
+                    //结束出铁
+                    if (null != mTIronData) {
+                        mTIronData.setIronCosttime(getIronElapsedMinute());
+                        mTIronData.setIronWeight(mTotalWeight.doubleValue());
+                        mTIronData.setIronEndtime(LocalDateUtils.formatDate(opcData.getSourceTime()));
+                        ironDataService.saveOrUpdate(mTIronData);
+                        opcData.setIronDataId(mTIronData.getId());
+                        opcDataService.save(opcData);
+                        //置空,防止和录入的数据干扰到出铁周期内数据
+                        mTIronData = null;
+                    }
+                }
+            } else {
+                if (ObjectUtils.isNotEmpty(mTIronData)) {
+                    //关联实时出铁信息
+                    opcData.setIronDataId(mTIronData.getId());
+                }
+                opcDataService.save(opcData);
             }
-            opcDataService.save(opcData);
+
+
         });
         //出铁操作
         taskExecutor.submit(() -> operate(opcData));
@@ -560,10 +600,10 @@ public class DeviceEventListener extends EventListener { //
      * 3. 清空出铁总量
      */
     private void taphole1Start() {
-        mTIronData = new TIronData();
-        mTIronData.setId(ToolUtils.makeUUID());
-        mTIronData.setIronStarttime(LocalDateUtils.formatDate(new Date()));
-        ironDataService.saveOrUpdate(mTIronData);
+//        mTIronData = new TIronData();
+//        mTIronData.setId(ToolUtils.makeUUID());
+//        mTIronData.setIronStarttime(LocalDateUtils.formatDate(new Date()));
+//        ironDataService.saveOrUpdate(mTIronData);
 
         synchronized (scheduledTaskManager) {
             //清空出铁总量
@@ -603,14 +643,14 @@ public class DeviceEventListener extends EventListener { //
 
                     log.info("1号车受铁速度:{}", speed1.get());
                     log.info("2号车受铁速度:{}", speed2.get());
-                    log.info("标准受铁速度:{},", STANDARD_SPEED.get());
+                    log.info("标准受铁速度:{},", STANDARD_SPEED);
                     log.info("1号铁口出铁状态:{}", ironLoading1.get());
                     log.info("2号铁口出铁状态:{}", ironLoading2.get());
                     log.info("3号铁口出铁状态:{}", ironLoading3.get());
                     log.info("4号铁口出铁状态:{}", ironLoading4.get());
 
                     if (result1) {
-                        //流速过大可能是由于铁口深度不足或发生跑大流问题,则提示将当前铁口堵口
+                        //1.流速过大可能是由于铁口深度不足或发生跑大流问题,则提示将当前铁口堵口
                         PushData.send2Warn(WarnData.warnClose("流速过快,请将当前铁口堵口", closureAlarmUrl));
                         taskExecutor.submit(() -> {
                             exceptionLogService.add(TExceptionLogCreateValidate.builder().exceptionType("4").exceptionDesc("流速过快,请将当前铁口堵口").build());
@@ -622,7 +662,7 @@ public class DeviceEventListener extends EventListener { //
                     Expression expression2 = mParser.parseExpression(modelExpression2);
                     boolean result2 = expression2.getValue(context, Boolean.class);
                     if (result2) {
-                        //若流速过小,但其它铁口正在出铁,则提示将当前铁口堵口
+                        //2.若流速过小,但其它铁口正在出铁,则提示将当前铁口堵口
                         PushData.send2Warn(WarnData.warnClose("流速过小,请将当前铁口堵口", closureAlarmUrl));
                         taskExecutor.submit(() -> {
                             exceptionLogService.add(TExceptionLogCreateValidate.builder().exceptionType("4").exceptionDesc("流速过小,请将当前铁口堵口").build());
@@ -635,6 +675,7 @@ public class DeviceEventListener extends EventListener { //
                     boolean result3 = expression3.getValue(context, Boolean.class);
 
                     if (result3) {
+                        //3.流速过小且其他铁口均未出铁,请先将其它铁口打开,再进行堵口
                         PushData.send2Warn(WarnData.warnClose("流速过小且其他铁口均未出铁,请先将其它铁口打开,再进行堵口", closureAlarmUrl));
                         taskExecutor.submit(() -> {
                             exceptionLogService.add(TExceptionLogCreateValidate.builder().exceptionType("4").exceptionDesc("流速过小且其他铁口均未出铁,请先将其它铁口打开,再进行堵口").build());
@@ -681,7 +722,7 @@ public class DeviceEventListener extends EventListener { //
             if ("1".equals(scheduleTappingTimeoutWarn.getStatus())) {
                 scheduledTaskManager.addTask(scheduleTappingTimeoutWarn.getName(), scheduleTappingTimeoutWarn.getDelay(), scheduleTappingTimeoutWarn.getPeriod(), TimeUnit.SECONDS, () -> {
                     // log.info("已出铁时间(秒):{},标准出铁时间(秒):{}", seconds, STANDARD_IRON_TIME.get());
-                    if (getIronElapsedMinute() > STANDARD_IRON_TIME.get()) {
+                    if (!RangeUtils.isInRange(STANDARD_IRON_TIME, getIronElapsedMinute())) {
                         PushData.send2Warn(WarnData.warnTappingTimeout("出铁时间超时", tappingTimeoutAlramUrl));
                         taskExecutor.submit(() -> {
                             exceptionLogService.add(TExceptionLogCreateValidate.builder().exceptionType("2").exceptionDesc("出铁时间超过设定时间").build());
@@ -735,6 +776,8 @@ public class DeviceEventListener extends EventListener { //
             mTIronData.setIronWeight(mTotalWeight.doubleValue());
             mTIronData.setIronEndtime(LocalDateUtils.formatDate(new Date()));
             ironDataService.saveOrUpdate(mTIronData);
+            //置空,防止和录入的数据干扰到周期出铁数据
+            mTIronData = null;
         }
 
         synchronized (scheduledTaskManager) {
@@ -879,16 +922,16 @@ public class DeviceEventListener extends EventListener { //
 
                 double tempNow = Double.parseDouble(opcData.getData().toString());
 
-                if (tempMax.get() <= 0 || tempNow > tempMax.get()) {
-                    tempMax.set(tempNow);
+                if (rtTempMax.get() <= 0 || tempNow > rtTempMax.get()) {
+                    rtTempMax.set(tempNow);
                 }
 
-                if (tempMin.get() <= 0 || tempNow < tempMin.get()) {
-                    tempMin.set(tempNow);
+                if (rtTempMin.get() <= 0 || tempNow < rtTempMin.get()) {
+                    rtTempMin.set(tempNow);
                 }
 
                 mContext.setVariable(ExpressionConstants.rtIronTemp, opcData.getData());
-                mContext.setVariable(ExpressionConstants.rtIronTempDiff, tempMax.get() - tempMin.get());
+                mContext.setVariable(ExpressionConstants.rtIronTempDiff, rtTempMax.get() - rtTempMin.get());
 
             } else if (opcData.getPointName().contains(SubscribeTagConstants.TAG_CAR11(opcData.getServerType())) || opcData.getPointName().contains(SubscribeTagConstants.TAG_CAR12(opcData.getServerType()))) {
                 //1TH-1号车受铁速度
@@ -972,7 +1015,7 @@ public class DeviceEventListener extends EventListener { //
                     log.info("准备调用taphole1End()");
                     taphole1End();
                 }
-                
+
                 mContext.setVariable(ExpressionConstants.rtIron01State, currentVal);
             } else if (opcData.getPointName().contains(SubscribeTagConstants.TAG_TAPHOLE2_STATUS(opcData.getServerType()))) {
                 ironLoading2.set(Double.parseDouble(opcData.getData().toString()));
@@ -1010,11 +1053,19 @@ public class DeviceEventListener extends EventListener { //
                     ironWeight12Max = BigDecimal.ZERO;
                     ironWeight12Current = BigDecimal.ZERO;
                 }
-            } else if (opcData.getPointName().contains(SubscribeTagConstants.TAG_FLUSH_STATUS(opcData.getServerType()))) {
+            } else if (opcData.getPointName().contains(SubscribeTagConstants.TAG_CHUZ_STATUS(opcData.getServerType()))) {
                 RealtimeData realtimeData = new RealtimeData();
                 realtimeData.setValue(opcData.getData());
+                realtimeData.setDesc("出渣状态");
+                mRealtimeStatus.put(CHUZ_STATUS, realtimeData);
+            } else if (opcData.getPointName().contains(SubscribeTagConstants.TAG_SZB_STATUS(opcData.getServerType()))
+                || opcData.getPointName().contains(SubscribeTagConstants.TAG_CZF_STATUS(opcData.getServerType()))
+            ) {
+                RealtimeData realtimeData = new RealtimeData();
+                Boolean value = mParser.parseExpression("#szxt999 > 0 and #szb999 > 0").getValue(mContext, Boolean.class);
+                realtimeData.setValue(Boolean.TRUE.equals(value) ?"1":"0");
                 realtimeData.setDesc("冲渣状态");
-                mRealtimeStatus.put(FLUSH_STATUS, realtimeData);
+                mRealtimeStatus.put(CHONGZ_STATUS, realtimeData);
             }
 
         }
@@ -1067,8 +1118,6 @@ public class DeviceEventListener extends EventListener { //
         mRealtimeData.put(IRON_WEIGHT, ironWeight);
         mContext.setVariable(ExpressionConstants.rtIronWeight, mTotalWeight.doubleValue());
 
-        //log.info("实时数据:{}", mRealtimeData);
-        //log.info("实时状态:{}", mRealtimeStatus);
         //推送实时数据
         PushData.send2RealtimeData(mRealtimeData);
         //推送实时状态
@@ -1282,25 +1331,25 @@ public class DeviceEventListener extends EventListener { //
             }
 
             for (IronStepVO child : stepDTO.getChilds()) {
-                if (Objects.equals(child.getIdentifier(), CAR_STATUS)) {
+                if (Objects.equals(child.getIdentifier(), YLGC_STATUS)) {
                     RealtimeData realtimeData = new RealtimeData();
                     realtimeData.setValue(child.getPassResult());
                     realtimeData.setDesc("鱼雷罐车到位状态");
-                    mRealtimeStatus.put(CAR_STATUS, realtimeData);
+                    mRealtimeStatus.put(YLGC_STATUS, realtimeData);
                 }
 
-                if (Objects.equals(child.getIdentifier(), GUN_STATUS)) {
-                    RealtimeData realtimeData = new RealtimeData();
-                    realtimeData.setValue(child.getPassResult());
-                    realtimeData.setDesc("拔炮状态");
-                    mRealtimeStatus.put(GUN_STATUS, realtimeData);
-                }
-                if (Objects.equals(child.getIdentifier(), MOUTH_STATUS)) {
-                    RealtimeData realtimeData = new RealtimeData();
-                    realtimeData.setValue(child.getPassResult());
-                    realtimeData.setDesc("摆动溜嘴状态");
-                    mRealtimeStatus.put(MOUTH_STATUS, realtimeData);
-                }
+//                if (Objects.equals(child.getIdentifier(), NPJ_STATUS)) {
+//                    RealtimeData realtimeData = new RealtimeData();
+//                    realtimeData.setValue(child.getPassResult());
+//                    realtimeData.setDesc("拔炮状态");
+//                    mRealtimeStatus.put(NPJ_STATUS, realtimeData);
+//                }
+//                if (Objects.equals(child.getIdentifier(), BDLZ_STATUS)) {
+//                    RealtimeData realtimeData = new RealtimeData();
+//                    realtimeData.setValue(child.getPassResult());
+//                    realtimeData.setDesc("摆动溜嘴状态");
+//                    mRealtimeStatus.put(BDLZ_STATUS, realtimeData);
+//                }
                 PushData.send2RealtimeStatus(mRealtimeStatus);
             }
         }

+ 23 - 0
taphole-iron/src/main/java/com/sckj/iron/util/RangeUtils.java

@@ -0,0 +1,23 @@
+package com.sckj.iron.util;
+
+import static org.apache.commons.lang3.StringUtils.isNumeric;
+
+public class RangeUtils {
+    public static boolean isInRange(String range, Object value) {
+        double numericValue;
+
+        if (value instanceof String && isNumeric((String) value)) {
+            numericValue = Double.parseDouble((String) value);
+        } else if (value instanceof Number) {
+            numericValue = ((Number) value).doubleValue();
+        } else {
+            throw new IllegalArgumentException("Unsupported value type");
+        }
+
+        String[] parts = range.split("-");
+        double min = Double.parseDouble(parts[0]);
+        double max = Double.parseDouble(parts[1]);
+
+        return numericValue >= min && numericValue <= max;
+    }
+}

+ 23 - 18
taphole-opc/src/main/java/com/sckj/opc/controller/THdTagController.java

@@ -37,7 +37,7 @@ public class THdTagController {
     HDServiceImpl hdService;
 
     @GetMapping("/list")
-    @ApiOperation(value = "TAG标记列表")
+    @ApiOperation(value = "标记列表")
     public AjaxResult<PageResult<THdTagListedVo>> list(@Validated PageValidate pageValidate,
                                                        @Validated THdTagSearchValidate searchValidate) {
         PageResult<THdTagListedVo> list = iTHdTagService.list(pageValidate, searchValidate);
@@ -45,7 +45,7 @@ public class THdTagController {
     }
 
     @GetMapping("/detail")
-    @ApiOperation(value = "TAG标记详情")
+    @ApiOperation(value = "标记详情")
     public AjaxResult<THdTagDetailVo> detail(@Validated @RequestParam("id") Long id) {
         THdTagDetailVo detail = iTHdTagService.detail(id);
         return AjaxResult.success(detail);
@@ -53,7 +53,7 @@ public class THdTagController {
 
     @Log(title = "TAG标记新增")
     @PostMapping("/add")
-    @ApiOperation(value = "IHD标记新增")
+    @ApiOperation(value = "标记新增")
     public AjaxResult<Object> add(@Validated @RequestBody THdTagCreateValidate createValidate) {
         iTHdTagService.add(createValidate);
         return AjaxResult.success();
@@ -61,15 +61,20 @@ public class THdTagController {
 
     @Log(title = "TAG标记编辑")
     @PostMapping("/edit")
-    @ApiOperation(value = "IHD标记编辑")
+    @ApiOperation(value = "标记编辑")
     public AjaxResult<Object> edit(@Validated @RequestBody THdTagUpdateValidate updateValidate) {
-        iTHdTagService.edit(updateValidate);
+        THdTag tag =  iTHdTagService.edit(updateValidate);
+        // 新增:如果已订阅,先取消再重新订阅
+        if (hdService.isSubscribed(tag.getTagName())) {
+            hdService.unsubscribe(tag);
+            hdService.subscribe(tag);
+        }
         return AjaxResult.success();
     }
 
     @Log(title = "TAG标记删除")
     @PostMapping("/del")
-    @ApiOperation(value = "IHD标记删除")
+    @ApiOperation(value = "标记删除")
     public AjaxResult<Object> del(@Validated @RequestBody LongIdValidate idValidate) {
         iTHdTagService.del(idValidate.getId());
         return AjaxResult.success();
@@ -118,7 +123,7 @@ public class THdTagController {
     /**
      * @return
      * @MethodName: subscribe
-     * @Description: 订阅所有可用订阅点
+     * @Description: 取消订阅可用订阅点
      */
     @PostMapping("/unsubscribeAvailable")
     @ResponseBody
@@ -144,7 +149,7 @@ public class THdTagController {
     @ResponseBody
     @NotLogin
     @ApiOperation(value = "订阅")
-    public AjaxResult subscribe(THdTag hdTag) {
+    public AjaxResult subscribe( @RequestBody THdTag hdTag) {
         try {
             hdService.subscribe(hdTag);
             return AjaxResult.success();
@@ -163,7 +168,7 @@ public class THdTagController {
     @ResponseBody
     @NotLogin
     @ApiOperation(value = "取消订阅")
-    public AjaxResult unsubscribe(THdTag hdTag) {
+    public AjaxResult unsubscribe(@RequestBody THdTag hdTag) {
         try {
             hdService.unsubscribe(hdTag);
         } catch (Exception e) {
@@ -182,7 +187,7 @@ public class THdTagController {
     @PostMapping("/readNodeValue")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "读取节点数据")
+    @ApiOperation(value = "IHD读取节点数据")
     public AjaxResult readNodeValue(THdTag hdTag) {
         try {
             return AjaxResult.success(ErrorEnum.SUCCESS.getCode(), ErrorEnum.SUCCESS.getMsg(), hdService.readTagValue(hdTag));
@@ -200,7 +205,7 @@ public class THdTagController {
     @PostMapping("/refreshTagIds")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "根据tagName刷新TagId")
+    @ApiOperation(value = "IHD根据tagName刷新TagId")
     public AjaxResult refreshTagIds() {
         try {
             hdService.refreshTagIds();
@@ -219,7 +224,7 @@ public class THdTagController {
     @PostMapping("/getBasicTagByName")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "根据Tag名称获取普通类型Tag对象")
+    @ApiOperation(value = "IHD根据Tag名称获取普通类型Tag对象")
     public AjaxResult getBasicTagByName(THdTag hdTag) {
         try {
             return AjaxResult.success(hdService.getBasicTagByName(hdTag));
@@ -237,7 +242,7 @@ public class THdTagController {
     @PostMapping("/getRawRecordsCount")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "查询时间段内的记录总数")
+    @ApiOperation(value = "IHD查询时间段内的记录总数")
     public AjaxResult getRawRecordsCount(int tagID, String startTime, String endTime) {
         try {
             return AjaxResult.success(ErrorEnum.SUCCESS.getCode(), ErrorEnum.SUCCESS.getMsg(),hdService.getRawRecordsCount(tagID, startTime, endTime));
@@ -256,7 +261,7 @@ public class THdTagController {
     @PostMapping("/queryTagRawRecordsByCount")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "按照记录条数和起始时间查询Tag点原始记录")
+    @ApiOperation(value = "IHD按照记录条数和起始时间查询Tag点原始记录")
     public AjaxResult queryTagRawRecordsByCount(int tagID, String startTime, boolean backward, int recordCount) {
         try {
             return AjaxResult.success(hdService.queryTagRawRecordsByCount(tagID, startTime, backward, recordCount));
@@ -274,7 +279,7 @@ public class THdTagController {
     @PostMapping("/queryTagHisRawRecords")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "查询Tag点在某时间段内的原始记录")
+    @ApiOperation(value = "IHD查询Tag点在某时间段内的原始记录")
     public AjaxResult queryTagHisRawRecords(int tagID, String startTime, String endTime) {
         try {
             return AjaxResult.success(hdService.queryTagHisRawRecords(tagID, startTime, endTime));
@@ -291,7 +296,7 @@ public class THdTagController {
     @PostMapping("/queryTagHisInterRecordsByMode")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "根据不同插值类型,查询Tag点的历史插值记录,查询结果按时间倒序返回")
+    @ApiOperation(value = "IHD根据不同插值类型,查询Tag点的历史插值记录,查询结果按时间倒序返回")
     public AjaxResult queryTagHisInterRecordsByMode(int tagID, String startTime, String endTime, long seconds,@RequestParam("mode") HDInterpolationMode mode) {
         HDErrcode errCode = new HDErrcode();
         try {
@@ -310,7 +315,7 @@ public class THdTagController {
     @PostMapping("/queryTagHisInterRecordsByModeAscending")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "以不同插值算法,查询Tag点的历史插值记录,查询结果按时间正序返回")
+    @ApiOperation(value = "IHD以不同插值算法,查询Tag点的历史插值记录,查询结果按时间正序返回")
     public AjaxResult queryTagHisInterRecordsByModeAscending(int tagID, String startTime, String endTime, long seconds,@RequestParam("mode")  HDInterpolationMode mode) {
         HDErrcode errCode = new HDErrcode();
         try {
@@ -329,7 +334,7 @@ public class THdTagController {
     @PostMapping("/queryTagHisInterRecords")
     @ResponseBody
     @NotLogin
-    @ApiOperation(value = "查询Tag点的历史插值记录")
+    @ApiOperation(value = "IHD查询Tag点的历史插值记录")
     public AjaxResult queryTagHisInterRecords(int tagID, String startTime, String endTime, long seconds) {
         HDErrcode errCode = new HDErrcode();
         try {

+ 96 - 9
taphole-opc/src/main/java/com/sckj/opc/dataservice/HDServiceImpl.java

@@ -29,6 +29,9 @@ import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
 
 import javax.annotation.PreDestroy;
 import javax.annotation.Resource;
@@ -42,6 +45,7 @@ import java.util.concurrent.TimeoutException;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.regex.Pattern;
 
 @Service
 @Slf4j
@@ -265,7 +269,7 @@ public class HDServiceImpl {
         });
     }
 
-    private void createSubscription(OPCServer opcServer, THdTag hdTag) {
+    private void createSubscription(OPCServer opcServer,final THdTag hdTag) {
         if (ObjectUtils.isEmpty(opcServer) || ObjectUtils.isEmpty(hdTag)) {
             throw new OperateException("未获取到服务器或标记信息");
         }
@@ -273,7 +277,8 @@ public class HDServiceImpl {
             log.warn("subscription point => tagId:{},tagName{},period{}", hdTag.getTagId(), hdTag.getTagName(), hdTag.getPeriod());
             throw new OperateException("tagId 、tagName、period不能为空");
         }
-
+        log.info("{}已订阅", hdTag.getTagName());
+        taskNameSet.add(hdTag.getTagName());
         scheduledTaskManager.addTask(hdTag.getTagName(), 0, hdTag.getPeriod(), TimeUnit.SECONDS, () -> {
             Object data = null;
             try {
@@ -303,11 +308,86 @@ public class HDServiceImpl {
                         }
                     }
 
+                    // 判断data是否在有效范围内(支持区间、枚举、比较符号、正则、复杂逻辑等多种格式)
+                    String dataRange = hdTag.getDataRangeExp();
+                    boolean valid = true;
+                    if (ObjectUtils.isNotEmpty(dataRange) && !dataRange.trim().isEmpty() && ObjectUtils.isNotEmpty(data)) {
+                        String valueStr = data.toString();
+                        String[] conditions = dataRange.split(",");
+                        for (String cond : conditions) {
+                            cond = cond.trim();
+                            if (cond.isEmpty()) continue;
+                            boolean condValid = true;
+                            try {
+                                if (cond.startsWith("regex:")) {
+                                    // 正则表达式判断
+                                    String regex = cond.substring(6);
+                                    condValid = Pattern.matches(regex, valueStr);
+                                } else if (cond.contains("-")) {
+                                    // 区间判断
+                                    String[] parts = cond.split("-");
+                                    if (parts.length == 2) {
+                                        double min = Double.parseDouble(parts[0].trim());
+                                        double max = Double.parseDouble(parts[1].trim());
+                                        double val = Double.parseDouble(valueStr);
+                                        condValid = val >= min && val <= max;
+                                    } else {
+                                        condValid = false;
+                                    }
+                                } else if (cond.startsWith(">=")) {
+                                    double min = Double.parseDouble(cond.substring(2).trim());
+                                    double val = Double.parseDouble(valueStr);
+                                    condValid = val >= min;
+                                } else if (cond.startsWith("<=")) {
+                                    double max = Double.parseDouble(cond.substring(2).trim());
+                                    double val = Double.parseDouble(valueStr);
+                                    condValid = val <= max;
+                                } else if (cond.startsWith(">")) {
+                                    double min = Double.parseDouble(cond.substring(1).trim());
+                                    double val = Double.parseDouble(valueStr);
+                                    condValid = val > min;
+                                } else if (cond.startsWith("<")) {
+                                    double max = Double.parseDouble(cond.substring(1).trim());
+                                    double val = Double.parseDouble(valueStr);
+                                    condValid = val < max;
+                                } else if (cond.startsWith("!=")) {
+                                    double notVal = Double.parseDouble(cond.substring(2).trim());
+                                    double val = Double.parseDouble(valueStr);
+                                    condValid = val != notVal;
+                                } else if (cond.contains("val") || cond.contains("&&") || cond.contains("||")) {
+                                    // 复杂逻辑表达式,使用Spring SpEL
+                                    try {
+                                        StandardEvaluationContext context = new StandardEvaluationContext();
+                                        context.setVariable("val", data instanceof Number ? ((Number)data).doubleValue() : data);
+                                        ExpressionParser parser = new SpelExpressionParser();
+                                        condValid = parser.parseExpression(cond).getValue(context, Boolean.class);
+                                    } catch (Exception e) {
+                                        condValid = false;
+                                    }
+                                } else {
+                                    // 枚举判断(字符串或数字)
+                                    condValid = cond.equals(valueStr);
+                                }
+                            } catch (Exception e) {
+                                condValid = false;
+                            }
+                            if (!condValid) {
+                                valid = false;
+                                break;
+                            }
+                        }
+                    }
+                    if (!valid) {
+                        log.info("{}({})数据{}不在有效范围{}内,不推送", hdTag.getTagName(), hdTag.getTagDesc(), data, dataRange);
+                        return;
+                    }
+
                     // 获取上一次的数据值
                     Object lastData = lastDataMap.get(hdTag.getTagName());
 
-                    // 只有当数据发生变化时才推送
-                    if (!ObjectUtils.equals(data, lastData)) {
+                    // 判断是否推送数据(1-允许重复,0-不允许重复)
+                    boolean allowDuplicate = "1".equals(hdTag.getAllowDuplicate());
+                    if (allowDuplicate || !ObjectUtils.equals(data, lastData)) {
                         OPCData build = OPCData.builder()
                                 .data(data)
                                 .pointName(hdTag.getTagName())
@@ -319,9 +399,11 @@ public class HDServiceImpl {
                                 .identifier(hdTag.getIdentifier())
                                 .build();
                         asyncEventBus.post(build);
-                        // 更新缓存中的数据值
-                        lastDataMap.put(hdTag.getTagName(), data);
-                        log.info("{}({})数据更新:{}", hdTag.getTagName(), hdTag.getTagDesc(), record.getValueStr());
+                        // 仅在不允许重复时更新缓存
+                        if (!allowDuplicate) {
+                            lastDataMap.put(hdTag.getTagName(), data);
+                        }
+                        log.info("{}({})数据推送:{}", hdTag.getTagName(), hdTag.getTagDesc(), record.getValueStr());
                     }
                 }
             } catch (Exception e) {
@@ -356,6 +438,7 @@ public class HDServiceImpl {
     public void unsubscribe(THdTag tHdTag) {
         taskNameSet.remove(tHdTag.getTagName());
         scheduledTaskManager.cancelTask(tHdTag.getTagName());
+        log.info("{}取消订阅", tHdTag.getTagName());
     }
 
     /***
@@ -384,9 +467,9 @@ public class HDServiceImpl {
             List<THdTag> opcPointList = hdTagService.list(pointQueryWrapper);
             if (ObjectUtils.isNotEmpty(opcPointList)) {
                 log.info("start point list:");
-                for (THdTag opcPoint : opcPointList) {
+                for (THdTag hdTag : opcPointList) {
                     // log.info(opcPoint.getTagName());
-                    createSubscription(opcServer, opcPoint);
+                    createSubscription(opcServer, hdTag);
                 }
             }
         }
@@ -832,6 +915,10 @@ public class HDServiceImpl {
         }
     }
 
+    // 判断tagName是否已被订阅
+    public boolean isSubscribed(String tagName) {
+        return taskNameSet.contains(tagName);
+    }
 
     @PreDestroy
     public void releaseServer() {

+ 6 - 0
taphole-opc/src/main/java/com/sckj/opc/entity/THdTag.java

@@ -69,4 +69,10 @@ public class THdTag implements Serializable {
     @ApiModelProperty(value = "唯一名称")
     private String identifier;
 
+    @ApiModelProperty(value = "是否允许推送重复数据(0-否,1-是)")
+    private String allowDuplicate;
+
+    @ApiModelProperty(value = "数据有效性范围(支持区间:10-100,枚举:A,B,C,比较符号:>10,<100,>=5,<=20,!=0,正则:regex:^[A-Z]{2}\\d+$,复杂逻辑:val>10&&val<100||val==200 等格式)")
+    private String dataRangeExp;
+
 }

+ 6 - 1
taphole-opc/src/main/java/com/sckj/opc/service/THdTagServiceImpl.java

@@ -119,7 +119,7 @@ public class THdTagServiceImpl  extends ServiceImpl<THdTagMapper, THdTag> {
      * @param updateValidate 参数
      */
     
-    public void edit(THdTagUpdateValidate updateValidate) {
+    public THdTag edit(THdTagUpdateValidate updateValidate) {
         THdTag model = tHdTagMapper.selectOne(
                 new QueryWrapper<THdTag>()
                     .eq("id",  updateValidate.getId())
@@ -135,7 +135,12 @@ public class THdTagServiceImpl  extends ServiceImpl<THdTagMapper, THdTag> {
         model.setTagType(updateValidate.getTagType());
         model.setTagDesc(updateValidate.getTagDesc());
         model.setRemark(updateValidate.getRemark());
+        model.setDigits(updateValidate.getDigits());
+        model.setAllowDuplicate(updateValidate.getAllowDuplicate());
+        model.setIdentifier(updateValidate.getIdentifier());
+        model.setDataRangeExp(updateValidate.getDataRangeExp());
         tHdTagMapper.updateById(model);
+        return model;
     }
 
     /**

+ 12 - 0
taphole-opc/src/main/java/com/sckj/opc/validate/THdTagCreateValidate.java

@@ -36,4 +36,16 @@ public class THdTagCreateValidate implements Serializable {
     @ApiModelProperty(value = "备注")
     private String remark;
 
+    @ApiModelProperty(value = "小数位")
+    private Integer digits;
+
+    @ApiModelProperty(value = "唯一名称")
+    private String identifier;
+
+    @ApiModelProperty(value = "是否允许推送重复数据(0-否,1-是)")
+    private String allowDuplicate;
+
+    @ApiModelProperty(value = "数据有效性范围(支持区间:10-100,枚举:A,B,C,比较符号:>10,<100,>=5,<=20,!=0,正则:regex:^[A-Z]{2}\\d+$,复杂逻辑:val>10&&val<100||val==200 等格式)")
+    private String dataRangeExp;
+
 }

+ 12 - 0
taphole-opc/src/main/java/com/sckj/opc/validate/THdTagSearchValidate.java

@@ -36,4 +36,16 @@ public class THdTagSearchValidate implements Serializable {
     @ApiModelProperty(value = "备注")
     private String remark;
 
+    @ApiModelProperty(value = "小数位")
+    private Integer digits;
+
+    @ApiModelProperty(value = "唯一名称")
+    private String identifier;
+
+    @ApiModelProperty(value = "是否允许推送重复数据(0-否,1-是)")
+    private String allowDuplicate;
+
+    @ApiModelProperty(value = "数据有效性范围(支持区间:10-100,枚举:A,B,C,比较符号:>10,<100,>=5,<=20,!=0,正则:regex:^[A-Z]{2}\\d+$,复杂逻辑:val>10&&val<100||val==200 等格式)")
+    private String dataRangeExp;
+
 }

+ 12 - 0
taphole-opc/src/main/java/com/sckj/opc/validate/THdTagUpdateValidate.java

@@ -43,4 +43,16 @@ public class THdTagUpdateValidate implements Serializable {
     @ApiModelProperty(value = "备注")
     private String remark;
 
+    @ApiModelProperty(value = "小数位")
+    private Integer digits;
+
+    @ApiModelProperty(value = "唯一名称")
+    private String identifier;
+
+    @ApiModelProperty(value = "是否允许推送重复数据(0-否,1-是)")
+    private String allowDuplicate;
+
+    @ApiModelProperty(value = "数据有效性范围(支持区间:10-100,枚举:A,B,C,比较符号:>10,<100,>=5,<=20,!=0,正则:regex:^[A-Z]{2}\\d+$,复杂逻辑:val>10&&val<100||val==200 等格式)")
+    private String dataRangeExp;
+
 }

+ 10 - 2
taphole-opc/src/main/java/com/sckj/opc/vo/THdTagDetailVo.java

@@ -5,8 +5,6 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
-import java.util.Date;
-import java.util.Date;
 
 @Data
 @ApiModel("IHD标记详情Vo")
@@ -41,5 +39,15 @@ public class THdTagDetailVo implements Serializable {
     @ApiModelProperty(value = "备注")
     private String remark;
 
+    @ApiModelProperty(value = "小数位")
+    private Integer digits;
 
+    @ApiModelProperty(value = "唯一名称")
+    private String identifier;
+
+    @ApiModelProperty(value = "是否允许推送重复数据(0-否,1-是)")
+    private String allowDuplicate;
+
+    @ApiModelProperty(value = "数据有效性范围(支持区间:10-100,枚举:A,B,C,比较符号:>10,<100,>=5,<=20,!=0,正则:regex:^[A-Z]{2}\\d+$,复杂逻辑:val>10&&val<100||val==200 等格式)")
+    private String dataRangeExp;
 }

+ 10 - 2
taphole-opc/src/main/java/com/sckj/opc/vo/THdTagListedVo.java

@@ -5,8 +5,6 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
-import java.util.Date;
-import java.util.Date;
 
 @Data
 @ApiModel("IHD标记列表Vo")
@@ -41,5 +39,15 @@ public class THdTagListedVo implements Serializable {
     @ApiModelProperty(value = "备注")
     private String remark;
 
+    @ApiModelProperty(value = "小数位")
+    private Integer digits;
 
+    @ApiModelProperty(value = "唯一名称")
+    private String identifier;
+
+    @ApiModelProperty(value = "是否允许推送重复数据(0-否,1-是)")
+    private String allowDuplicate;
+
+    @ApiModelProperty(value = "数据有效性范围(支持区间:10-100,枚举:A,B,C,比较符号:>10,<100,>=5,<=20,!=0,正则:regex:^[A-Z]{2}\\d+$,复杂逻辑:val>10&&val<100||val==200 等格式)")
+    private String dataRangeExp;
 }