|
|
@@ -1,97 +1,309 @@
|
|
|
package com.dcs.hnyz.service.impl;
|
|
|
|
|
|
import java.util.Arrays;
|
|
|
+import java.util.Collections;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.Executors;
|
|
|
+import java.util.concurrent.ScheduledExecutorService;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
import com.dcs.common.utils.DateUtils;
|
|
|
+import com.dcs.equipment.task.ModbusTcpTask;
|
|
|
+import com.dcs.equipment.utils.ModbusUtil;
|
|
|
+import com.dcs.hnyz.domain.Equipment;
|
|
|
+import com.dcs.hnyz.domain.bo.CachedAction;
|
|
|
+import com.dcs.hnyz.domain.condition.Condition;
|
|
|
+import com.dcs.hnyz.domain.condition.ConditionGroup;
|
|
|
+import com.dcs.hnyz.domain.condition.ConditionItem;
|
|
|
+import com.dcs.hnyz.domain.vo.EquipmentParamFormVo;
|
|
|
+import com.dcs.hnyz.enums.ActionTypeEnum;
|
|
|
+import com.dcs.hnyz.enums.DeviceTypeEnum;
|
|
|
+import com.dcs.hnyz.enums.OperationType;
|
|
|
+import com.dcs.hnyz.enums.TriggerTypeEnum;
|
|
|
+import com.dcs.hnyz.service.IActionBindingService;
|
|
|
+import com.dcs.hnyz.service.IEquipmentParamService;
|
|
|
+import com.dcs.hnyz.service.IEquipmentService;
|
|
|
+import com.dcs.hnyz.utils.ActionCacheBuilder;
|
|
|
+import com.dcs.hnyz.utils.RegisterCodeMapBuilder;
|
|
|
+import com.fasterxml.jackson.databind.JsonNode;
|
|
|
+import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
|
+import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
|
|
|
+import com.intelligt.modbus.jlibmodbus.exception.ModbusNumberException;
|
|
|
+import com.intelligt.modbus.jlibmodbus.exception.ModbusProtocolException;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import com.dcs.hnyz.mapper.ActionConfigMapper;
|
|
|
import com.dcs.hnyz.domain.ActionConfig;
|
|
|
import com.dcs.hnyz.service.IActionConfigService;
|
|
|
|
|
|
+import javax.annotation.PreDestroy;
|
|
|
+import javax.annotation.Resource;
|
|
|
+
|
|
|
+import static com.dcs.framework.datasource.DynamicDataSourceContextHolder.log;
|
|
|
+
|
|
|
/**
|
|
|
* 动作配置管理Service业务层处理
|
|
|
- *
|
|
|
+ *
|
|
|
* @author hmy
|
|
|
* @date 2025-04-28
|
|
|
*/
|
|
|
@Service
|
|
|
-public class ActionConfigServiceImpl implements IActionConfigService
|
|
|
-{
|
|
|
- @Autowired
|
|
|
+public class ActionConfigServiceImpl implements IActionConfigService {
|
|
|
+ @Resource
|
|
|
private ActionConfigMapper actionConfigMapper;
|
|
|
+ @Autowired
|
|
|
+ private ActionCacheBuilder actionCacheBuilder;
|
|
|
+ @Autowired
|
|
|
+ private RegisterCodeMapBuilder registerCodeMapBuilder;
|
|
|
+ @Autowired
|
|
|
+ private IEquipmentParamService equipmentParamService;
|
|
|
+ @Autowired
|
|
|
+ private IActionBindingService actionBindingService;
|
|
|
+
|
|
|
+ //共享线程池
|
|
|
+ private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* 查询动作配置管理
|
|
|
- *
|
|
|
+ *
|
|
|
* @param actionId 动作配置管理主键
|
|
|
* @return 动作配置管理
|
|
|
*/
|
|
|
@Override
|
|
|
- public ActionConfig selectActionConfigByActionId(Long actionId)
|
|
|
- {
|
|
|
+ public ActionConfig selectActionConfigByActionId(Long actionId) {
|
|
|
return actionConfigMapper.selectById(actionId);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 查询动作配置管理列表
|
|
|
- *
|
|
|
+ *
|
|
|
* @param actionConfig 动作配置管理
|
|
|
* @return 动作配置管理
|
|
|
*/
|
|
|
@Override
|
|
|
- public List<ActionConfig> selectActionConfigList(ActionConfig actionConfig)
|
|
|
- {
|
|
|
+ public List<ActionConfig> selectActionConfigList(ActionConfig actionConfig) {
|
|
|
return actionConfigMapper.selectActionConfigList(actionConfig);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 新增动作配置管理
|
|
|
- *
|
|
|
+ *
|
|
|
* @param actionConfig 动作配置管理
|
|
|
* @return 结果
|
|
|
*/
|
|
|
@Override
|
|
|
- public int insertActionConfig(ActionConfig actionConfig)
|
|
|
- {
|
|
|
+ public int insertActionConfig(ActionConfig actionConfig) {
|
|
|
actionConfig.setCreateTime(DateUtils.getNowDate());
|
|
|
return actionConfigMapper.insert(actionConfig);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 修改动作配置管理
|
|
|
- *
|
|
|
+ *
|
|
|
* @param actionConfig 动作配置管理
|
|
|
* @return 结果
|
|
|
*/
|
|
|
@Override
|
|
|
- public int updateActionConfig(ActionConfig actionConfig)
|
|
|
- {
|
|
|
+ public int updateActionConfig(ActionConfig actionConfig) {
|
|
|
actionConfig.setUpdateTime(DateUtils.getNowDate());
|
|
|
return actionConfigMapper.updateById(actionConfig);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 批量删除动作配置管理
|
|
|
- *
|
|
|
+ *
|
|
|
* @param actionIds 需要删除的动作配置管理主键
|
|
|
* @return 结果
|
|
|
*/
|
|
|
@Override
|
|
|
- public int deleteActionConfigByActionIds(Long[] actionIds)
|
|
|
- {
|
|
|
+ public int deleteActionConfigByActionIds(Long[] actionIds) {
|
|
|
return actionConfigMapper.deleteBatchIds(Arrays.asList(actionIds));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 删除动作配置管理信息
|
|
|
- *
|
|
|
+ *
|
|
|
* @param actionId 动作配置管理主键
|
|
|
* @return 结果
|
|
|
*/
|
|
|
@Override
|
|
|
- public int deleteActionConfigByActionId(Long actionId)
|
|
|
- {
|
|
|
+ public int deleteActionConfigByActionId(Long actionId) {
|
|
|
return actionConfigMapper.deleteById(actionId);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 递归判断条件是否满足(核心逻辑)
|
|
|
+ * @param condition 条件
|
|
|
+ * @param deviceMap 设备ID到寄存器code的映射
|
|
|
+ * @param plcData 实时数据
|
|
|
+ * @return 是否满足条件
|
|
|
+ */
|
|
|
+ public boolean evaluate(Condition condition,
|
|
|
+ Map<Integer, String> deviceMap,
|
|
|
+ Map<String, EquipmentParamFormVo> plcData) {
|
|
|
+ if (condition instanceof ConditionGroup) {//判断是否为组合条件
|
|
|
+ ConditionGroup group = (ConditionGroup) condition;
|
|
|
+ boolean result = "AND".equalsIgnoreCase(group.getOperator());
|
|
|
+ for (Condition child : group.getConditions()) {//递归判断子条件
|
|
|
+ boolean childResult = evaluate(child, deviceMap, plcData);
|
|
|
+ if ("AND".equalsIgnoreCase(group.getOperator())) {
|
|
|
+ result = result && childResult;
|
|
|
+ if (!result) break; // 短路优化
|
|
|
+ } else {
|
|
|
+ result = result || childResult;
|
|
|
+ if (result) break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+ } else if (condition instanceof ConditionItem) {// 判断是否为叶子条件
|
|
|
+ ConditionItem item = (ConditionItem) condition;
|
|
|
+ int deviceId = item.getDevice();
|
|
|
+ String operator = item.getOperator();
|
|
|
+ String expectedValue = item.getValue();
|
|
|
+
|
|
|
+ String code = deviceMap.get(deviceId);
|
|
|
+ if (code == null) return false;
|
|
|
+
|
|
|
+ EquipmentParamFormVo param = plcData.get(code);
|
|
|
+ if (param == null || param.getValue() == null) return false;
|
|
|
+
|
|
|
+ String actualValue = param.getValue().toString();//实际值
|
|
|
+ //判断条件是否满足
|
|
|
+ try {
|
|
|
+ OperationType op = OperationType.fromCode(operator);
|
|
|
+ return op.apply(actualValue, expectedValue);
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 把将 paramList 处理为 Map<String, EquipmentParamFormVo>
|
|
|
+ */
|
|
|
+ public Map<String, EquipmentParamFormVo> paramListToMap(List<EquipmentParamFormVo> paramList) {
|
|
|
+ return paramList.stream()
|
|
|
+ .filter(p -> p.getCode() != null && p.getValue() != null)
|
|
|
+ .collect(Collectors.toMap(
|
|
|
+ EquipmentParamFormVo::getCode,
|
|
|
+ p -> p,
|
|
|
+ (existing, replacement) -> replacement // 如果有重复 Code,用后者覆盖
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Scheduled(fixedRate = 3000) // 每 3 秒执行一次
|
|
|
+ public void checkActions() {
|
|
|
+ EquipmentParamFormVo param = new EquipmentParamFormVo();
|
|
|
+// param.setCode("D2kxh");
|
|
|
+// param.setValue(true);
|
|
|
+ Map<String, EquipmentParamFormVo> plcData = paramListToMap(Collections.singletonList(param));
|
|
|
+ for (CachedAction action : actionCacheBuilder.getCache().values()) {
|
|
|
+ ActionConfig actionConfig = action.getActionConfig();//动作配置
|
|
|
+ //只处理条件触发事件
|
|
|
+ if (!TriggerTypeEnum.CONDITION_TRIGGER.getCode().equals(actionConfig.getTriggerType())) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ //对于已触发的事件不在重复触发
|
|
|
+ if (action.isTrigger()) continue;
|
|
|
+// boolean match = evaluate(action.getCondition(), registerCodeMapBuilder.getRegisterCodeMap(),paramListToMap(ModbusTcpTask.getBroadCastEquipmentParamFormVOList()));
|
|
|
+ boolean match = evaluate(action.getCondition(), registerCodeMapBuilder.getRegisterCodeMap(), plcData);
|
|
|
+ //修改缓存内动作触发状态
|
|
|
+ actionCacheBuilder.setTrigger(actionConfig.getActionId(), match);
|
|
|
+ if (match) {
|
|
|
+ log.info("动作命中:ID={}, 缓存={}", actionConfig.getActionId(), action.getCondition());
|
|
|
+ triggerActionDelay(actionConfig);
|
|
|
+ //处理同步触发事件
|
|
|
+ List<Long> longs = actionBindingService.selectBindActionIdsByActionId(actionConfig.getActionId());
|
|
|
+ for (Long actionId : longs) {
|
|
|
+ //对应同步事件触发要满足的动作配置
|
|
|
+ CachedAction actionSync = actionCacheBuilder.getCache().get(actionId);
|
|
|
+ //对于已触发的事件不在重复触发
|
|
|
+ if (actionSync.isTrigger()) continue;
|
|
|
+ ActionConfig actionConfigSync=actionSync.getActionConfig();
|
|
|
+ Condition condition = actionSync.getCondition();//动作条件
|
|
|
+ boolean matchSync = evaluate(condition, registerCodeMapBuilder.getRegisterCodeMap(), plcData);
|
|
|
+ if (matchSync) {
|
|
|
+ log.info("动作命中:ID={}, 缓存={}", actionConfigSync.getActionId(), actionSync.getCondition());
|
|
|
+ triggerActionDelay(actionConfigSync);
|
|
|
+ }
|
|
|
+ actionCacheBuilder.setTrigger(actionConfigSync.getActionId(), matchSync);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 延时处理
|
|
|
+ * @param actionConfig 动作配置
|
|
|
+ */
|
|
|
+ private void triggerActionDelay(ActionConfig actionConfig) {
|
|
|
+ //延时处理
|
|
|
+ if (actionConfig.getTriggerDelay() > 0) {
|
|
|
+ scheduler.schedule(() -> {
|
|
|
+ log.info("延时动作执行:ID={}", actionConfig.getActionId());
|
|
|
+ executeAction(actionConfig);
|
|
|
+ }, actionConfig.getTriggerDelay(), TimeUnit.SECONDS);
|
|
|
+ } else {
|
|
|
+ log.info("动作执行:ID={}", actionConfig.getActionId());
|
|
|
+ executeAction(actionConfig);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行动作逻辑
|
|
|
+ * @param actionConfig 动作配置
|
|
|
+ */
|
|
|
+ public void executeAction(ActionConfig actionConfig) {
|
|
|
+ //动作执行对应寄存器的code
|
|
|
+ Integer address = equipmentParamService.getSetCodeByParentId(actionConfig.getEquipmentId());
|
|
|
+ if (address == null) {
|
|
|
+ log.info("动作执行对应寄存器的code为空");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //动作类型
|
|
|
+ ActionTypeEnum type;
|
|
|
+ try {
|
|
|
+ type = ActionTypeEnum.valueOf(actionConfig.getActionType());
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ log.warn("未知动作类型:{}", actionConfig.getActionType());
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ switch (type) {
|
|
|
+ case SET_FLOAT_VALUE:
|
|
|
+ ModbusUtil.setRegisterFloatValue(1, address, Float.parseFloat(actionConfig.getValue()));
|
|
|
+ break;
|
|
|
+ case ON:
|
|
|
+// System.out.println("on" + address);
|
|
|
+ ModbusUtil.setCoilValue(1, address, true);
|
|
|
+ break;
|
|
|
+ case OFF:
|
|
|
+// System.out.println("off" + address);
|
|
|
+ ModbusUtil.setCoilValue(1, address, false);
|
|
|
+ break;
|
|
|
+ case SET_INT_VALUE:
|
|
|
+ ModbusUtil.setRegisterValue(1, address, Integer.parseInt(actionConfig.getValue()));
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ log.warn("未处理的动作类型:{}", type);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("执行动作失败,类型={},寄存器地址={},值={},异常信息:{}",
|
|
|
+ type, address, actionConfig.getValue(), e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @PreDestroy
|
|
|
+ public void shutdownScheduler() {
|
|
|
+ scheduler.shutdown();
|
|
|
+ }
|
|
|
+
|
|
|
}
|