20251229:设备上电触发任务修改
This commit is contained in:
parent
9ebb0d0986
commit
b35a7b8162
@ -141,7 +141,7 @@ sip:
|
|||||||
# 日志配置
|
# 日志配置
|
||||||
logging:
|
logging:
|
||||||
level:
|
level:
|
||||||
com.fastbee: debug
|
com.fastbee: info
|
||||||
com.yomahub: warn
|
com.yomahub: warn
|
||||||
org.dromara: warn
|
org.dromara: warn
|
||||||
org.springframework: warn
|
org.springframework: warn
|
||||||
|
|||||||
@ -287,6 +287,11 @@ public interface FastBeeConstant {
|
|||||||
*/
|
*/
|
||||||
String POWER_ON_TRIGGER_PENDING = "power:on:trigger:pending:";
|
String POWER_ON_TRIGGER_PENDING = "power:on:trigger:pending:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备上电触发任务ID列表缓存(用于避免重复查询数据库)
|
||||||
|
*/
|
||||||
|
String POWER_ON_TRIGGER_JOB_IDS = "power:on:trigger:jobIds:";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PROTOCOL {
|
interface PROTOCOL {
|
||||||
|
|||||||
@ -160,7 +160,7 @@ public class RedisKeyBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备上电触发指令缓存key
|
* 设备上电触发指令缓存key
|
||||||
* 用于存储设备上电触发任务下发的指令片段(索引2到11位)
|
* 用于存储设备上电触发任务下发的指令片段(索引2到3位)
|
||||||
*/
|
*/
|
||||||
public static String buildPowerOnTriggerCommandKey(String serialNumber) {
|
public static String buildPowerOnTriggerCommandKey(String serialNumber) {
|
||||||
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_COMMAND + serialNumber.toUpperCase();
|
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_COMMAND + serialNumber.toUpperCase();
|
||||||
@ -174,5 +174,12 @@ public class RedisKeyBuilder {
|
|||||||
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_PENDING + serialNumber.toUpperCase();
|
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_PENDING + serialNumber.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备上电触发任务ID列表缓存key
|
||||||
|
* 用于缓存设备上电触发任务的ID列表,避免重复查询数据库
|
||||||
|
*/
|
||||||
|
public static String buildPowerOnTriggerJobIdsKey(String serialNumber) {
|
||||||
|
return FastBeeConstant.REDIS.POWER_ON_TRIGGER_JOB_IDS + serialNumber.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设备调试错误日志AOP切面
|
* 设备调试错误日志AOP切面
|
||||||
@ -232,7 +233,7 @@ public class DeviceDebugLogAspect {
|
|||||||
* 统一异常处理入口
|
* 统一异常处理入口
|
||||||
*/
|
*/
|
||||||
private void handleException(JoinPoint joinPoint, Throwable e,
|
private void handleException(JoinPoint joinPoint, Throwable e,
|
||||||
java.util.function.Function<Object[], ErrorLogContext> contextExtractor,
|
Function<Object[], ErrorLogContext> contextExtractor,
|
||||||
String errorLogPrefix) {
|
String errorLogPrefix) {
|
||||||
try {
|
try {
|
||||||
Object[] args = joinPoint.getArgs();
|
Object[] args = joinPoint.getArgs();
|
||||||
@ -378,9 +379,11 @@ public class DeviceDebugLogAspect {
|
|||||||
// 构建并保存设备日志
|
// 构建并保存设备日志
|
||||||
DeviceLog deviceLog = buildDeviceLog(device, context);
|
DeviceLog deviceLog = buildDeviceLog(device, context);
|
||||||
logService.saveDeviceLog(deviceLog);
|
logService.saveDeviceLog(deviceLog);
|
||||||
log.info("已保存错误日志到数据库 - 设备: {}, 错误类型: {}, 错误信息: {}",
|
if (log.isDebugEnabled()) {
|
||||||
serialNumber, context.getIdentity(),
|
log.debug("已保存错误日志到数据库 - 设备: {}, 错误类型: {}, 错误信息: {}",
|
||||||
StringUtils.substring(context.getErrorMsg(), 0, MAX_ERROR_MSG_DISPLAY_LENGTH));
|
serialNumber, context.getIdentity(),
|
||||||
|
StringUtils.substring(context.getErrorMsg(), 0, MAX_ERROR_MSG_DISPLAY_LENGTH));
|
||||||
|
}
|
||||||
|
|
||||||
// 推送错误信息到前端页面
|
// 推送错误信息到前端页面
|
||||||
pushErrorToFrontend(serialNumber, productId, context);
|
pushErrorToFrontend(serialNumber, productId, context);
|
||||||
@ -462,8 +465,10 @@ public class DeviceDebugLogAspect {
|
|||||||
|
|
||||||
// 推送消息到MQTT
|
// 推送消息到MQTT
|
||||||
mqttRemoteManager.pushCommon(messageBo);
|
mqttRemoteManager.pushCommon(messageBo);
|
||||||
log.info("已推送错误信息到前端 - 设备: {}, 产品ID: {}, 错误类型: {}",
|
if (log.isDebugEnabled()) {
|
||||||
serialNumber, productId, context.getIdentity());
|
log.debug("已推送错误信息到前端 - 设备: {}, 产品ID: {}, 错误类型: {}",
|
||||||
|
serialNumber, productId, context.getIdentity());
|
||||||
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("推送错误信息到前端失败: {}", ex.getMessage(), ex);
|
log.error("推送错误信息到前端失败: {}", ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,458 @@
|
|||||||
|
package com.fastbee.iot.data.executor;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.fastbee.base.service.ISessionStore;
|
||||||
|
import com.fastbee.base.session.Session;
|
||||||
|
import com.fastbee.common.constant.FastBeeConstant;
|
||||||
|
import com.fastbee.common.core.protocol.Message;
|
||||||
|
import com.fastbee.common.core.redis.RedisCache;
|
||||||
|
import com.fastbee.common.core.redis.RedisKeyBuilder;
|
||||||
|
import com.fastbee.common.enums.ServerType;
|
||||||
|
import com.fastbee.common.enums.TopicType;
|
||||||
|
import com.fastbee.common.utils.DateUtils;
|
||||||
|
import com.fastbee.common.utils.StringUtils;
|
||||||
|
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
|
||||||
|
import com.fastbee.common.utils.gateway.protocol.ByteUtils;
|
||||||
|
import com.fastbee.iot.data.service.impl.MessageManager;
|
||||||
|
import com.fastbee.iot.domain.Device;
|
||||||
|
import com.fastbee.iot.domain.ModbusJob;
|
||||||
|
import com.fastbee.iot.model.DeviceStatusVO;
|
||||||
|
import com.fastbee.iot.service.IDeviceService;
|
||||||
|
import com.fastbee.iot.service.IModbusJobService;
|
||||||
|
import com.fastbee.mqttclient.PubMqttClient;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备上电触发任务执行器
|
||||||
|
* 专门用于执行设备上电触发任务,直接下发指令,不通过队列,确保指令及时准确下发
|
||||||
|
*
|
||||||
|
* @author Sumuxi
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class PowerOnTriggerTaskExecutor {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PubMqttClient pubMqttClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisCache redisCache;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MessageManager messageManager;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private TopicsUtils topicsUtils;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IDeviceService deviceService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ISessionStore sessionStore;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IModbusJobService modbusJobService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记设备需要触发上电任务(等待第一个数据包)
|
||||||
|
* 优化:直接查询 ModbusJob,缓存 taskId 列表,避免后续重复查询数据库
|
||||||
|
* @param device 设备信息
|
||||||
|
*/
|
||||||
|
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
||||||
|
public void markPowerOnTriggerPending(Device device) {
|
||||||
|
try {
|
||||||
|
String serialNumber = device.getSerialNumber().toUpperCase();
|
||||||
|
String taskIdsKey = RedisKeyBuilder.buildPowerOnTriggerJobIdsKey(serialNumber);
|
||||||
|
String pendingKey = RedisKeyBuilder.buildPowerOnTriggerPendingKey(serialNumber);
|
||||||
|
|
||||||
|
// 直接查询该设备的上电触发任务(cycleType=3,即 remark 包含 power_on_trigger)
|
||||||
|
ModbusJob queryJob = new ModbusJob();
|
||||||
|
queryJob.setSubSerialNumber(serialNumber);
|
||||||
|
queryJob.setStatus("0"); // 正常状态
|
||||||
|
queryJob.setCycleType(3); // 上电触发任务
|
||||||
|
|
||||||
|
List<ModbusJob> modbusJobList = modbusJobService.selectModbusJobList(queryJob);
|
||||||
|
if (CollectionUtils.isEmpty(modbusJobList)) {
|
||||||
|
// 清除可能存在的缓存
|
||||||
|
redisCache.deleteObject(taskIdsKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选出上电触发任务,并收集 taskId 列表
|
||||||
|
List<Long> powerOnTriggerTaskIds = new ArrayList<>();
|
||||||
|
for (ModbusJob job : modbusJobList) {
|
||||||
|
if (Objects.equals(job.getCycleType(), 3)) {
|
||||||
|
powerOnTriggerTaskIds.add(job.getTaskId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有上电触发任务,缓存 taskId 列表
|
||||||
|
if (!powerOnTriggerTaskIds.isEmpty()) {
|
||||||
|
// 缓存 taskId 列表,过期时间10分钟
|
||||||
|
redisCache.setCacheObject(taskIdsKey, JSON.toJSONString(powerOnTriggerTaskIds), 600, TimeUnit.SECONDS);
|
||||||
|
// 关键:设置"等待第一个数据包"标记(简单的标记即可,比如 "1")
|
||||||
|
redisCache.setCacheObject(pendingKey, "1", 600, TimeUnit.SECONDS);
|
||||||
|
log.debug("标记设备需要触发上电任务,等待第一个数据包,设备编号:{},任务数量:{}",
|
||||||
|
serialNumber, powerOnTriggerTaskIds.size());
|
||||||
|
} else {
|
||||||
|
// 如果没有上电触发任务,清除可能存在的旧缓存
|
||||||
|
redisCache.deleteObject(taskIdsKey);
|
||||||
|
redisCache.deleteObject(pendingKey);
|
||||||
|
log.debug("清除设备上电触发任务标记,设备编号:{}", serialNumber);
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("标记设备上电触发任务异常,设备编号:{}", device.getSerialNumber(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步检查并触发设备上电任务(在收到第一个数据包时)
|
||||||
|
* 优化:使用 taskId 列表作为标志位,列表为空或不存在表示没有待执行任务
|
||||||
|
* @param serialNumber 设备编号
|
||||||
|
* @param productId 产品ID
|
||||||
|
*/
|
||||||
|
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
||||||
|
public void checkAndTriggerPowerOnTaskAsync(String serialNumber, Long productId) {
|
||||||
|
if (StringUtils.isEmpty(serialNumber)) {
|
||||||
|
log.warn("设备编号为空,无法触发设备上电任务");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String pendingKey = RedisKeyBuilder.buildPowerOnTriggerPendingKey(serialNumber);
|
||||||
|
// 关键:检查是否存在"等待第一个数据包"标记
|
||||||
|
Object pendingFlag = redisCache.getCacheObject(pendingKey);
|
||||||
|
if (pendingFlag == null) {
|
||||||
|
// 标记不存在,说明不是第一个数据包,或者已经处理过了
|
||||||
|
log.debug("设备上电触发任务标记不存在,不是第一个数据包,设备编号:{}", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 补偿查询设备信息(获取产品ID)
|
||||||
|
if (productId == null) {
|
||||||
|
Device device = deviceService.selectDeviceBySerialNumber(serialNumber);
|
||||||
|
if (device == null) {
|
||||||
|
log.warn("设备编号:{},产品ID为空,无法触发设备上电任务", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
productId = device.getProductId();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
serialNumber = serialNumber.toUpperCase();
|
||||||
|
String taskIdsKey = RedisKeyBuilder.buildPowerOnTriggerJobIdsKey(serialNumber);
|
||||||
|
|
||||||
|
// 从缓存读取 taskId 列表(列表本身作为标志位)
|
||||||
|
List<Long> taskIds = null;
|
||||||
|
Object cachedTaskIds = redisCache.getCacheObject(taskIdsKey);
|
||||||
|
if (cachedTaskIds != null) {
|
||||||
|
try {
|
||||||
|
taskIds = JSON.parseArray(cachedTaskIds.toString(), Long.class);
|
||||||
|
// 如果列表为空,表示没有待执行任务,直接返回(天然标志位)
|
||||||
|
if (CollectionUtils.isEmpty(taskIds)) {
|
||||||
|
log.debug("设备上电触发任务列表为空,设备编号:{}", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.debug("从缓存读取上电触发任务ID列表,设备编号:{},任务数量:{}", serialNumber, taskIds.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("解析缓存的任务ID列表失败,设备编号:{},将查询数据库", serialNumber, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 缓存不存在,表示没有待执行任务,直接返回
|
||||||
|
log.debug("设备上电触发任务缓存不存在,设备编号:{}", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果缓存中没有或解析失败,则查询数据库(兼容性处理)
|
||||||
|
if (taskIds == null || taskIds.isEmpty()) {
|
||||||
|
ModbusJob queryJob = new ModbusJob();
|
||||||
|
queryJob.setSubSerialNumber(serialNumber);
|
||||||
|
queryJob.setStatus("0"); // 正常状态
|
||||||
|
queryJob.setCycleType(3);
|
||||||
|
|
||||||
|
List<ModbusJob> modbusJobList = modbusJobService.selectModbusJobList(queryJob);
|
||||||
|
if (CollectionUtils.isEmpty(modbusJobList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选出上电触发任务的 taskId
|
||||||
|
taskIds = new ArrayList<>();
|
||||||
|
for (ModbusJob job : modbusJobList) {
|
||||||
|
if (Objects.equals(job.getCycleType(), 3) && "0".equals(job.getStatus())) {
|
||||||
|
taskIds.add(job.getTaskId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taskIds.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据 taskId 列表查询 ModbusJob 任务
|
||||||
|
List<ModbusJob> modbusJobList = new ArrayList<>();
|
||||||
|
for (Long taskId : taskIds) {
|
||||||
|
ModbusJob job = modbusJobService.selectModbusJobByTaskId(taskId);
|
||||||
|
if (job != null && "0".equals(job.getStatus()) && Objects.equals(job.getCycleType(), 3)) {
|
||||||
|
modbusJobList.add(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行所有上电触发任务(传入 taskIdsKey,用于成功后删除 taskId)
|
||||||
|
if (!modbusJobList.isEmpty()) {
|
||||||
|
execute(serialNumber, productId, modbusJobList, taskIdsKey);
|
||||||
|
} else {
|
||||||
|
// 如果没有有效任务,清除缓存
|
||||||
|
redisCache.deleteObject(taskIdsKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关键:无论任务是否成功,都删除"等待第一个数据包"标记
|
||||||
|
// 这样后续数据包就不会再触发任务了
|
||||||
|
redisCache.deleteObject(pendingKey);
|
||||||
|
log.info("设备上电触发任务处理完成,已删除第一个数据包标记,设备编号:{}", serialNumber);
|
||||||
|
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("处理设备上电触发任务异常,设备编号:{}", serialNumber, e);
|
||||||
|
// 异常时也删除标记,避免一直阻塞
|
||||||
|
try {
|
||||||
|
redisCache.deleteObject(pendingKey);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
// 忽略删除异常
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行设备上电触发任务
|
||||||
|
* 直接下发指令,不通过队列,确保指令及时准确下发
|
||||||
|
* 优化:TCP协议使用block()等待响应,确保上一条指令处理完成后再发送下一条
|
||||||
|
* 优化:任务执行成功后,从 taskId 列表中删除,列表为空时删除整个 key
|
||||||
|
*
|
||||||
|
* @param serialNumber 设备编号
|
||||||
|
* @param productId 产品ID
|
||||||
|
* @param modbusJobList Modbus任务列表
|
||||||
|
* @param taskIdsKey Redis key,用于存储待执行的 taskId 列表
|
||||||
|
*/
|
||||||
|
public void execute(String serialNumber, Long productId, List<ModbusJob> modbusJobList, String taskIdsKey) {
|
||||||
|
if (StringUtils.isEmpty(serialNumber) || productId == null || CollectionUtils.isEmpty(modbusJobList)) {
|
||||||
|
log.warn("设备上电触发任务执行失败:设备或任务列表为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取设备状态和传输协议
|
||||||
|
DeviceStatusVO deviceStatusVO = deviceService.selectDeviceStatusAndTransportStatus(serialNumber);
|
||||||
|
if (deviceStatusVO == null) {
|
||||||
|
log.warn("设备上电触发任务执行失败:无法获取设备状态,设备编号:{}", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerType serverType = ServerType.explain(deviceStatusVO.getTransport());
|
||||||
|
String topic = topicsUtils.buildTopic(productId, serialNumber, TopicType.FUNCTION_GET);
|
||||||
|
|
||||||
|
// 检查设备是否在线(TCP需要检查session)
|
||||||
|
if (ServerType.TCP.equals(serverType)) {
|
||||||
|
Session session = sessionStore.getSession(serialNumber);
|
||||||
|
if (session == null) {
|
||||||
|
log.warn("设备上电触发任务执行失败:TCP设备不在线,设备编号:{}", serialNumber);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录设备上电触发任务下发的指令片段(索引2到3位,2个字符)
|
||||||
|
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerCommandKey(serialNumber);
|
||||||
|
// 收集所有指令片段(用于支持多个指令的情况)
|
||||||
|
Set<String> commandSegments = new HashSet<>();
|
||||||
|
|
||||||
|
// 执行所有相关的modbus任务
|
||||||
|
int successCount = 0;
|
||||||
|
int timeoutCount = 0;
|
||||||
|
int totalCount = 0;
|
||||||
|
|
||||||
|
for (ModbusJob modbusJob : modbusJobList) {
|
||||||
|
if (!"0".equals(modbusJob.getStatus())) {
|
||||||
|
// 只执行正常状态的任务
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalCount++;
|
||||||
|
String command = modbusJob.getCommand();
|
||||||
|
if (StringUtils.isEmpty(command)) {
|
||||||
|
log.warn("设备上电触发任务执行失败:指令为空,设备编号:{},任务ID:{}", serialNumber, modbusJob.getTaskId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 记录下发的指令片段(索引2到3位,即第3到第4个字符,2个字符)
|
||||||
|
String cleanCommand = command.replaceAll("\\s+", "").toUpperCase();
|
||||||
|
if (cleanCommand.length() >= 4) {
|
||||||
|
String commandSegment = cleanCommand.substring(2, 4);
|
||||||
|
commandSegments.add(commandSegment);
|
||||||
|
log.debug("收集设备上电触发任务指令片段,设备编号:{},指令片段:{}", serialNumber, commandSegment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转换指令为字节数组
|
||||||
|
byte[] commandData = ByteUtils.convertToHexByteArray(command);
|
||||||
|
if (commandData.length == 0) {
|
||||||
|
log.warn("设备上电触发任务执行失败:指令转换失败,设备编号:{},指令:{}", serialNumber, command);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录指令到Redis缓存(用于轮询任务管理)
|
||||||
|
String runtimeCacheKey = RedisKeyBuilder.buildModbusRuntimeCacheKey(serialNumber);
|
||||||
|
redisCache.zSetAdd(runtimeCacheKey, command, DateUtils.getTimestampSeconds());
|
||||||
|
|
||||||
|
// 记录发送时间
|
||||||
|
long sendTime = System.currentTimeMillis();
|
||||||
|
boolean responseReceived;
|
||||||
|
|
||||||
|
// 根据传输协议下发指令
|
||||||
|
switch (serverType) {
|
||||||
|
case MQTT:
|
||||||
|
// 通过MQTT内部客户端下发指令
|
||||||
|
pubMqttClient.publish(commandData, topic, false, 0);
|
||||||
|
log.info("=>上电触发-MQTT-线程=[{}],轮询指令:[{}],主题:[{}]", Thread.currentThread().getName(), command, topic);
|
||||||
|
// MQTT协议无法直接等待响应,等待200ms后继续
|
||||||
|
try {
|
||||||
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
log.warn("MQTT指令等待被中断,设备编号:{}", serialNumber);
|
||||||
|
}
|
||||||
|
responseReceived = true; // MQTT协议假设发送成功
|
||||||
|
break;
|
||||||
|
case TCP:
|
||||||
|
// 下发TCP客户端,等待200ms内的响应
|
||||||
|
Message msg = new Message();
|
||||||
|
msg.setClientId(serialNumber);
|
||||||
|
msg.setPayload(Unpooled.wrappedBuffer(commandData));
|
||||||
|
try {
|
||||||
|
messageManager.requestR(serialNumber, msg, Message.class)
|
||||||
|
.timeout(Duration.ofMillis(20000)) // 200ms超时
|
||||||
|
.doOnSuccess(result -> log.info("=>上电触发-TCP-线程=[{}],轮询指令:[{}],响应成功", Thread.currentThread().getName(), command))
|
||||||
|
.doOnError(error -> log.warn("=>上电触发-TCP-线程=[{}],轮询指令:[{}],响应超时或失败:{}", Thread.currentThread().getName(), command, error.getMessage()))
|
||||||
|
.block(); // 阻塞等待响应,最多200ms
|
||||||
|
responseReceived = true;
|
||||||
|
log.info("=>上电触发-TCP-线程=[{}],轮询指令:[{}],已收到响应", Thread.currentThread().getName(), command);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 200ms内未收到响应,视为超时
|
||||||
|
long elapsed = System.currentTimeMillis() - sendTime;
|
||||||
|
log.warn("=>上电触发-TCP-线程=[{}],轮询指令:[{}],200ms内未收到响应,耗时:{}ms", Thread.currentThread().getName(), command, elapsed);
|
||||||
|
responseReceived = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log.warn("设备上电触发任务执行失败:不支持的传输协议,设备编号:{},协议:{}", serialNumber, serverType);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有在200ms内收到响应时才更新任务执行时间和从列表中删除 taskId
|
||||||
|
if (responseReceived) {
|
||||||
|
successCount++;
|
||||||
|
// 更新任务执行时间
|
||||||
|
updateTaskExecuteTime(modbusJob.getTaskId());
|
||||||
|
// 从 taskId 列表中删除已成功执行的任务
|
||||||
|
removeTaskIdFromList(taskIdsKey, modbusJob.getTaskId());
|
||||||
|
} else {
|
||||||
|
timeoutCount++;
|
||||||
|
log.warn("设备上电触发任务超时,不更新任务状态,设备编号:{},任务ID:{}", serialNumber, modbusJob.getTaskId());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("设备上电触发任务执行异常,设备编号:{},任务ID:{},指令:{}", serialNumber, modbusJob.getTaskId(), command, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有指令下发完成后,缓存指令片段集合(用于识别回包)
|
||||||
|
if (!commandSegments.isEmpty()) {
|
||||||
|
// 将指令片段集合转换为JSON字符串存储,过期时间5秒(足够设备响应)
|
||||||
|
String segmentsJson = JSON.toJSONString(new ArrayList<>(commandSegments));
|
||||||
|
redisCache.setCacheObject(cacheKey, segmentsJson, 5, TimeUnit.SECONDS);
|
||||||
|
log.debug("缓存设备上电触发任务指令片段集合,设备编号:{},片段数量:{},片段:{}", serialNumber, commandSegments.size(), commandSegments);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("设备上电触发任务执行完成,设备编号:{},总任务数:{},成功:{},超时:{}", serialNumber, totalCount, successCount, timeoutCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新任务执行时间(仅在收到响应时调用)
|
||||||
|
*
|
||||||
|
* @param taskId 任务ID
|
||||||
|
*/
|
||||||
|
private void updateTaskExecuteTime(Long taskId) {
|
||||||
|
if (taskId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusJob job = new ModbusJob();
|
||||||
|
job.setTaskId(taskId);
|
||||||
|
job.setExecuteTime(new Date());
|
||||||
|
job.setStatus("1");
|
||||||
|
modbusJobService.updateModbusJob(job);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 taskId 列表中删除已成功执行的任务
|
||||||
|
* 如果列表为空,则删除整个 key
|
||||||
|
*
|
||||||
|
* @param taskIdsKey Redis key
|
||||||
|
* @param taskId 要删除的任务ID
|
||||||
|
*/
|
||||||
|
private void removeTaskIdFromList(String taskIdsKey, Long taskId) {
|
||||||
|
if (StringUtils.isEmpty(taskIdsKey) || taskId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 读取当前列表
|
||||||
|
Object cachedTaskIds = redisCache.getCacheObject(taskIdsKey);
|
||||||
|
if (cachedTaskIds == null) {
|
||||||
|
log.debug("taskId 列表不存在,key:{}", taskIdsKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析为 List
|
||||||
|
List<Long> taskIds = JSON.parseArray(cachedTaskIds.toString(), Long.class);
|
||||||
|
if (taskIds == null || CollectionUtils.isEmpty(taskIds)) {
|
||||||
|
// 列表为空或解析失败,删除整个 key
|
||||||
|
redisCache.deleteObject(taskIdsKey);
|
||||||
|
log.debug("taskId 列表为空或解析失败,删除 key:{}", taskIdsKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除指定的 taskId
|
||||||
|
boolean removed = taskIds.remove(taskId);
|
||||||
|
if (!removed) {
|
||||||
|
log.debug("taskId {} 不在列表中,key:{}", taskId, taskIdsKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果列表为空,删除整个 key;否则更新列表
|
||||||
|
if (taskIds.isEmpty()) {
|
||||||
|
redisCache.deleteObject(taskIdsKey);
|
||||||
|
log.debug("上电触发任务列表已清空,删除 key:{}", taskIdsKey);
|
||||||
|
} else {
|
||||||
|
// 更新列表,保持过期时间
|
||||||
|
redisCache.setCacheObject(taskIdsKey, JSON.toJSONString(taskIds), 600, TimeUnit.SECONDS);
|
||||||
|
log.debug("从列表中删除 taskId:{},剩余任务数:{}", taskId, taskIds.size());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("从 taskId 列表中删除任务失败,key:{},taskId:{}", taskIdsKey, taskId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -47,10 +47,4 @@ public interface IDeviceReportMessageService {
|
|||||||
*/
|
*/
|
||||||
IProtocol selectedProtocol(Long productId);
|
IProtocol selectedProtocol(Long productId);
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步检查并触发设备上电任务(在收到第一个数据包时)
|
|
||||||
* @param serialNumber 设备编号
|
|
||||||
*/
|
|
||||||
void checkAndTriggerPowerOnTaskAsync(String serialNumber);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import com.fastbee.common.utils.DateUtils;
|
|||||||
import com.fastbee.common.utils.StringUtils;
|
import com.fastbee.common.utils.StringUtils;
|
||||||
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
|
import com.fastbee.common.utils.gateway.mq.TopicsUtils;
|
||||||
import com.fastbee.iot.cache.IOtaTaskCache;
|
import com.fastbee.iot.cache.IOtaTaskCache;
|
||||||
|
import com.fastbee.iot.data.executor.PowerOnTriggerTaskExecutor;
|
||||||
import com.fastbee.iot.data.service.IDataHandler;
|
import com.fastbee.iot.data.service.IDataHandler;
|
||||||
import com.fastbee.iot.data.service.IDeviceReportMessageService;
|
import com.fastbee.iot.data.service.IDeviceReportMessageService;
|
||||||
import com.fastbee.iot.data.service.IFirmwareTaskDetailService;
|
import com.fastbee.iot.data.service.IFirmwareTaskDetailService;
|
||||||
@ -83,9 +84,9 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
|
|||||||
private TopicsUtils topicsUtils;
|
private TopicsUtils topicsUtils;
|
||||||
@Resource
|
@Resource
|
||||||
private MqttRemoteManager remoteManager;
|
private MqttRemoteManager remoteManager;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ApplicationContext applicationContext; // self注入,用于异步调用
|
private PowerOnTriggerTaskExecutor powerOnTriggerTaskExecutor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理设备主动上报数据
|
* 处理设备主动上报数据
|
||||||
@ -93,17 +94,16 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
|
|||||||
@Override
|
@Override
|
||||||
public void parseReportMsg(DeviceReportBo bo) {
|
public void parseReportMsg(DeviceReportBo bo) {
|
||||||
String serialNumber = bo.getSerialNumber();
|
String serialNumber = bo.getSerialNumber();
|
||||||
|
Long productId = bo.getProductId();
|
||||||
|
|
||||||
// 异步检查是否需要触发上电任务(收到第一个数据包时触发)
|
// 异步检查是否需要触发上电任务(收到第一个数据包时触发)
|
||||||
DeviceReportMessageServiceImpl bean = applicationContext.getBean(DeviceReportMessageServiceImpl.class);
|
powerOnTriggerTaskExecutor.checkAndTriggerPowerOnTaskAsync(serialNumber, productId);
|
||||||
bean.checkAndTriggerPowerOnTaskAsync(serialNumber);
|
|
||||||
|
|
||||||
switch (bo.getServerType()) {
|
switch (bo.getServerType()) {
|
||||||
case MQTT:
|
case MQTT:
|
||||||
log.debug("=>MQ*收到设备主题[{}],消息:[{}]", bo.getTopicName(), bo.getData());
|
log.debug("=>MQ*收到设备主题[{}],消息:[{}]", bo.getTopicName(), bo.getData());
|
||||||
//构建消息
|
//构建消息
|
||||||
DeviceStatusVO deviceStatusVO = buildReport(bo);
|
DeviceStatusVO deviceStatusVO = buildReport(bo);
|
||||||
Long productId = bo.getProductId();
|
|
||||||
/*获取协议处理器*/
|
/*获取协议处理器*/
|
||||||
IProtocol protocol = selectedProtocol(productId);
|
IProtocol protocol = selectedProtocol(productId);
|
||||||
DeviceData data = DeviceData.builder()
|
DeviceData data = DeviceData.builder()
|
||||||
@ -245,43 +245,6 @@ public class DeviceReportMessageServiceImpl implements IDeviceReportMessageServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 异步检查并触发设备上电任务(在收到第一个数据包时)
|
|
||||||
* @param serialNumber 设备编号
|
|
||||||
*/
|
|
||||||
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
|
||||||
public void checkAndTriggerPowerOnTaskAsync(String serialNumber) {
|
|
||||||
try {
|
|
||||||
if (StringUtils.isEmpty(serialNumber)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerPendingKey(serialNumber.toUpperCase());
|
|
||||||
// 检查是否有待触发的上电任务标记
|
|
||||||
Object pendingFlag = redisCache.getCacheObject(cacheKey);
|
|
||||||
if (pendingFlag == null) {
|
|
||||||
// 没有待触发的任务,直接返回
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 有待触发的任务,删除标记(确保只触发一次)
|
|
||||||
redisCache.deleteObject(cacheKey);
|
|
||||||
|
|
||||||
// 获取设备信息
|
|
||||||
Device device = deviceService.selectDeviceBySerialNumber(serialNumber);
|
|
||||||
if (device == null) {
|
|
||||||
log.warn("触发上电任务时设备不存在,设备编号:{}", serialNumber);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触发上电任务(异步执行)
|
|
||||||
log.info("收到设备第一个数据包,触发上电任务,设备编号:{}", serialNumber);
|
|
||||||
deviceService.triggerPowerOnTasks(device);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("检查并触发设备上电任务异常,设备编号:{}", serialNumber, e);
|
|
||||||
} }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建消息
|
* 构建消息
|
||||||
*
|
*
|
||||||
|
|||||||
@ -44,7 +44,7 @@ public class MessageDecoderWrapper extends ChannelInboundHandlerAdapter {
|
|||||||
/*将当前readerIndex增加指定的长度,已经解码完成*/
|
/*将当前readerIndex增加指定的长度,已经解码完成*/
|
||||||
input.skipBytes(input.readableBytes());
|
input.skipBytes(input.readableBytes());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("消息解码异常[" + ByteBufUtil.hexDump(input, 0, input.writerIndex()) + "]", e);
|
log.error("消息解码异常[{}]", ByteBufUtil.hexDump(input, 0, input.writerIndex()), e);
|
||||||
throw new DecoderException(e);
|
throw new DecoderException(e);
|
||||||
} finally {
|
} finally {
|
||||||
input.release();
|
input.release();
|
||||||
|
|||||||
@ -185,18 +185,6 @@ public interface IDeviceService {
|
|||||||
*/
|
*/
|
||||||
public int updateDeviceStatusAndLocation(Device device, String ipAddress);
|
public int updateDeviceStatusAndLocation(Device device, String ipAddress);
|
||||||
|
|
||||||
/**
|
|
||||||
* 标记设备上电任务待处理
|
|
||||||
* @param device 设备信息
|
|
||||||
*/
|
|
||||||
void markPowerOnTriggerPending(Device device);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 触发设备上电任务(供外部调用,如收到第一个数据包时)
|
|
||||||
* @param device 设备信息
|
|
||||||
*/
|
|
||||||
public void triggerPowerOnTasks(Device device);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新设备状态
|
* 更新设备状态
|
||||||
*
|
*
|
||||||
|
|||||||
@ -149,9 +149,6 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
@Resource
|
@Resource
|
||||||
private IModbusJobService modbusJobService;
|
private IModbusJobService modbusJobService;
|
||||||
@Resource
|
@Resource
|
||||||
@Lazy
|
|
||||||
private IDeviceJobService deviceJobService;
|
|
||||||
@Resource
|
|
||||||
private ProductSubGatewayMapper productSubGatewayMapper;
|
private ProductSubGatewayMapper productSubGatewayMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ModbusParamsMapper modbusParamsMapper;
|
private ModbusParamsMapper modbusParamsMapper;
|
||||||
@ -1373,201 +1370,18 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
|
|
||||||
// 设备上线时,标记需要触发上电任务(等待第一个数据包)
|
// 设备上线时,标记需要触发上电任务(等待第一个数据包)
|
||||||
if (device.getStatus() == 3) {
|
if (device.getStatus() == 3) {
|
||||||
DeviceServiceImpl bean = applicationContext.getBean(DeviceServiceImpl.class);
|
try {
|
||||||
bean.markPowerOnTriggerPending(device);
|
Object executor = applicationContext.getBean("powerOnTriggerTaskExecutor");
|
||||||
|
Method executeMethod = executor.getClass().getMethod("markPowerOnTriggerPending", Device.class);
|
||||||
|
executeMethod.invoke(executor, device);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("标记需要触发上电任务失败,设备编号:{}", device.getSerialNumber(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 标记设备需要触发上电任务(等待第一个数据包)
|
|
||||||
* @param device 设备信息
|
|
||||||
*/
|
|
||||||
@Async(FastBeeConstant.TASK.TRIGGER_POWER_ON_TASK_ASYNC)
|
|
||||||
@Override
|
|
||||||
public void markPowerOnTriggerPending(Device device) {
|
|
||||||
try {
|
|
||||||
// 查询该设备的所有设备上电触发任务
|
|
||||||
DeviceJob queryJob = new DeviceJob();
|
|
||||||
queryJob.setDeviceId(device.getDeviceId());
|
|
||||||
queryJob.setJobType(5); // modbus轮询任务
|
|
||||||
queryJob.setStatus("0"); // 正常状态
|
|
||||||
|
|
||||||
List<DeviceJob> jobList = deviceJobService.selectJobList(queryJob);
|
|
||||||
if (CollectionUtils.isEmpty(jobList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有上电触发任务
|
|
||||||
boolean hasPowerOnTrigger = false;
|
|
||||||
for (DeviceJob job : jobList) {
|
|
||||||
if (isPowerOnTrigger(job.getRemark())) {
|
|
||||||
hasPowerOnTrigger = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果有上电触发任务,则在Redis中标记,等待第一个数据包
|
|
||||||
// 无论标记是否存在,都重新设置标记,确保恢复任务后能重新触发
|
|
||||||
String serialNumber = device.getSerialNumber().toUpperCase();
|
|
||||||
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerPendingKey(serialNumber);
|
|
||||||
if (hasPowerOnTrigger) {
|
|
||||||
// 设置过期时间为10分钟,防止长时间未收到数据包导致标记一直存在
|
|
||||||
// 无论之前是否有标记,都重新设置,确保恢复任务后能重新触发
|
|
||||||
redisCache.setCacheObject(cacheKey, "1", 600, TimeUnit.SECONDS);
|
|
||||||
log.info("标记设备需要触发上电任务,等待第一个数据包,设备编号:{}", serialNumber);
|
|
||||||
} else {
|
|
||||||
// 如果没有上电触发任务,清除可能存在的旧标记
|
|
||||||
if (redisCache.hasKey(cacheKey)) {
|
|
||||||
redisCache.deleteObject(cacheKey);
|
|
||||||
log.debug("清除设备上电触发任务标记,设备编号:{}", serialNumber);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("标记设备上电触发任务异常,设备编号:{}", device.getSerialNumber(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 触发设备上电任务(收到第一个数据包时)
|
|
||||||
* @param device 设备信息
|
|
||||||
*/
|
|
||||||
public void triggerPowerOnTasks(Device device) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
// 查询该设备的所有设备上电触发任务
|
|
||||||
DeviceJob queryJob = new DeviceJob();
|
|
||||||
queryJob.setDeviceId(device.getDeviceId());
|
|
||||||
queryJob.setJobType(5); // modbus轮询任务
|
|
||||||
// 只查询正常状态的任务(如果用户手动改成正常,下次上电会再次触发)
|
|
||||||
queryJob.setStatus("0"); // 正常状态("0"=正常,"1"=暂停)
|
|
||||||
|
|
||||||
List<DeviceJob> jobList = deviceJobService.selectJobList(queryJob);
|
|
||||||
if (CollectionUtils.isEmpty(jobList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查找设备上电触发任务(状态为正常即可,不需要检查sent状态)
|
|
||||||
for (DeviceJob job : jobList) {
|
|
||||||
if (isPowerOnTrigger(job.getRemark())) {
|
|
||||||
// 执行设备上电触发任务
|
|
||||||
executePowerOnTriggerTask(job, device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("处理设备上电触发任务异常,设备编号:{}", device.getSerialNumber(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查是否是设备上电触发任务
|
|
||||||
*
|
|
||||||
* @param remark remark字段
|
|
||||||
* @return true表示是设备上电触发任务
|
|
||||||
*/
|
|
||||||
private boolean isPowerOnTrigger(String remark) {
|
|
||||||
if (remark == null || remark.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return remark.contains("power_on_trigger");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行设备上电触发任务
|
|
||||||
*
|
|
||||||
* @param deviceJob 设备任务
|
|
||||||
* @param device 设备信息
|
|
||||||
*/
|
|
||||||
private void executePowerOnTriggerTask(DeviceJob deviceJob, Device device) {
|
|
||||||
try {
|
|
||||||
// 查询对应的ModbusJob
|
|
||||||
ModbusJob queryJob = new ModbusJob();
|
|
||||||
queryJob.setJobId(deviceJob.getJobId());
|
|
||||||
List<ModbusJob> modbusJobList = modbusJobService.selectModbusJobList(queryJob);
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(modbusJobList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行所有相关的modbus任务
|
|
||||||
boolean hasActiveTask = false;
|
|
||||||
// 记录设备上电触发任务下发的指令片段(索引2到11位)
|
|
||||||
String serialNumber = device.getSerialNumber().toUpperCase();
|
|
||||||
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerCommandKey(serialNumber);
|
|
||||||
|
|
||||||
for (ModbusJob modbusJob : modbusJobList) {
|
|
||||||
if ("0".equals(modbusJob.getStatus())) { // 只执行正常状态的任务
|
|
||||||
hasActiveTask = true;
|
|
||||||
// 记录下发的指令片段(索引2到11位,即第3到第12个字符)
|
|
||||||
String command = modbusJob.getCommand();
|
|
||||||
if (StringUtils.isNotEmpty(command) && command.length() >= 12) {
|
|
||||||
// 去除空格并转为大写
|
|
||||||
String cleanCommand = command.replaceAll("\\s+", "").toUpperCase();
|
|
||||||
if (cleanCommand.length() >= 12) {
|
|
||||||
// 提取索引2到11位(即第3到第12个字符)
|
|
||||||
String commandSegment = cleanCommand.substring(2, 12);
|
|
||||||
// 存储到Redis缓存,设置过期时间为5分钟(设备上报应该在5分钟内完成)
|
|
||||||
redisCache.setCacheObject(cacheKey, commandSegment, 300, TimeUnit.SECONDS);
|
|
||||||
log.info("记录设备上电触发任务下发指令片段,设备编号:{},指令片段:{}", serialNumber, commandSegment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasActiveTask) {
|
|
||||||
// 通过反射调用DeviceJobInvoke.invokeMethod来执行任务
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName("com.fastbee.iot.data.job.DeviceJobInvoke");
|
|
||||||
Method method = clazz.getMethod("invokeMethod", DeviceJob.class);
|
|
||||||
method.invoke(null, deviceJob);
|
|
||||||
log.info("设备上电触发任务执行成功,设备编号:{},任务ID:{}", device.getSerialNumber(), deviceJob.getJobId());
|
|
||||||
|
|
||||||
// 执行成功后,将任务状态改为暂停
|
|
||||||
updatePowerOnTriggerTaskStatus(modbusJobList);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("执行设备上电触发任务异常,设备编号:{},任务ID:{}", device.getSerialNumber(), deviceJob.getJobId(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("处理设备上电触发任务异常,设备编号:{},任务ID:{}", device.getSerialNumber(), deviceJob.getJobId(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新设备上电触发任务状态为暂停
|
|
||||||
*
|
|
||||||
* @param modbusJobList 设备任务
|
|
||||||
*/
|
|
||||||
private void updatePowerOnTriggerTaskStatus(List<ModbusJob> modbusJobList) {
|
|
||||||
if (CollectionUtils.isEmpty(modbusJobList)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
log.info("更新设备上电触发任务状态为暂停,任务数量:{}", modbusJobList.size());
|
|
||||||
java.util.Date executeTime = DateUtils.getNowDate();
|
|
||||||
modbusJobList.forEach(modbusJob -> {
|
|
||||||
// 只更新status=0的任务为status=1,如果已经是status=1则跳过
|
|
||||||
if (!"0".equals(modbusJob.getStatus())) {
|
|
||||||
log.debug("任务ID:{}的状态已经是{},跳过更新", modbusJob.getTaskId(), modbusJob.getStatus());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ModbusJob job = new ModbusJob();
|
|
||||||
job.setStatus("1");
|
|
||||||
job.setTaskId(modbusJob.getTaskId());
|
|
||||||
// 记录执行时间
|
|
||||||
job.setExecuteTime(executeTime);
|
|
||||||
try {
|
|
||||||
// 将任务状态改为暂停("1"=暂停,"0"=正常),并记录执行时间
|
|
||||||
modbusJobService.updateModbusJob(job);
|
|
||||||
log.info("设备上电触发任务已设置为暂停状态并记录执行时间,任务ID:{},执行时间:{}", modbusJob.getTaskId(), executeTime);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("更新设备上电触发任务状态异常,任务ID:{}", modbusJob.getTaskId(), e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param device 设备状态
|
* @param device 设备状态
|
||||||
* @return 结果
|
* @return 结果
|
||||||
@ -2130,7 +1944,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断是否是设备上电触发的回包
|
* 判断是否是设备上电触发的回包
|
||||||
* 从Redis缓存中获取下发的指令片段(索引2到11位),与上报的指令片段进行比较
|
* 从Redis缓存中获取下发的指令片段(索引2到3位,2个字符),与上报的指令片段进行比较
|
||||||
*
|
*
|
||||||
* @param sources 原始消息数据包
|
* @param sources 原始消息数据包
|
||||||
* @param deviceNumber 设备编号
|
* @param deviceNumber 设备编号
|
||||||
@ -2142,13 +1956,36 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 从Redis缓存中获取下发的指令片段
|
// 从Redis缓存中获取下发的指令片段(可能是单个片段或片段集合)
|
||||||
String serialNumber = deviceNumber.toUpperCase();
|
String serialNumber = deviceNumber.toUpperCase();
|
||||||
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerCommandKey(serialNumber);
|
String cacheKey = RedisKeyBuilder.buildPowerOnTriggerCommandKey(serialNumber);
|
||||||
String sentCommandSegment = redisCache.getCacheObject(cacheKey);
|
Object cachedSegments = redisCache.getCacheObject(cacheKey);
|
||||||
|
|
||||||
// 如果缓存中没有下发的指令片段,说明不是设备上电触发的回包
|
// 如果缓存中没有下发的指令片段,说明不是设备上电触发的回包
|
||||||
if (StringUtils.isEmpty(sentCommandSegment)) {
|
if (cachedSegments == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析指令片段(可能是单个字符串或JSON数组)
|
||||||
|
Set<String> sentCommandSegments = new HashSet<>();
|
||||||
|
try {
|
||||||
|
if (cachedSegments instanceof String) {
|
||||||
|
String segmentsStr = (String) cachedSegments;
|
||||||
|
// 尝试解析为JSON数组
|
||||||
|
if (segmentsStr.startsWith("[")) {
|
||||||
|
List<String> segmentsList = JSON.parseArray(segmentsStr, String.class);
|
||||||
|
sentCommandSegments.addAll(segmentsList);
|
||||||
|
} else {
|
||||||
|
// 单个片段(兼容旧数据)
|
||||||
|
sentCommandSegments.add(segmentsStr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("解析指令片段失败,设备编号:{}", serialNumber, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sentCommandSegments.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2172,15 +2009,16 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
hexStr = sources.toString().replaceAll("\\s+", "").toUpperCase();
|
hexStr = sources.toString().replaceAll("\\s+", "").toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hexStr.length() < 12) {
|
// 至少需要4个字符才能提取索引2到3位
|
||||||
|
if (hexStr.length() < 4) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取上报指令的索引2到11位(即第3到第12个字符)
|
// 提取上报指令的索引2到3位(即第3到第4个字符,2个字符)
|
||||||
String actualSegment = hexStr.substring(2, 12);
|
String actualSegment = hexStr.substring(2, 4);
|
||||||
|
|
||||||
// 与下发的指令片段进行比较
|
// 与下发的指令片段集合进行比较(支持多个指令)
|
||||||
boolean isMatch = sentCommandSegment.equals(actualSegment);
|
boolean isMatch = sentCommandSegments.contains(actualSegment);
|
||||||
|
|
||||||
if (isMatch) {
|
if (isMatch) {
|
||||||
// 匹配成功,删除缓存(避免重复匹配)
|
// 匹配成功,删除缓存(避免重复匹配)
|
||||||
@ -2350,3 +2188,4 @@ public class DeviceServiceImpl implements IDeviceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -106,6 +106,15 @@ public class ModbusJobServiceImpl implements IModbusJobService {
|
|||||||
@Override
|
@Override
|
||||||
public int insertModbusJob(ModbusJob modbusJob) {
|
public int insertModbusJob(ModbusJob modbusJob) {
|
||||||
try {
|
try {
|
||||||
|
// 自动填充 cycleType:如果前端没有传递,则根据 remark 判断
|
||||||
|
if (modbusJob.getCycleType() == null) {
|
||||||
|
if (isPowerOnTrigger(modbusJob.getRemark())) {
|
||||||
|
modbusJob.setCycleType(3); // 设备上电触发
|
||||||
|
} else {
|
||||||
|
modbusJob.setCycleType(1); // 周期循环(默认)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//查询关联的网关设备
|
//查询关联的网关设备
|
||||||
DeviceAndProtocol deviceAndProtocol = deviceService.selectProtocolBySerialNumber(modbusJob.getSubSerialNumber());
|
DeviceAndProtocol deviceAndProtocol = deviceService.selectProtocolBySerialNumber(modbusJob.getSubSerialNumber());
|
||||||
//如果是网关子设备
|
//如果是网关子设备
|
||||||
@ -202,6 +211,15 @@ public class ModbusJobServiceImpl implements IModbusJobService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int updateModbusJob(ModbusJob modbusJob) {
|
public int updateModbusJob(ModbusJob modbusJob) {
|
||||||
|
// 自动填充 cycleType:如果前端没有传递,则根据 remark 判断
|
||||||
|
if (modbusJob.getCycleType() == null && modbusJob.getRemark() != null) {
|
||||||
|
if (isPowerOnTrigger(modbusJob.getRemark())) {
|
||||||
|
modbusJob.setCycleType(3); // 设备上电触发
|
||||||
|
} else {
|
||||||
|
modbusJob.setCycleType(1); // 周期循环(默认)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果状态从1(暂停)改为0(正常),需要清空执行时间
|
// 如果状态从1(暂停)改为0(正常),需要清空执行时间
|
||||||
if (modbusJob.getTaskId() != null && modbusJob.getStatus() != null && "0".equals(modbusJob.getStatus())) {
|
if (modbusJob.getTaskId() != null && modbusJob.getStatus() != null && "0".equals(modbusJob.getStatus())) {
|
||||||
ModbusJob existingJob = modbusJobMapper.selectModbusJobByTaskId(modbusJob.getTaskId());
|
ModbusJob existingJob = modbusJobMapper.selectModbusJobByTaskId(modbusJob.getTaskId());
|
||||||
|
|||||||
@ -17,10 +17,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<result property="remark" column="remark" />
|
<result property="remark" column="remark" />
|
||||||
<result property="deviceType" column="device_type" />
|
<result property="deviceType" column="device_type" />
|
||||||
<result property="executeTime" column="execute_time" />
|
<result property="executeTime" column="execute_time" />
|
||||||
|
<result property="cycleType" column="cycle_type" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="selectModbusJobVo">
|
<sql id="selectModbusJobVo">
|
||||||
select task_id,job_name, sub_device_id, sub_serial_number, command,device_type, job_id, status, create_by, create_time, remark, execute_time from iot_modbus_job
|
select task_id,job_name, sub_device_id, sub_serial_number, command,device_type, job_id, status, create_by, create_time, remark, execute_time, cycle_type from iot_modbus_job
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectModbusJobList" parameterType="com.fastbee.iot.domain.ModbusJob" resultMap="ModbusJobResult">
|
<select id="selectModbusJobList" parameterType="com.fastbee.iot.domain.ModbusJob" resultMap="ModbusJobResult">
|
||||||
@ -29,6 +30,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="subDeviceId != null "> and sub_device_id = #{subDeviceId}</if>
|
<if test="subDeviceId != null "> and sub_device_id = #{subDeviceId}</if>
|
||||||
<if test="subSerialNumber != null and subSerialNumber != ''"> and sub_serial_number = #{subSerialNumber}</if>
|
<if test="subSerialNumber != null and subSerialNumber != ''"> and sub_serial_number = #{subSerialNumber}</if>
|
||||||
<if test="status != null "> and status = #{status}</if>
|
<if test="status != null "> and status = #{status}</if>
|
||||||
|
<if test="cycleType != null "> and cycle_type = #{cycleType}</if>
|
||||||
<if test="jobId != null">and job_id = #{jobId}</if>
|
<if test="jobId != null">and job_id = #{jobId}</if>
|
||||||
<if test="subDeviceList != null and subDeviceList != ''">
|
<if test="subDeviceList != null and subDeviceList != ''">
|
||||||
and sub_serial_number in
|
and sub_serial_number in
|
||||||
@ -58,6 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="remark != null">remark,</if>
|
<if test="remark != null">remark,</if>
|
||||||
<if test="deviceType != null">device_type,</if>
|
<if test="deviceType != null">device_type,</if>
|
||||||
<if test="executeTime != null">execute_time,</if>
|
<if test="executeTime != null">execute_time,</if>
|
||||||
|
<if test="cycleType != null">cycle_type,</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="jobName != null">#{jobName},</if>
|
<if test="jobName != null">#{jobName},</if>
|
||||||
@ -71,6 +74,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="remark != null">#{remark},</if>
|
<if test="remark != null">#{remark},</if>
|
||||||
<if test="deviceType != null">#{deviceType},</if>
|
<if test="deviceType != null">#{deviceType},</if>
|
||||||
<if test="executeTime != null">#{executeTime},</if>
|
<if test="executeTime != null">#{executeTime},</if>
|
||||||
|
<if test="cycleType != null">#{cycleType},</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
@ -87,6 +91,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<if test="createTime != null">create_time = #{createTime},</if>
|
<if test="createTime != null">create_time = #{createTime},</if>
|
||||||
<if test="remark != null">remark = #{remark},</if>
|
<if test="remark != null">remark = #{remark},</if>
|
||||||
<if test="executeTime != null">execute_time = #{executeTime},</if>
|
<if test="executeTime != null">execute_time = #{executeTime},</if>
|
||||||
|
<if test="cycleType != null">cycle_type = #{cycleType},</if>
|
||||||
</trim>
|
</trim>
|
||||||
where task_id = #{taskId}
|
where task_id = #{taskId}
|
||||||
</update>
|
</update>
|
||||||
|
|||||||
@ -619,12 +619,14 @@ CREATE TABLE `iot_modbus_job`
|
|||||||
`sub_device_id` bigint(20) NOT NULL COMMENT '子设备id',
|
`sub_device_id` bigint(20) NOT NULL COMMENT '子设备id',
|
||||||
`sub_serial_number` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT '子设备编号',
|
`sub_serial_number` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT '子设备编号',
|
||||||
`device_type` tinyint(1) DEFAULT NULL COMMENT '设备类型',
|
`device_type` tinyint(1) DEFAULT NULL COMMENT '设备类型',
|
||||||
|
`cycle_type` int(2) DEFAULT NULL COMMENT '时间周期类型(1-周期循环,2-自定义时间段,3-设备上电触发)',
|
||||||
`command` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '指令',
|
`command` varchar(255) COLLATE utf8_unicode_ci NOT NULL COMMENT '指令',
|
||||||
`job_id` bigint(20) NOT NULL COMMENT '任务id',
|
`job_id` bigint(20) NOT NULL COMMENT '任务id',
|
||||||
`status` char(1) COLLATE utf8_unicode_ci NOT NULL COMMENT '状态(0正常 1暂停)',
|
`status` char(1) COLLATE utf8_unicode_ci NOT NULL COMMENT '状态(0正常 1暂停)',
|
||||||
`create_by` varchar(64) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '创建者',
|
`create_by` varchar(64) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '创建者',
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
`remark` varchar(500) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '备注信息',
|
`remark` varchar(500) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '备注信息',
|
||||||
|
`execute_time` datetime DEFAULT NULL COMMENT '执行时间(仅设备上电触发任务使用,记录任务执行的时间)',
|
||||||
PRIMARY KEY (`task_id`) USING BTREE
|
PRIMARY KEY (`task_id`) USING BTREE
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='轮训任务列表';
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='轮训任务列表';
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,10 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('dataCenter.history.384934-3')" prop="identifiers">
|
<el-form-item :label="$t('dataCenter.history.384934-3')" prop="identifiers">
|
||||||
<el-select style="width: 100%" v-model="devQueryParams.identifiers" :placeholder="$t('dataCenter.history.384934-4')" filterable multiple collapse-tags>
|
<el-select style="width: 100%" v-model="devQueryParams.identifiers" :placeholder="$t('dataCenter.history.384934-4')" filterable multiple collapse-tags @change="handleDevIdentifierChange">
|
||||||
|
<el-option label="全部勾选" value="__selectAll__">
|
||||||
|
<span style="font-weight: bold; color: #409EFF;">全部勾选</span>
|
||||||
|
</el-option>
|
||||||
<el-option v-for="(item, index) in devIdentifierList" :key="index" :label="item.modelName" :value="item.identifier"></el-option>
|
<el-option v-for="(item, index) in devIdentifierList" :key="index" :label="item.modelName" :value="item.identifier"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -76,7 +79,10 @@
|
|||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('dataCenter.history.384934-3')" prop="identifiers">
|
<el-form-item :label="$t('dataCenter.history.384934-3')" prop="identifiers">
|
||||||
<el-select style="width: 100%" v-model="sceneQueryParams.identifiers" :placeholder="$t('dataCenter.history.384934-4')" multiple collapse-tags>
|
<el-select style="width: 100%" v-model="sceneQueryParams.identifiers" :placeholder="$t('dataCenter.history.384934-4')" multiple collapse-tags @change="handleSceneIdentifierChange">
|
||||||
|
<el-option label="全部勾选" value="__selectAll__">
|
||||||
|
<span style="font-weight: bold; color: #409EFF;">全部勾选</span>
|
||||||
|
</el-option>
|
||||||
<el-option v-for="(item, index) in sceneIdentifierList" :key="index" :label="item.sourceName" :value="item.identifier"></el-option>
|
<el-option v-for="(item, index) in sceneIdentifierList" :key="index" :label="item.sourceName" :value="item.identifier"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -284,6 +290,17 @@ export default {
|
|||||||
this.devQueryParams.identifiers = [];
|
this.devQueryParams.identifiers = [];
|
||||||
this.getDevIdentifierList(val);
|
this.getDevIdentifierList(val);
|
||||||
},
|
},
|
||||||
|
// 设备变量选择变化
|
||||||
|
handleDevIdentifierChange(val) {
|
||||||
|
// 如果选择了"全部勾选",则选中所有变量
|
||||||
|
const index = val.indexOf('__selectAll__');
|
||||||
|
if (index > -1) {
|
||||||
|
const allIdentifiers = this.devIdentifierList.map(item => item.identifier);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.devQueryParams.identifiers = [...allIdentifiers];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
getDevIdentifierList(deviceId) {
|
getDevIdentifierList(deviceId) {
|
||||||
const params = {
|
const params = {
|
||||||
deviceId: deviceId,
|
deviceId: deviceId,
|
||||||
@ -379,6 +396,17 @@ export default {
|
|||||||
this.sceneQueryParams.identifiers = [];
|
this.sceneQueryParams.identifiers = [];
|
||||||
this.getSceneIdentifierList(val);
|
this.getSceneIdentifierList(val);
|
||||||
},
|
},
|
||||||
|
// 场景变量选择变化
|
||||||
|
handleSceneIdentifierChange(val) {
|
||||||
|
// 如果选择了"全部勾选",则选中所有变量
|
||||||
|
const index = val.indexOf('__selectAll__');
|
||||||
|
if (index > -1) {
|
||||||
|
const allIdentifiers = this.sceneIdentifierList.map(item => item.identifier);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.sceneQueryParams.identifiers = [...allIdentifiers];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
// 获取变量情况列表
|
// 获取变量情况列表
|
||||||
getSceneIdentifierList(sceneModelDeviceId) {
|
getSceneIdentifierList(sceneModelDeviceId) {
|
||||||
const params = {
|
const params = {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user