package com.zy.task; import HslCommunication.Core.Types.OperateResult; import HslCommunication.Core.Types.OperateResultExOne; import HslCommunication.Profinet.Siemens.SiemensPLCS; import HslCommunication.Profinet.Siemens.SiemensS7Net; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.zy.core.model.CrnSlave; import com.zy.core.properties.SlaveProperties; import com.zy.entity.CrnTiltRecord; import com.zy.service.CrnTiltRecordService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.util.Calendar; import java.util.Date; import java.util.List; /** * 堆垛机倾斜度记录定时任务 * 每周记录一次堆垛机的倾斜度数据 * * 说明: * 1. 根据"货架倾斜度.txt"和"堆垛机_DB101发送wcs信息.pdf"文档 * 2. 倾斜度数据从 PLC 的 DB101 数据块读取(具体偏移量需要根据实际传感器配置确定) * 3. 每周执行一次(默认每周一凌晨2点执行) */ @Slf4j @Component public class CrnTiltRecordTask implements ApplicationListener { @Autowired private SlaveProperties slaveProperties; @Autowired private CrnTiltRecordService crnTiltRecordService; private boolean startupRecordExecuted = false; /** * 系统启动后自动记录一次倾斜度数据 * 使用 ApplicationListener 监听 ContextRefreshedEvent,确保在 Spring 上下文完全初始化后执行 */ @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 避免重复执行(Spring 可能会触发多次事件) if (startupRecordExecuted) { return; } startupRecordExecuted = true; log.info("========== 系统启动,开始执行堆垛机倾斜度启动记录 =========="); // 延迟10秒执行,确保系统完全启动,数据库连接等都已就绪 new Thread(() -> { try { log.info("倾斜度启动记录任务将在10秒后执行..."); Thread.sleep(10000); log.info("开始执行倾斜度启动记录任务..."); recordTiltDataOnStartup(); } catch (InterruptedException e) { log.error("启动记录任务被中断", e); } catch (Exception e) { log.error("启动记录任务执行异常", e); } }, "CrnTiltRecord-Startup").start(); } /** * 启动时记录倾斜度数据(不检查当天是否已记录) */ private void recordTiltDataOnStartup() { log.info("========== 开始执行倾斜度启动记录 =========="); try { if (slaveProperties == null) { log.error("SlaveProperties 未注入,跳过启动倾斜度记录"); return; } if (crnTiltRecordService == null) { log.error("CrnTiltRecordService 未注入,跳过启动倾斜度记录"); return; } List crnSlaves = slaveProperties.getCrn(); if (crnSlaves == null || crnSlaves.isEmpty()) { log.warn("没有配置堆垛机,跳过启动倾斜度记录"); return; } log.info("找到 {} 台堆垛机,开始记录倾斜度数据", crnSlaves.size()); Date recordTime = new Date(); // 计算今天的开始时间(用于去重判断) Calendar cal = Calendar.getInstance(); cal.setTime(recordTime); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Date todayStart = cal.getTime(); Date todayEnd = new Date(todayStart.getTime() + 24 * 60 * 60 * 1000 - 1); int successCount = 0; int failCount = 0; for (CrnSlave crnSlave : crnSlaves) { try { // 检查今天是否已经记录过(使用 record_time 的日期部分) EntityWrapper checkWrapper = new EntityWrapper<>(); checkWrapper.eq("crn_no", crnSlave.getId()); checkWrapper.ge("record_time", todayStart); checkWrapper.le("record_time", todayEnd); int count = crnTiltRecordService.selectCount(checkWrapper); if (count > 0) { log.info("堆垛机{}今天已记录过倾斜度数据,跳过", crnSlave.getId()); continue; } // 读取倾斜度数据 CrnTiltRecord record = readTiltDataFromPLC(crnSlave); if (record != null) { record.setCrnNo(crnSlave.getId()); record.setRecordTime(recordTime); record.setRecordType("STARTUP"); // 启动时记录 // 获取上次记录用于对比 EntityWrapper lastWrapper = new EntityWrapper<>(); lastWrapper.eq("crn_no", crnSlave.getId()); lastWrapper.orderBy("record_time", false); lastWrapper.last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"); CrnTiltRecord lastRecord = crnTiltRecordService.selectOne(lastWrapper); if (lastRecord != null && lastRecord.getTiltValue() != null) { // 对比倾斜度 record.setPrevTiltValue(lastRecord.getTiltValue()); if (record.getTiltValue() != null) { float change = record.getTiltValue() - lastRecord.getTiltValue(); record.setTiltChange(change); log.info("堆垛机{}启动倾斜度对比:当前值={}, 上次值={}, 变化量={}", crnSlave.getId(), record.getTiltValue(), lastRecord.getTiltValue(), change); } } // 保存到数据库 if (crnTiltRecordService.insert(record)) { successCount++; log.info("堆垛机{}启动倾斜度记录成功:倾斜度={}, X={}, Y={}, Z={}, 变化量={}", crnSlave.getId(), record.getTiltValue(), record.getTiltX(), record.getTiltY(), record.getTiltZ(), record.getTiltChange() != null ? record.getTiltChange() : 0); } else { failCount++; log.error("堆垛机{}启动倾斜度记录保存失败", crnSlave.getId()); } } else { failCount++; log.error("堆垛机{}启动倾斜度数据读取失败", crnSlave.getId()); } } catch (Exception e) { failCount++; log.error("堆垛机{}启动倾斜度记录异常", crnSlave.getId(), e); } } log.info("堆垛机倾斜度启动记录完成:成功{}条,失败{}条", successCount, failCount); } catch (Exception e) { log.error("堆垛机倾斜度启动记录任务执行异常", e); } } /** * 每周记录一次堆垛机倾斜度数据 * cron表达式:0 0 2 ? * MON 表示每周一凌晨2点执行 * 如果需要修改执行时间,可以调整cron表达式 */ @Scheduled(cron = "0 0 2 ? * MON") public void recordCrnTiltData() { log.info("开始执行堆垛机倾斜度记录任务..."); try { List crnSlaves = slaveProperties.getCrn(); if (crnSlaves == null || crnSlaves.isEmpty()) { log.warn("没有配置堆垛机,跳过倾斜度记录"); return; } Date recordTime = new Date(); // 计算本周的开始时间(用于去重判断) Calendar cal = Calendar.getInstance(); cal.setTime(recordTime); // 设置为本周一 int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK); int daysFromMonday = (dayOfWeek == Calendar.SUNDAY ? 6 : dayOfWeek - Calendar.MONDAY); cal.add(Calendar.DAY_OF_MONTH, -daysFromMonday); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); Date weekStart = cal.getTime(); Date weekEnd = new Date(weekStart.getTime() + 7 * 24 * 60 * 60 * 1000 - 1); int successCount = 0; int failCount = 0; for (CrnSlave crnSlave : crnSlaves) { try { // 检查本周是否已经记录过(使用 record_time 的日期部分) EntityWrapper wrapper = new EntityWrapper<>(); wrapper.eq("crn_no", crnSlave.getId()); wrapper.ge("record_time", weekStart); wrapper.le("record_time", weekEnd); int count = crnTiltRecordService.selectCount(wrapper); if (count > 0) { log.info("堆垛机{}本周已记录过倾斜度数据,跳过", crnSlave.getId()); continue; } // 读取倾斜度数据 CrnTiltRecord record = readTiltDataFromPLC(crnSlave); if (record != null) { record.setCrnNo(crnSlave.getId()); record.setRecordTime(recordTime); record.setRecordType("AUTO"); // 自动记录 // 获取上次记录用于对比 EntityWrapper lastWrapper = new EntityWrapper<>(); lastWrapper.eq("crn_no", crnSlave.getId()); lastWrapper.orderBy("record_time", false); lastWrapper.last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"); CrnTiltRecord lastRecord = crnTiltRecordService.selectOne(lastWrapper); if (lastRecord != null && lastRecord.getTiltValue() != null) { // 对比倾斜度 record.setPrevTiltValue(lastRecord.getTiltValue()); if (record.getTiltValue() != null) { float change = record.getTiltValue() - lastRecord.getTiltValue(); record.setTiltChange(change); log.info("堆垛机{}倾斜度对比:当前值={}, 上次值={}, 变化量={}", crnSlave.getId(), record.getTiltValue(), lastRecord.getTiltValue(), change); } } // 保存到数据库 if (crnTiltRecordService.insert(record)) { successCount++; log.info("堆垛机{}倾斜度记录成功:倾斜度={}, X={}, Y={}, Z={}, 变化量={}", crnSlave.getId(), record.getTiltValue(), record.getTiltX(), record.getTiltY(), record.getTiltZ(), record.getTiltChange() != null ? record.getTiltChange() : 0); } else { failCount++; log.error("堆垛机{}倾斜度记录保存失败", crnSlave.getId()); } } else { failCount++; log.error("堆垛机{}倾斜度数据读取失败", crnSlave.getId()); } } catch (Exception e) { failCount++; log.error("堆垛机{}倾斜度记录异常", crnSlave.getId(), e); } } log.info("堆垛机倾斜度记录任务完成:成功{}条,失败{}条", successCount, failCount); } catch (Exception e) { log.error("堆垛机倾斜度记录任务执行异常", e); } } /** * 从 PLC 读取倾斜度数据 * * 注意:根据实际传感器配置,倾斜度数据可能存储在: * 1. DB101 的某个偏移量位置(需要根据"货架倾斜度.txt"文档确定具体位置) * 2. 或者其他数据块 * * 当前实现假设倾斜度数据存储在 DB101 的偏移量位置(需要根据实际配置调整) * * @param crnSlave 堆垛机配置 * @return 倾斜度记录对象,如果读取失败返回null */ private CrnTiltRecord readTiltDataFromPLC(CrnSlave crnSlave) { SiemensS7Net siemensNet = null; try { // 创建 PLC 连接 siemensNet = new SiemensS7Net(SiemensPLCS.S1200, crnSlave.getIp()); siemensNet.setRack(crnSlave.getRack().byteValue()); siemensNet.setSlot(crnSlave.getSlot().byteValue()); OperateResult connect = siemensNet.ConnectServer(); if (!connect.IsSuccess) { log.error("堆垛机{} PLC连接失败:{}", crnSlave.getId(), connect.Message); return null; } // 读取倾斜度数据 // 注意:根据"货架倾斜度.txt"文档,需要确定倾斜度数据在 DB101 中的具体偏移量 // 假设倾斜度数据存储在 DB101.56 开始的位置(需要根据实际配置调整) // 读取4个Real类型数据(X、Y、Z倾斜度和总倾斜度),每个Real占4字节,共16字节 OperateResultExOne result = siemensNet.Read("DB101.56", (short) 16); if (!result.IsSuccess) { log.error("堆垛机{}读取倾斜度数据失败:{}", crnSlave.getId(), result.Message); return null; } CrnTiltRecord record = new CrnTiltRecord(); // 解析倾斜度数据(根据实际传感器数据格式调整) // 假设:偏移量0-3: X方向倾斜度(Real),偏移量4-7: Y方向倾斜度(Real),偏移量8-11: Z方向倾斜度(Real),偏移量12-15: 总倾斜度(Real) record.setTiltX(siemensNet.getByteTransform().TransSingle(result.Content, 0)); record.setTiltY(siemensNet.getByteTransform().TransSingle(result.Content, 4)); record.setTiltZ(siemensNet.getByteTransform().TransSingle(result.Content, 8)); record.setTiltValue(siemensNet.getByteTransform().TransSingle(result.Content, 12)); log.debug("堆垛机{}倾斜度数据读取成功:X={}, Y={}, Z={}, 总倾斜度={}", crnSlave.getId(), record.getTiltX(), record.getTiltY(), record.getTiltZ(), record.getTiltValue()); return record; } catch (Exception e) { log.error("堆垛机{}读取倾斜度数据异常", crnSlave.getId(), e); return null; } finally { if (siemensNet != null) { try { siemensNet.ConnectClose(); } catch (Exception e) { log.warn("关闭PLC连接异常", e); } } } } /** * 手动触发记录(用于测试或手动记录) * @param crnNo 堆垛机编号,如果为null则记录所有堆垛机 */ public void manualRecord(Integer crnNo) { log.info("手动触发堆垛机倾斜度记录任务,堆垛机编号:{}", crnNo); try { List crnSlaves = slaveProperties.getCrn(); if (crnSlaves == null || crnSlaves.isEmpty()) { log.warn("没有配置堆垛机,跳过倾斜度记录"); return; } Date recordTime = new Date(); int successCount = 0; int failCount = 0; for (CrnSlave crnSlave : crnSlaves) { // 如果指定了堆垛机编号,只记录指定的堆垛机 if (crnNo != null && !crnNo.equals(crnSlave.getId())) { continue; } try { // 读取倾斜度数据 CrnTiltRecord record = readTiltDataFromPLC(crnSlave); if (record != null) { record.setCrnNo(crnSlave.getId()); record.setRecordTime(recordTime); record.setRecordType("MANUAL"); // 手动触发 // 获取上次记录用于对比 EntityWrapper lastWrapper = new EntityWrapper<>(); lastWrapper.eq("crn_no", crnSlave.getId()); lastWrapper.orderBy("record_time", false); lastWrapper.last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"); CrnTiltRecord lastRecord = crnTiltRecordService.selectOne(lastWrapper); if (lastRecord != null && lastRecord.getTiltValue() != null) { // 对比倾斜度 record.setPrevTiltValue(lastRecord.getTiltValue()); if (record.getTiltValue() != null) { float change = record.getTiltValue() - lastRecord.getTiltValue(); record.setTiltChange(change); log.info("堆垛机{}倾斜度对比:当前值={}, 上次值={}, 变化量={}", crnSlave.getId(), record.getTiltValue(), lastRecord.getTiltValue(), change); } } // 保存到数据库 if (crnTiltRecordService.insert(record)) { successCount++; log.info("堆垛机{}手动倾斜度记录成功:倾斜度={}, X={}, Y={}, Z={}, 变化量={}", crnSlave.getId(), record.getTiltValue(), record.getTiltX(), record.getTiltY(), record.getTiltZ(), record.getTiltChange() != null ? record.getTiltChange() : 0); } else { failCount++; log.error("堆垛机{}倾斜度记录保存失败", crnSlave.getId()); } } else { failCount++; log.error("堆垛机{}倾斜度数据读取失败", crnSlave.getId()); } } catch (Exception e) { failCount++; log.error("堆垛机{}倾斜度记录异常", crnSlave.getId(), e); } } log.info("手动堆垛机倾斜度记录任务完成:成功{}条,失败{}条", successCount, failCount); } catch (Exception e) { log.error("手动堆垛机倾斜度记录任务执行异常", e); } } }