package com.mes.interaction.workstation.transfer.handler;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.mes.device.entity.DeviceConfig;
|
import com.mes.device.entity.GlassInfo;
|
import com.mes.device.mapper.DeviceGlassInfoMapper;
|
import com.mes.device.service.DevicePlcOperationService;
|
import com.mes.device.service.GlassInfoService;
|
import com.mes.device.vo.DevicePlcVO;
|
import com.mes.interaction.workstation.base.WorkstationBaseHandler;
|
import com.mes.interaction.workstation.config.WorkstationLogicConfig;
|
import com.mes.s7.enhanced.EnhancedS7Serializer;
|
import com.mes.s7.provider.S7SerializerProvider;
|
import com.mes.service.PlcDynamicDataService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.stereotype.Component;
|
|
import javax.annotation.PreDestroy;
|
import java.time.LocalDateTime;
|
import java.util.*;
|
import java.util.concurrent.*;
|
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.stream.Collectors;
|
|
/**
|
* 卧转立主体设备逻辑处理器
|
* 负责玻璃缓冲、容量校验、批次组装、PLC写入等逻辑
|
*/
|
@Slf4j
|
@Component
|
public class HorizontalTransferLogicHandler extends WorkstationBaseHandler {
|
|
private final PlcDynamicDataService plcDynamicDataService;
|
private final GlassInfoService glassInfoService;
|
private final S7SerializerProvider s7SerializerProvider;
|
|
@Autowired(required = false)
|
private DeviceGlassInfoMapper glassInfoMapper;
|
|
// 玻璃缓冲队列:deviceId -> 玻璃信息列表
|
private final Map<String, List<GlassBufferItem>> glassBuffer = new ConcurrentHashMap<>();
|
|
// 最后扫码时间:deviceId -> 最后扫码时间戳
|
private final Map<String, AtomicLong> lastScanTime = new ConcurrentHashMap<>();
|
|
// 监控任务:deviceId -> 监控任务
|
private final Map<String, ScheduledFuture<?>> monitorTasks = new ConcurrentHashMap<>();
|
|
// 监控线程池
|
private final ScheduledExecutorService monitorExecutor = Executors.newScheduledThreadPool(5, r -> {
|
Thread t = new Thread(r, "HorizontalTransferMonitor");
|
t.setDaemon(true);
|
return t;
|
});
|
|
@Autowired
|
public HorizontalTransferLogicHandler(DevicePlcOperationService devicePlcOperationService,
|
PlcDynamicDataService plcDynamicDataService,
|
@Qualifier("deviceGlassInfoService") GlassInfoService glassInfoService,
|
S7SerializerProvider s7SerializerProvider) {
|
super(devicePlcOperationService);
|
this.plcDynamicDataService = plcDynamicDataService;
|
this.glassInfoService = glassInfoService;
|
this.s7SerializerProvider = s7SerializerProvider;
|
}
|
|
@Override
|
public String getDeviceType() {
|
return DeviceConfig.DeviceType.WORKSTATION_TRANSFER;
|
}
|
|
@Override
|
protected DevicePlcVO.OperationResult doExecute(DeviceConfig deviceConfig,
|
String operation,
|
Map<String, Object> params,
|
Map<String, Object> logicParams) {
|
WorkstationLogicConfig config = parseWorkstationConfig(logicParams);
|
|
try {
|
switch (operation) {
|
case "checkAndProcess":
|
case "process":
|
// 这里必须把 params 传进去,以便在多设备任务流程中
|
// 能够通过 _taskContext 将卧转立输出的玻璃ID写入任务上下文
|
return handleCheckAndProcess(deviceConfig, config, logicParams, params);
|
case "startMonitor":
|
return handleStartMonitor(deviceConfig, config, logicParams);
|
case "stopMonitor":
|
return handleStopMonitor(deviceConfig);
|
case "clearBuffer":
|
return handleClearBuffer(deviceConfig);
|
case "clearPlc":
|
return handleClearPlc(deviceConfig);
|
default:
|
return buildResult(deviceConfig, operation, false,
|
"不支持的操作: " + operation);
|
}
|
} catch (Exception e) {
|
log.error("卧转立主体处理异常: deviceId={}, operation={}",
|
deviceConfig.getId(), operation, e);
|
return buildResult(deviceConfig, operation, false,
|
"处理异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 检查并处理玻璃批次
|
* 从数据库读取最近扫码的玻璃,进行容量判断,组装批次,写入PLC
|
*/
|
private DevicePlcVO.OperationResult handleCheckAndProcess(
|
DeviceConfig deviceConfig,
|
WorkstationLogicConfig config,
|
Map<String, Object> logicParams,
|
Map<String, Object> params) {
|
|
String deviceId = deviceConfig.getDeviceId();
|
EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
|
if (serializer == null) {
|
return buildResult(deviceConfig, "checkAndProcess", false,
|
"获取PLC序列化器失败");
|
}
|
|
try {
|
// 1. 从数据库查询最近扫码的玻璃信息(最近1分钟内的记录)
|
List<GlassInfo> recentGlasses = queryRecentScannedGlasses(deviceConfig, logicParams);
|
boolean hasNewGlass = false;
|
|
if (!recentGlasses.isEmpty()) {
|
log.info("查询到最近扫码的玻璃: deviceId={}, count={}",
|
deviceId, recentGlasses.size());
|
|
// 2. 更新缓冲队列;仅在有“新玻璃”加入缓冲时才更新最后扫码时间
|
hasNewGlass = updateBuffer(deviceId, recentGlasses);
|
if (hasNewGlass) {
|
lastScanTime
|
.computeIfAbsent(deviceId, k -> new AtomicLong())
|
.set(System.currentTimeMillis());
|
}
|
} else {
|
log.debug("未查询到最近扫码的玻璃: deviceId={}", deviceId);
|
}
|
|
// 3. 检查缓冲队列(即使查询不到新玻璃,缓冲中可能还有待处理的玻璃)
|
List<GlassBufferItem> buffer = glassBuffer.get(deviceId);
|
if (buffer == null || buffer.isEmpty()) {
|
// 缓冲为空且无新玻璃,返回空状态
|
return buildResult(deviceConfig, "checkAndProcess", true,
|
"缓冲队列为空,无待处理玻璃");
|
}
|
|
// 4. 判断是否满足处理条件
|
boolean shouldProcess = shouldProcessBatch(deviceId, buffer, config);
|
if (!shouldProcess) {
|
// 未满足处理条件:构造带有等待进度的提示信息,便于前端展示
|
String waitMessage;
|
AtomicLong lastTime = lastScanTime.get(deviceId);
|
Integer delayMs = config.getTransferDelayMs();
|
if (lastTime != null && delayMs != null && delayMs > 0) {
|
long elapsedMs = System.currentTimeMillis() - lastTime.get();
|
if (elapsedMs < 0) {
|
elapsedMs = 0;
|
}
|
long totalMs = delayMs;
|
long elapsedSec = elapsedMs / 1000;
|
long totalSec = totalMs / 1000;
|
waitMessage = String.format("等待更多玻璃或超时触发批次处理 (已等待 %d/%d 秒)",
|
elapsedSec, totalSec);
|
} else {
|
// 没有有效的最后扫码时间或配置,退回到固定提示
|
waitMessage = "等待更多玻璃或30s超时";
|
}
|
return buildResult(deviceConfig, "checkAndProcess", true, waitMessage);
|
}
|
|
// 5. 容量判断和批次组装(考虑玻璃间隙)
|
Integer glassGap = getLogicParam(logicParams, "glassGap", 200); // 玻璃之间的物理间隔(mm),默认200mm
|
List<GlassInfo> batch = assembleBatch(buffer, config.getVehicleCapacity(), glassGap);
|
if (batch.isEmpty()) {
|
return buildResult(deviceConfig, "checkAndProcess", false,
|
"无法组装有效批次(容量不足)");
|
}
|
|
// 6. 写入PLC(尝试从任务参数中获取卧转立编号)
|
DevicePlcVO.OperationResult writeResult = writeBatchToPlc(
|
deviceConfig, batch, serializer, logicParams, params);
|
|
if (!Boolean.TRUE.equals(writeResult.getSuccess())) {
|
return writeResult;
|
}
|
|
// 卧转立批次已成功写入PLC,将本批次玻璃ID写入任务上下文,供大车进片使用
|
try {
|
if (params != null) {
|
Object ctxObj = params.get("_taskContext");
|
if (ctxObj instanceof com.mes.task.model.TaskExecutionContext) {
|
com.mes.task.model.TaskExecutionContext ctx =
|
(com.mes.task.model.TaskExecutionContext) ctxObj;
|
List<String> batchGlassIds = batch.stream()
|
.map(GlassInfo::getGlassId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toList());
|
if (!batchGlassIds.isEmpty()) {
|
ctx.getSharedData().put("transferReadyGlassIds",
|
new java.util.ArrayList<>(batchGlassIds));
|
log.info("卧转立已输出批次玻璃到任务上下文: deviceId={}, glassIds={}",
|
deviceConfig.getId(), batchGlassIds);
|
}
|
}
|
}
|
} catch (Exception e) {
|
log.warn("卧转立写入任务上下文transferReadyGlassIds失败: deviceId={}", deviceConfig.getId(), e);
|
}
|
|
// 7. 从缓冲队列中移除已处理的玻璃并更新状态
|
removeProcessedGlasses(deviceId, batch);
|
glassInfoService.updateGlassStatus(
|
batch.stream().map(GlassInfo::getGlassId).collect(Collectors.toList()),
|
GlassInfo.Status.PROCESSED);
|
|
// 8. 检查缓冲是否为空,如果为空且无新玻璃,标记为完成
|
List<GlassBufferItem> remainingBuffer = glassBuffer.get(deviceId);
|
boolean bufferEmpty = remainingBuffer == null || remainingBuffer.isEmpty();
|
boolean noNewGlass = !hasNewGlass;
|
|
String msg;
|
if (bufferEmpty && noNewGlass) {
|
// 缓冲已清空且无新玻璃,任务完成
|
msg = String.format("批次已写入PLC: glassCount=%d, glassIds=%s, 缓冲已清空,任务完成",
|
batch.size(),
|
batch.stream().map(GlassInfo::getGlassId).collect(Collectors.joining(",")));
|
} else {
|
// 缓冲还有玻璃或可能有新玻璃,继续运行
|
msg = String.format("批次已写入PLC: glassCount=%d, glassIds=%s",
|
batch.size(),
|
batch.stream().map(GlassInfo::getGlassId).collect(Collectors.joining(",")));
|
}
|
return buildResult(deviceConfig, "checkAndProcess", true, msg);
|
|
} catch (Exception e) {
|
log.error("检查并处理玻璃批次异常: deviceId={}", deviceId, e);
|
return buildResult(deviceConfig, "checkAndProcess", false,
|
"处理异常: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 查询最近扫码的玻璃信息
|
*/
|
private List<GlassInfo> queryRecentScannedGlasses(
|
DeviceConfig deviceConfig,
|
Map<String, Object> logicParams) {
|
|
if (glassInfoMapper == null) {
|
log.warn("GlassInfoMapper未注入,无法查询最近扫码的玻璃");
|
return Collections.emptyList();
|
}
|
|
try {
|
// 从配置中获取workLine,用于过滤(配置中是Integer类型)
|
Integer workLine = getLogicParam(logicParams, "workLine", null);
|
|
// 查询state=1的玻璃记录(已扫码交互完成,等待卧转立处理)
|
LambdaQueryWrapper<GlassInfo> wrapper = new LambdaQueryWrapper<>();
|
wrapper.in(GlassInfo::getStatus, GlassInfo.Status.PENDING, GlassInfo.Status.ACTIVE)
|
.eq(GlassInfo::getState, 1) // 只查询state=1的玻璃(已扫码完成)
|
.orderByDesc(GlassInfo::getCreatedTime)
|
.last("LIMIT 20"); // 限制查询数量,避免过多
|
|
// 如果配置了workLine,则过滤work_line字段
|
if (workLine != null) {
|
wrapper.eq(GlassInfo::getWorkLine, workLine);
|
}
|
|
List<GlassInfo> recentGlasses = glassInfoMapper.selectList(wrapper);
|
|
log.debug("查询到最近扫码的玻璃: deviceId={}, workLine={}, count={}",
|
deviceConfig.getId(), workLine, recentGlasses.size());
|
|
return recentGlasses;
|
|
} catch (Exception e) {
|
log.error("查询最近扫码的玻璃信息异常: deviceId={}",
|
deviceConfig.getId(), e);
|
return Collections.emptyList();
|
}
|
}
|
|
/**
|
* 更新缓冲队列
|
* @return 是否有新的玻璃被加入缓冲(用于判断是否刷新 lastScanTime)
|
*/
|
private boolean updateBuffer(String deviceId, List<GlassInfo> newGlasses) {
|
List<GlassBufferItem> buffer = glassBuffer.computeIfAbsent(
|
deviceId, k -> new CopyOnWriteArrayList<>());
|
|
Set<String> existingIds = buffer.stream()
|
.map(item -> item.glassInfo.getGlassId())
|
.collect(Collectors.toSet());
|
|
boolean hasNewGlass = false;
|
for (GlassInfo glass : newGlasses) {
|
if (!existingIds.contains(glass.getGlassId())) {
|
buffer.add(new GlassBufferItem(glass, System.currentTimeMillis()));
|
hasNewGlass = true;
|
log.debug("添加玻璃到缓冲队列: deviceId={}, glassId={}",
|
deviceId, glass.getGlassId());
|
}
|
}
|
return hasNewGlass;
|
}
|
|
/**
|
* 判断是否应该处理批次
|
* 注意:这里只做粗略判断,精确的容量计算(含间隙)在assembleBatch中完成
|
*/
|
private boolean shouldProcessBatch(String deviceId,
|
List<GlassBufferItem> buffer,
|
WorkstationLogicConfig config) {
|
// 条件1:缓冲队列已满(达到容量限制)
|
// 粗略计算:所有玻璃长度之和(不考虑间隙,因为间隙是动态的)
|
int totalLength = buffer.stream()
|
.mapToInt(item -> item.glassInfo.getGlassLength() != null ?
|
item.glassInfo.getGlassLength() : 0)
|
.sum();
|
// 粗略判断:如果总长度接近容量(留一些余量给间隙),就触发处理
|
// 精确判断会在assembleBatch中完成
|
if (totalLength >= config.getVehicleCapacity() * 0.8) { // 80%阈值,留余量给间隙
|
log.info("缓冲队列容量接近满载,触发批次处理: deviceId={}, totalLength={}, capacity={}",
|
deviceId, totalLength, config.getVehicleCapacity());
|
return true;
|
}
|
|
// 条件2:30s内无新玻璃扫码
|
AtomicLong lastTime = lastScanTime.get(deviceId);
|
if (lastTime != null) {
|
long elapsed = System.currentTimeMillis() - lastTime.get();
|
if (elapsed >= config.getTransferDelayMs()) {
|
log.info("30s内无新玻璃扫码,触发批次处理: deviceId={}, elapsed={}ms",
|
deviceId, elapsed);
|
return true;
|
}
|
}
|
|
return false;
|
}
|
|
/**
|
* 组装批次(容量判断,考虑玻璃间隙)
|
* @param buffer 缓冲队列
|
* @param vehicleCapacity 车辆容量(mm)
|
* @param glassGap 玻璃之间的物理间隔(mm),默认200mm
|
* @return 组装好的批次列表
|
*/
|
private List<GlassInfo> assembleBatch(List<GlassBufferItem> buffer,
|
int vehicleCapacity,
|
int glassGap) {
|
List<GlassInfo> batch = new ArrayList<>();
|
int usedLength = 0;
|
int gap = Math.max(glassGap, 0); // 确保间隔不为负数
|
|
for (GlassBufferItem item : buffer) {
|
GlassInfo glass = item.glassInfo;
|
int glassLength = glass.getGlassLength() != null ?
|
glass.getGlassLength() : 0;
|
|
if (glassLength <= 0) {
|
continue; // 跳过无效长度的玻璃
|
}
|
|
if (batch.isEmpty()) {
|
// 第一块玻璃,不需要间隙
|
if (glassLength <= vehicleCapacity && batch.size() < 6) {
|
batch.add(glass);
|
usedLength = glassLength;
|
} else {
|
break; // 第一块就装不下
|
}
|
} else {
|
// 后续玻璃需要考虑间隙:玻璃长度 + 间隙
|
int requiredLength = glassLength + gap;
|
if (usedLength + requiredLength <= vehicleCapacity && batch.size() < 6) {
|
batch.add(glass);
|
usedLength += requiredLength; // 包含间隙
|
} else {
|
break; // 装不下了
|
}
|
}
|
}
|
|
log.debug("批次组装完成: batchSize={}, usedLength={}, capacity={}, glassGap={}",
|
batch.size(), usedLength, vehicleCapacity, gap);
|
|
return batch;
|
}
|
|
/**
|
* 写入批次到PLC
|
*/
|
private DevicePlcVO.OperationResult writeBatchToPlc(
|
DeviceConfig deviceConfig,
|
List<GlassInfo> batch,
|
EnhancedS7Serializer serializer,
|
Map<String, Object> logicParams,
|
Map<String, Object> params) {
|
|
Map<String, Object> payload = new HashMap<>();
|
|
// 写入玻璃ID(最多6个)
|
int count = Math.min(batch.size(), 6);
|
for (int i = 0; i < count; i++) {
|
String fieldName = "plcGlassId" + (i + 1);
|
payload.put(fieldName, batch.get(i).getGlassId());
|
}
|
|
// 写入玻璃数量
|
payload.put("plcGlassCount", count);
|
|
// 写入卧转立编号(优先从任务参数获取,其次从设备配置获取,直接写入编号,不进行位置映射)
|
Object inPosition = null;
|
if (params != null) {
|
try {
|
Object ctxObj = params.get("_taskContext");
|
if (ctxObj instanceof com.mes.task.model.TaskExecutionContext) {
|
com.mes.task.model.TaskExecutionContext ctx =
|
(com.mes.task.model.TaskExecutionContext) ctxObj;
|
inPosition = ctx.getParameters().getExtra() != null
|
? ctx.getParameters().getExtra().get("inPosition") : null;
|
}
|
} catch (Exception e) {
|
log.debug("从任务参数获取卧转立编号失败: deviceId={}", deviceConfig.getId(), e);
|
}
|
}
|
// 如果任务参数中没有,从设备配置中获取
|
if (inPosition == null) {
|
inPosition = getLogicParam(logicParams, "inPosition", null);
|
}
|
if (inPosition != null) {
|
// 直接写入编号本身,不进行位置映射转换
|
payload.put("inPosition", inPosition);
|
log.info("写入卧转立编号: deviceId={}, inPosition={}", deviceConfig.getId(), inPosition);
|
} else {
|
log.debug("未配置卧转立编号,跳过写入: deviceId={}", deviceConfig.getId());
|
}
|
|
// 写入请求字(触发大车)
|
payload.put("plcRequest", 1);
|
|
try {
|
plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
|
log.info("批次已写入PLC: deviceId={}, glassCount={}, inPosition={}",
|
deviceConfig.getId(), count, inPosition);
|
return buildResult(deviceConfig, "writeBatchToPlc", true,
|
"批次写入成功");
|
} catch (Exception e) {
|
log.error("写入批次到PLC失败: deviceId={}", deviceConfig.getId(), e);
|
return buildResult(deviceConfig, "writeBatchToPlc", false,
|
"写入失败: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 从缓冲队列移除已处理的玻璃
|
*/
|
private void removeProcessedGlasses(String deviceId, List<GlassInfo> processed) {
|
List<GlassBufferItem> buffer = glassBuffer.get(deviceId);
|
if (buffer == null) {
|
return;
|
}
|
|
Set<String> processedIds = processed.stream()
|
.map(GlassInfo::getGlassId)
|
.collect(Collectors.toSet());
|
|
buffer.removeIf(item -> processedIds.contains(item.glassInfo.getGlassId()));
|
}
|
|
/**
|
* 启动监控任务(定期检查并处理)
|
*/
|
private DevicePlcVO.OperationResult handleStartMonitor(
|
DeviceConfig deviceConfig,
|
WorkstationLogicConfig config,
|
Map<String, Object> logicParams) {
|
|
String deviceId = deviceConfig.getDeviceId();
|
|
// 停止旧的监控任务
|
handleStopMonitor(deviceConfig);
|
|
// 获取监控间隔
|
Integer monitorIntervalMs = getLogicParam(logicParams, "monitorIntervalMs",
|
config.getScanIntervalMs());
|
|
// 启动监控任务
|
ScheduledFuture<?> future = monitorExecutor.scheduleWithFixedDelay(() -> {
|
try {
|
// 监控任务不在多设备任务上下文中运行,这里不需要传入 params/_taskContext
|
handleCheckAndProcess(deviceConfig, config, logicParams, null);
|
} catch (Exception e) {
|
log.error("监控任务执行异常: deviceId={}", deviceId, e);
|
}
|
}, monitorIntervalMs, monitorIntervalMs, TimeUnit.MILLISECONDS);
|
|
monitorTasks.put(deviceId, future);
|
log.info("已启动卧转立监控任务: deviceId={}, interval={}ms",
|
deviceId, monitorIntervalMs);
|
|
return buildResult(deviceConfig, "startMonitor", true,
|
"监控任务已启动");
|
}
|
|
/**
|
* 停止监控任务
|
*/
|
private DevicePlcVO.OperationResult handleStopMonitor(DeviceConfig deviceConfig) {
|
String deviceId = deviceConfig.getDeviceId();
|
ScheduledFuture<?> future = monitorTasks.remove(deviceId);
|
if (future != null && !future.isCancelled()) {
|
future.cancel(false);
|
log.info("已停止卧转立监控任务: deviceId={}", deviceId);
|
}
|
return buildResult(deviceConfig, "stopMonitor", true, "监控任务已停止");
|
}
|
|
/**
|
* 清空缓冲队列
|
*/
|
private DevicePlcVO.OperationResult handleClearBuffer(DeviceConfig deviceConfig) {
|
String deviceId = deviceConfig.getDeviceId();
|
glassBuffer.remove(deviceId);
|
lastScanTime.remove(deviceId);
|
log.info("已清空缓冲队列: deviceId={}", deviceId);
|
return buildResult(deviceConfig, "clearBuffer", true, "缓冲队列已清空");
|
}
|
|
/**
|
* 清空PLC相关字段(供测试页面一键清空使用)
|
*/
|
private DevicePlcVO.OperationResult handleClearPlc(DeviceConfig deviceConfig) {
|
try {
|
EnhancedS7Serializer serializer = s7SerializerProvider.getSerializer(deviceConfig);
|
if (serializer == null) {
|
return buildResult(deviceConfig, "clearPlc", false, "获取PLC序列化器失败");
|
}
|
|
Map<String, Object> payload = new HashMap<>();
|
// 根据卧转立主体写入的字段进行清空
|
for (int i = 1; i <= 6; i++) {
|
payload.put("plcGlassId" + i, "");
|
}
|
payload.put("plcGlassCount", 0);
|
payload.put("plcRequest", 0);
|
payload.put("inPosition", 0);
|
|
plcDynamicDataService.writePlcData(deviceConfig, payload, serializer);
|
log.info("卧转立主体清空PLC字段完成: deviceId={}", deviceConfig.getId());
|
return buildResult(deviceConfig, "clearPlc", true, "已清空卧转立主体PLC字段");
|
} catch (Exception e) {
|
log.error("卧转立主体清空PLC失败: deviceId={}", deviceConfig.getId(), e);
|
return buildResult(deviceConfig, "clearPlc", false, "清空PLC失败: " + e.getMessage());
|
}
|
}
|
|
/**
|
* 构建操作结果
|
*/
|
private DevicePlcVO.OperationResult buildResult(DeviceConfig deviceConfig,
|
String operation,
|
boolean success,
|
String message) {
|
return DevicePlcVO.OperationResult.builder()
|
.deviceId(deviceConfig.getId())
|
.deviceName(deviceConfig.getDeviceName())
|
.deviceCode(deviceConfig.getDeviceCode())
|
.projectId(deviceConfig.getProjectId() != null ?
|
String.valueOf(deviceConfig.getProjectId()) : null)
|
.operation(operation)
|
.success(success)
|
.message(message)
|
.timestamp(LocalDateTime.now())
|
.build();
|
}
|
|
/**
|
* 应用关闭时清理资源
|
*/
|
@PreDestroy
|
public void destroy() {
|
log.info("正在关闭卧转立监控线程池...");
|
|
// 停止所有监控任务
|
for (String deviceId : new ArrayList<>(monitorTasks.keySet())) {
|
ScheduledFuture<?> future = monitorTasks.remove(deviceId);
|
if (future != null && !future.isCancelled()) {
|
future.cancel(false);
|
}
|
}
|
|
// 关闭线程池
|
monitorExecutor.shutdown();
|
try {
|
if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
|
monitorExecutor.shutdownNow();
|
if (!monitorExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
|
log.warn("卧转立监控线程池未能正常关闭");
|
}
|
}
|
} catch (InterruptedException e) {
|
monitorExecutor.shutdownNow();
|
Thread.currentThread().interrupt();
|
}
|
|
log.info("卧转立监控线程池已关闭");
|
}
|
|
/**
|
* 玻璃缓冲项
|
*/
|
private static class GlassBufferItem {
|
final GlassInfo glassInfo;
|
final long timestamp;
|
|
GlassBufferItem(GlassInfo glassInfo, long timestamp) {
|
this.glassInfo = glassInfo;
|
this.timestamp = timestamp;
|
}
|
}
|
}
|