package jnpf.task;
import jnpf.entity.EquipmentparameterEntity;
import jnpf.entity.UleabnormalrecordsEntity;
import jnpf.myutils.DateUtils;
import jnpf.service.*;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.;
import java.time.format.DateTimeFormatter;
import java.util.;
import java.util.stream.Collectors;
@Slf4j
@Component
public class HAHFAnalysisTask {
private static final Logger logger = LoggerFactory.getLogger(HAHFAnalysisTask.class); private static final int BUCKET_SIZE = 6; // 6小时分桶 private static final int MIN_DATA_POINTS = 4; // 最小数据点数 private static final int HOURS_IN_DAY = 24; // 一天的小时数 private static final int RECOVERY_WINDOW_HOURS = 24; // 恢复判断窗口小时数 @Autowired private EquipmentparameterService equipmentparameterService; @Autowired private UleabnormalrecordsService uleabnormalrecordsService; @Autowired private RealDataService realDataService; @Autowired private EquipmentledgerService equipmentledgerService; @Autowired private ProcessmanageService processmanageService; DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //@XxlJob("hahfAnalysis") public void hahfAnalysis() { logger.info("HAHF分析任务启动"); // 1. 获取TSP设备列表 List<EquipmentparameterEntity> equipmentList = equipmentparameterService.geHAHFList(); if (equipmentList.isEmpty()) { logger.info("没有可分析的TSP设备"); return; } // 2. 准备查询参数 Map<String, Object> params = new HashMap<>(); List<String> tags = equipmentList.stream() .map(EquipmentparameterEntity::getTag) .collect(Collectors.toList()); params.put("tags", tags); // 3. 获取日常值数据 params.put("begindate", DateUtils.dataTimeformat(new Date())); List<Map> dailyValue = realDataService.selectLastmonthDailyvalue(params); Map<String, Map<String, Object>> dailyValueMap = dailyValue.stream() .collect(Collectors.toMap( m -> (String) m.get("datag"), m -> m )); // 4. 获取小时数据(从前一天开始时间到当前时间) LocalDateTime endTime = LocalDateTime.now(); // 获取前一天开始时间(00:00:00) LocalDateTime startTime = LocalDate.now().minusDays(1).atStartOfDay(); params.put("begindate", formatDateTime(startTime)); params.put("enddate", formatDateTime(endTime)); List<Map> hourDataMaps = realDataService.selectHourdataByTagIdsAndDate(params); // 5. 按tag分组小时数据 Map<String, List<Map>> hourDataByTag = hourDataMaps.stream() .collect(Collectors.groupingBy( m -> (String) m.get("datag") )); // 6. 获取当前报警中的记录 List<UleabnormalrecordsEntity> activeAlarms = uleabnormalrecordsService.getOnhahfList(); Map<String, UleabnormalrecordsEntity> activeAlarmMap = activeAlarms.stream() .collect(Collectors.toMap( UleabnormalrecordsEntity::getParacode, alarm -> alarm )); // 7. 处理每个设备 for (EquipmentparameterEntity equipment : equipmentList) { try { processEquipment( equipment, dailyValueMap, hourDataByTag, activeAlarmMap, startTime, endTime ); } catch (Exception e) { logger.error("处理设备{}失败: {}", equipment.getEncode(), e.getMessage(), e); } } logger.info("HAHF分析任务完成,共处理{}个设备", equipmentList.size()); } private void processEquipment(EquipmentparameterEntity equipment, Map<String, Map<String, Object>> dailyValueMap, Map<String, List<Map>> hourDataByTag, Map<String, UleabnormalrecordsEntity> activeAlarmMap, LocalDateTime analysisStart, LocalDateTime analysisEnd) { String tag = equipment.getTag(); String paraCode = equipment.getEncode(); // 获取该tag的日常值配置 Map<String, Object> dailyConfig = dailyValueMap.get(tag); if (dailyConfig == null) { logger.warn("设备{}缺少日常值", paraCode); return; } double farBeyondValue = ((Number) dailyConfig.get("farbeyondvalue")).doubleValue(); double limitingValue = ((Number) dailyConfig.get("limitingvalue")).doubleValue(); // 获取该tag的小时数据 List<Map> hourDataList = hourDataByTag.getOrDefault(tag, new ArrayList<>()); // 无小时数据直接返回 if (hourDataList.isEmpty()) { logger.info("设备{}没有小时数据", paraCode); return; } // 准备时间序列数据 List<DataPoint> dataPoints = prepareDataPoints(hourDataList); // 按时间排序 dataPoints.sort(Comparator.comparing(DataPoint::getDateTime)); // 获取最后数据点的时间和值 DataPoint lastDataPoint = dataPoints.get(dataPoints.size() - 1); LocalDateTime lastDataTime = lastDataPoint.dateTime; double lastDataValue = lastDataPoint.value; // 添加调试信息 logger.debug("设备{}有{}个数据点,时间范围: {} 到 {},最后数据点: {}={}", paraCode, dataPoints.size(), dataPoints.get(0).dateTime, dataPoints.get(dataPoints.size()-1).dateTime, lastDataTime, lastDataValue); // 设备有报警中记录 - 无论阈值条件是否满足,都需要检查恢复 if (activeAlarmMap.containsKey(paraCode)) { handleActiveAlarm( activeAlarmMap.get(paraCode), dataPoints, farBeyondValue, limitingValue, analysisEnd, lastDataTime, lastDataValue ); } // 设备无报警中记录 - 只有满足阈值条件时才处理 else { // 检查是否满足报警条件 boolean isValidForAlarm = isValidForAlarm(dataPoints, farBeyondValue, limitingValue); if (isValidForAlarm) { handleNewAlarm( equipment, dataPoints, farBeyondValue, limitingValue, analysisEnd, lastDataTime, lastDataValue ); } else { logger.info("设备{}数据不满足报警条件", paraCode); } } } // ===================== 报警处理核心逻辑 ===================== /** * 处理新报警检测 */ private void handleNewAlarm(EquipmentparameterEntity equipment, List<DataPoint> dataPoints, double farBeyondValue, double limitingValue, LocalDateTime analysisEnd, LocalDateTime lastDataTime, double lastDataValue) { // 寻找所有符合条件的连续时间段 List<TimePeriod> validPeriods = findValidPeriods(dataPoints, farBeyondValue, limitingValue, lastDataTime, analysisEnd); if (validPeriods.isEmpty()) { logger.info("设备{}满足阈值条件但未找到有效时间段", equipment.getEncode()); return; } // 合并连续的时间段 List<TimePeriod> mergedPeriods = mergeContinuousPeriods(validPeriods); // 创建报警记录 for (TimePeriod period : mergedPeriods) { // 确定实际报警开始和结束时间 LocalDateTime actualStart = findActualStartTime(dataPoints, period.start, farBeyondValue, limitingValue); LocalDateTime actualEnd = findActualEndTime(dataPoints, period.end, farBeyondValue, limitingValue, analysisEnd); UleabnormalrecordsEntity uar = uleabnormalrecordsService.getInfoByParacodeAbnormalcode(equipment.getEncode(),7 ); if(uar == null||DateUtils.betweenseconds(df.format(uar.getCompletetime()), actualStart.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))>0){ createAlarmRecord(equipment, actualStart, actualEnd); } } } /** * 处理活跃报警 */ private void handleActiveAlarm(UleabnormalrecordsEntity activeAlarm, List<DataPoint> dataPoints, double farBeyondValue, double limitingValue, LocalDateTime analysisEnd, LocalDateTime lastDataTime, double lastDataValue) { LocalDateTime alarmStartTime = toLocalDateTime(activeAlarm.getAbnormaltime()); // 检查报警是否恢复:连续24小时不满足条件才算恢复 LocalDateTime recoveryTime = checkRecovery( dataPoints, alarmStartTime, farBeyondValue, limitingValue, analysisEnd ); // 情况1: 报警恢复 if (recoveryTime != null) { completeAlarm(activeAlarm, recoveryTime); // 检查恢复后是否再次触发报警 List<DataPoint> postRecoveryData = dataPoints.stream() .filter(point -> point.dateTime.isAfter(recoveryTime)) .collect(Collectors.toList()); if (!postRecoveryData.isEmpty()) { DataPoint lastPostDataPoint = postRecoveryData.get(postRecoveryData.size() - 1); if (isValidForAlarm(postRecoveryData, farBeyondValue, limitingValue)) { EquipmentparameterEntity equipment = new EquipmentparameterEntity(); equipment.setEncode(activeAlarm.getParacode()); equipment.setTag(activeAlarm.getParacode()); equipment.setEquipmentledgerid(activeAlarm.getEquipmentid()); equipment.setFullname(activeAlarm.getParaname()); handleNewAlarm( equipment, postRecoveryData, farBeyondValue, limitingValue, analysisEnd, lastPostDataPoint.dateTime, lastPostDataPoint.value ); } } } // 情况2: 报警持续中 else { // 检查最后一个数据点是否仍超过阈值,确保报警状态正确 if (lastDataValue >= limitingValue) { logger.info("报警{}仍在持续中,已持续{}小时", activeAlarm.getId(), Duration.between(alarmStartTime, LocalDateTime.now()).toHours()); } else { // 最后一个点低于阈值但未满足恢复条件,可能是短暂波动 logger.info("报警{}最后数据点低于阈值但未确认恢复,继续观察", activeAlarm.getId()); } } } // ===================== 报警检测算法 ===================== /** * 核心算法:寻找有效时间段 */ private List<TimePeriod> findValidPeriods(List<DataPoint> dataPoints, double farBeyondValue, double limitingValue, LocalDateTime lastDataTime, LocalDateTime analysisEnd) { List<TimePeriod> validPeriods = new ArrayList<>(); if (dataPoints.size() < MIN_DATA_POINTS) { return validPeriods; } // 按日期和时间排序 dataPoints.sort(Comparator.comparing(DataPoint::getDateTime)); // 使用滑动窗口检测24小时时间段 int i = 0; while (i < dataPoints.size()) { LocalDateTime windowStart = dataPoints.get(i).dateTime; LocalDateTime windowEnd = windowStart.plusHours(HOURS_IN_DAY); // 获取窗口内的数据点 List<DataPoint> windowData = new ArrayList<>(); int j = i; while (j < dataPoints.size()) { DataPoint point = dataPoints.get(j); if (point.dateTime.isBefore(windowEnd)) { windowData.add(point); j++; } else { break; } } // 检查窗口有效性 if (isValidPeriod(windowData, farBeyondValue, limitingValue, windowStart, windowEnd)) { // 确定时间段结束时间 LocalDateTime periodEnd; // 如果窗口结束时间在最后数据时间之后,且最后数据仍超过阈值,则结束时间为null(持续中) if (windowEnd.isAfter(lastDataTime) && lastDataTime.isAfter(windowStart) && dataPoints.get(dataPoints.size() - 1).value >= limitingValue) { periodEnd = null; } else { periodEnd = windowEnd; } validPeriods.add(new TimePeriod(windowStart, periodEnd)); // 移动到窗口结束位置,避免重复检测 i = j; } else { i++; } } return validPeriods; } /** * 检查24小时窗口是否有效 */ private boolean isValidPeriod(List<DataPoint> dataPoints, double farBeyondValue, double limitingValue, LocalDateTime windowStart, LocalDateTime windowEnd) { if (dataPoints.isEmpty()) { return false; } // 检查时间跨度是否足够(至少24小时) Duration duration = Duration.between(windowStart, windowEnd); if (duration.toHours() < HOURS_IN_DAY) { return false; } // 检查限制值条件 - 窗口内所有数据点都要大于等于limitingValue boolean allAboveLimiting = dataPoints.stream() .allMatch(point -> point.value >= limitingValue); if (!allAboveLimiting) { return false; } // 按6小时分桶组织数据 Map<Integer, List<DataPoint>> bucketMap = new HashMap<>(); for (DataPoint point : dataPoints) { int bucketIdx = point.dateTime.getHour() / BUCKET_SIZE; bucketMap.computeIfAbsent(bucketIdx, k -> new ArrayList<>()).add(point); } // 检查每个桶是否满足条件:每个6小时桶至少有一个点远高于日常值 for (int bucketIdx = 0; bucketIdx < 4; bucketIdx++) { List<DataPoint> bucketData = bucketMap.getOrDefault(bucketIdx, new ArrayList<>()); // 桶内无数据或没有有效点 if (bucketData.isEmpty() || bucketData.stream().noneMatch(point -> point.value >= farBeyondValue)) { return false; } } return true; } /** * 检查报警恢复:需要连续24小时不满足报警条件 */ private LocalDateTime checkRecovery(List<DataPoint> dataPoints, LocalDateTime startTime, double farBeyondValue, double limitingValue, LocalDateTime analysisEnd) { if (dataPoints.isEmpty()) { return null; } // 过滤出报警开始时间之后的数据 List<DataPoint> postAlarmData = dataPoints.stream() .filter(point -> !point.dateTime.isBefore(startTime)) .collect(Collectors.toList()); if (postAlarmData.isEmpty()) { return null; } // 按时间排序 postAlarmData.sort(Comparator.comparing(DataPoint::getDateTime)); // 预先构建分桶数据结构 Map<BucketKey, BucketData> bucketMap = new HashMap<>(); for (DataPoint point : postAlarmData) { LocalDate date = point.dateTime.toLocalDate(); int bucketIndex = point.dateTime.getHour() / 6; // 0-3的桶索引 BucketKey key = new BucketKey(date, bucketIndex); BucketData bucket = bucketMap.computeIfAbsent(key, k -> new BucketData()); bucket.points.add(point); if (point.value >= farBeyondValue) { bucket.hasFarBeyond = true; bucket.lastFarBeyondTime = point.dateTime; } if (bucket.firstPoint == null || point.dateTime.isBefore(bucket.firstPoint.dateTime)) { bucket.firstPoint = point; } } // 检查是否有连续24小时不满足条件 for (int i = 0; i < postAlarmData.size(); i++) { LocalDateTime windowStart = postAlarmData.get(i).dateTime; LocalDateTime windowEnd = windowStart.plusHours(RECOVERY_WINDOW_HOURS); // 如果窗口超出分析结束时间,则调整窗口结束时间 if (windowEnd.isAfter(analysisEnd)) { windowEnd = analysisEnd; } // 获取窗口内的数据点 List<DataPoint> windowData = new ArrayList<>(); for (int j = i; j < postAlarmData.size(); j++) { DataPoint point = postAlarmData.get(j); if (point.dateTime.isBefore(windowEnd)) { windowData.add(point); } else { break; } } // 检查窗口是否满足恢复条件:不满足报警条件 if (!isValidPeriod(windowData, farBeyondValue, limitingValue, windowStart, windowEnd)) { // 找到第一个不满足条件的点作为恢复开始时间 for (int k = 0; k < windowData.size(); k++) { DataPoint point = windowData.get(k); // 条件1: 当前点低于limitingValue if (point.value < limitingValue) { return point.dateTime; } // 条件2: 检查当前桶是否满足farBeyond条件 LocalDate date = point.dateTime.toLocalDate(); int bucketIndex = point.dateTime.getHour() / 6; BucketKey key = new BucketKey(date, bucketIndex); BucketData bucket = bucketMap.get(key); // 桶不存在或没有farBeyond点 if (bucket == null || !bucket.hasFarBeyond) { // 第一桶(0-6点)直接返回桶首点时间 if (bucketIndex == 0) { return bucket != null && bucket.firstPoint != null ? bucket.firstPoint.dateTime : point.dateTime; } // 非第一桶返回上一桶最后farBeyond时间+1小时 else { // 计算上一桶的键 BucketKey prevKey; if (bucketIndex == 0) { // 跨天处理 prevKey = new BucketKey(date.minusDays(1), 3); } else { prevKey = new BucketKey(date, bucketIndex - 1); } BucketData prevBucket = bucketMap.get(prevKey); if (prevBucket != null && prevBucket.lastFarBeyondTime != null) { return prevBucket.lastFarBeyondTime.plusHours(1); } else { return bucket != null && bucket.firstPoint != null ? bucket.firstPoint.dateTime : point.dateTime; } } } } // 如果所有点都高于limitingValue但不满足其他条件,取窗口第一个点时间 return windowData.get(0).dateTime; } } return null; } /** * 检查是否满足报警条件 */ private boolean isValidForAlarm(List<DataPoint> dataPoints, double farBeyondValue, double limitingValue) { if (dataPoints.size() < MIN_DATA_POINTS) { return false; } // 检查是否有至少24小时的连续数据满足条件 for (int i = 0; i < dataPoints.size(); i++) { LocalDateTime windowStart = dataPoints.get(i).dateTime; LocalDateTime windowEnd = windowStart.plusHours(HOURS_IN_DAY); // 获取窗口内的数据点 List<DataPoint> windowData = new ArrayList<>(); for (int j = i; j < dataPoints.size(); j++) { DataPoint point = dataPoints.get(j); if (point.dateTime.isBefore(windowEnd)) { windowData.add(point); } else { break; } } if (isValidPeriod(windowData, farBeyondValue, limitingValue, windowStart, windowEnd)) { return true; } } return false; } // ===================== 报警记录管理 ===================== /** * 创建报警记录 */ private void createAlarmRecord(EquipmentparameterEntity equipment, LocalDateTime startTime, LocalDateTime endTime) { try { UleabnormalrecordsEntity record = new UleabnormalrecordsEntity(); record.setParacode(equipment.getEncode()); record.setAbnormaltime(toDate(startTime)); record.setCompletetime(toDate(endTime)); // 计算持续时间(分钟) if (endTime != null) { long durationSeconds = Duration.between(startTime, endTime).getSeconds(); record.setDuration((int) (durationSeconds / 60)); } record.setAbnormaltype("700229026538590853"); record.setAbnormalstate(endTime == null ? "0" : "1"); // 0-报警中, 1-已结束 String processName = getProcessname(equipment.getEquipmentledgerid()); String equipName = getEquipname(equipment.getEquipmentledgerid()); record.setAbnormaldescribe( "【" + processName + "】工序-【" + equipName + "】设备-【" + equipment.getFullname() + "】参数,于(" + df.format(record.getAbnormaltime()) + ")出现高频高幅事件" ); record.setIsdispatch(equipment.getIsdispatch()); record.setProcess(equipmentledgerService.getInfo(equipment.getEquipmentledgerid()).getProcesscode()); record.setEquipmentid(equipment.getEquipmentledgerid()); record.setEquipmenttype(equipmentledgerService.getInfo(equipment.getEquipmentledgerid()).getEquipmenttype()); record.setEquipmentcode(equipmentledgerService.getInfo(equipment.getEquipmentledgerid()).getDevicecode()); record.setParacode(equipment.getEncode()); record.setParaname(equipment.getFullname()); record.setAbnormalcode("7"); record.setCreatortime(new Date()); record.setCreatoruserid("349057407209541"); uleabnormalrecordsService.save(record); if (endTime == null) { logger.info("创建新报警记录: {} - 开始时间: {} (持续中)", equipment.getEncode(), startTime); } else { logger.info("创建完整报警记录: {} - 开始: {}, 结束: {} (持续{}小时)", equipment.getEncode(), startTime, endTime, Duration.between(startTime, endTime).toHours()); } } catch (Exception e) { logger.error("创建报警记录失败: {}", e.getMessage(), e); } } /** * 完成报警处理 */ private void completeAlarm(UleabnormalrecordsEntity alarm, LocalDateTime recoveryTime) { try { alarm.setCompletetime(toDate(recoveryTime)); LocalDateTime startTime = toLocalDateTime(alarm.getAbnormaltime()); long durationSeconds = Duration.between(startTime, recoveryTime).getSeconds(); alarm.setDuration((int) (durationSeconds / 60)); alarm.setAbnormalstate("1"); alarm.setLastmodifytime(new Date()); alarm.setLastmodifyuserid("349057407209541"); uleabnormalrecordsService.updateById(alarm); long durationHours = Duration.between(startTime, recoveryTime).toHours(); logger.info("报警{}已恢复,开始时间: {},结束时间: {},持续小时: {}", alarm.getId(), startTime, recoveryTime, durationHours); } catch (Exception e) { logger.error("完成报警处理失败: {}", e.getMessage(), e); } } // ===================== 辅助方法 ===================== private List<DataPoint> prepareDataPoints(List<Map> hourDataList) { return hourDataList.stream() .map(map -> new DataPoint( (String) map.get("datadt"), ((Number) map.get("avgdata")).doubleValue() )) .collect(Collectors.toList()); } private LocalDateTime findActualStartTime(List<DataPoint> dataPoints, LocalDateTime windowStart, double farBeyondValue, double limitingValue) { // 向前查找实际的异常开始时间 for (DataPoint point : dataPoints) { if (point.dateTime.isAfter(windowStart)) { break; } if (point.value >= limitingValue) { return point.dateTime; } } return windowStart; } private LocalDateTime findActualEndTime(List<DataPoint> dataPoints, LocalDateTime windowEnd, double farBeyondValue, double limitingValue, LocalDateTime analysisEnd) { if (windowEnd == null) { return null; } // 按时间排序数据点 dataPoints.sort(Comparator.comparing(DataPoint::getDateTime)); // 2. 预先构建分桶数据结构 Map<BucketKey, BucketData> bucketMap = new HashMap<>(); for (DataPoint point : dataPoints) { LocalDate date = point.dateTime.toLocalDate(); int bucketIndex = point.dateTime.getHour() / 6; // 0-3的桶索引 BucketKey key = new BucketKey(date, bucketIndex); BucketData bucket = bucketMap.computeIfAbsent(key, k -> new BucketData()); bucket.points.add(point); if (point.value >= farBeyondValue) { bucket.hasFarBeyond = true; bucket.lastFarBeyondTime = point.dateTime; } if (bucket.firstPoint == null || point.dateTime.isBefore(bucket.firstPoint.dateTime)) { bucket.firstPoint = point; } } // 3. 遍历窗口结束时间后的数据点 for (int i = 0; i < dataPoints.size(); i++) { DataPoint point = dataPoints.get(i); // 跳过窗口结束时间前的点 if (!point.dateTime.isAfter(windowEnd)) { continue; } // 条件1: 当前点低于limitingValue if (point.value < limitingValue) { return point.dateTime; } // 条件2: 检查当前桶是否满足farBeyond条件 LocalDate date = point.dateTime.toLocalDate(); int bucketIndex = point.dateTime.getHour() / 6; BucketKey key = new BucketKey(date, bucketIndex); BucketData bucket = bucketMap.get(key); // 桶不存在或没有farBeyond点 if (bucket == null || !bucket.hasFarBeyond) { // 第一桶(0-6点)直接返回桶首点时间 if (bucketIndex == 0) { return bucket != null && bucket.firstPoint != null ? bucket.firstPoint.dateTime : point.dateTime; } // 非第一桶返回上一桶最后farBeyond时间+1小时 else { BucketKey prevKey = new BucketKey( bucketIndex == 0 ? date.minusDays(1) : date, // 跨天处理 bucketIndex == 0 ? 3 : bucketIndex - 1 ); BucketData prevBucket = bucketMap.get(prevKey); if (prevBucket != null && prevBucket.lastFarBeyondTime != null) { return prevBucket.lastFarBeyondTime.plusHours(1); } else { return bucket != null && bucket.firstPoint != null ? bucket.firstPoint.dateTime : point.dateTime; } } } } return analysisEnd; } // ===================== 辅助类 ===================== private static class BucketKey { final LocalDate date; final int bucketIndex; // 0-3 BucketKey(LocalDate date, int bucketIndex) { this.date = date; this.bucketIndex = bucketIndex; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BucketKey bucketKey = (BucketKey) o; return bucketIndex == bucketKey.bucketIndex && Objects.equals(date, bucketKey.date); } @Override public int hashCode() { return Objects.hash(date, bucketIndex); } } private static class BucketData { List<DataPoint> points = new ArrayList<>(); DataPoint firstPoint = null; boolean hasFarBeyond = false; LocalDateTime lastFarBeyondTime = null; } private String formatDateTime(LocalDateTime dateTime) { return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } private Date toDate(LocalDateTime localDateTime) { if (localDateTime == null) { return null; } return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); } private LocalDateTime toLocalDateTime(Date date) { if (date == null) { return null; } return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault()); } /** * 合并连续的时间段 */ private List<TimePeriod> mergeContinuousPeriods(List<TimePeriod> periods) { if (periods.isEmpty()) { return periods; } // 按开始时间排序 periods.sort(Comparator.comparing(p -> p.start)); List<TimePeriod> merged = new ArrayList<>(); TimePeriod current = periods.get(0); for (int i = 1; i < periods.size(); i++) { TimePeriod next = periods.get(i); // 如果当前时间段结束时间与下一个时间段开始时间相差不超过24小时,则合并 if (current.end != null && next.start != null && Duration.between(current.end, next.start).toHours() <= 24) { current.end = next.end; } else { merged.add(current); current = next; } } merged.add(current); return merged; } // ===================== 辅助类 ===================== private static class DataPoint { LocalDateTime dateTime; double value; DataPoint(String dateStr, double value) { try { // 尝试解析多种可能的时间格式 if (dateStr.contains("T")) { // ISO格式: yyyy-MM-ddTHH:mm:ss this.dateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME); } else if (dateStr.length() == 16 && dateStr.contains(":")) { // 格式: yyyy-MM-dd HH:mm this.dateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); } else if (dateStr.length() == 13 && dateStr.contains(" ")) { // 格式: yyyy-MM-dd HH this.dateTime = LocalDateTime.parse(dateStr + ":00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } else if (dateStr.length() == 10 && dateStr.contains("-")) { // 格式: yyyy-MM-dd this.dateTime = LocalDateTime.parse(dateStr + " 00:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } else { // 默认尝试ISO格式 this.dateTime = LocalDateTime.parse(dateStr, DateTimeFormatter.ISO_LOCAL_DATE_TIME); } } catch (Exception e) { logger.error("解析时间字符串失败: {}", dateStr, e); throw new RuntimeException("时间格式解析失败: " + dateStr); } this.value = value; } LocalDateTime getDateTime() { return dateTime; } } private static class TimePeriod { LocalDateTime start; LocalDateTime end; // null表示仍在持续 TimePeriod(LocalDateTime start, LocalDateTime end) { this.start = start; this.end = end; } } /** * 获取工序名称方法 * @param eid * @return */ String getProcessname(String eid) { try { return processmanageService.getInfo( equipmentledgerService.getInfo(eid).getProcesscode() ).getProcessname(); } catch (Exception e) { logger.error("获取工序名称失败: {}", e.getMessage()); return "未知工序"; } } /** * 获取设备名称方法 * @param eid * @return */ String getEquipname(String eid) { try { return equipmentledgerService.getInfo(eid).getDevicename(); } catch (Exception e) { logger.error("获取设备名称失败: {}", e.getMessage()); return "未知设备"; } }
}
分析(2025-09-05 6 1309.3715
2025-09-05 5 1311.2253
2025-09-05 4 3312.3486
2025-09-05 3 1308.1030
2025-09-05 2 1305.4357
2025-09-05 1 1310.0124
2025-09-05 0 3303.4152
2025-09-04 23 3309.4347
2025-09-04 22 1309.8344
2025-09-04 21 1302.9254
2025-09-04 20 1305.8466
2025-09-04 19 1300.6333
2025-09-04 18 3292.2767
2025-09-04 17 1285.4596
2025-09-04 16 1283.2088
2025-09-04 15 1290.2380
2025-09-04 14 1306.5605
2025-09-04 13 1311.1862
2025-09-04 12 3307.0581
2025-09-04 11 1295.7108
2025-09-04 10 1283.5517
2025-09-04 9 1266.4618
2025-09-04 8 1277.8895
2025-09-04 7 3277.2967
2025-09-04 6 3286.7954
2025-09-04 5 3300.7924
2025-09-04 4 3308.1007
2025-09-04 3 3313.4492
2025-09-04 2 3314.3323
2025-09-04 1 1312.7171
2025-09-04 0 1311.3445),其中limitingValue值为1000,farBeyondValue值为1800.分析结果为创建完整报警记录: EP000015 - 开始: 2025-09-04T00:00, 结束: 2025-09-05T05:00 (持续29小时)。正常应该是 开始时间: 2025-09-04T02:00,持续中。
最新发布