diff --git a/modules/common/target/common-1.0.0.jar b/modules/common/target/common-1.0.0.jar index 1977983..a49f390 100644 Binary files a/modules/common/target/common-1.0.0.jar and b/modules/common/target/common-1.0.0.jar differ diff --git a/modules/common/target/maven-archiver/pom.properties b/modules/common/target/maven-archiver/pom.properties index 7ff955e..721b4ec 100644 --- a/modules/common/target/maven-archiver/pom.properties +++ b/modules/common/target/maven-archiver/pom.properties @@ -1,5 +1,5 @@ #Generated by Maven -#Sat May 24 09:59:24 CST 2025 +#Fri Jan 23 15:42:05 CST 2026 groupId=com.zhehekeji artifactId=common version=1.0.0 diff --git a/modules/filter/target/filter-1.0.0.jar b/modules/filter/target/filter-1.0.0.jar index 9acee5d..d1356ce 100644 Binary files a/modules/filter/target/filter-1.0.0.jar and b/modules/filter/target/filter-1.0.0.jar differ diff --git a/web/src/main/java/com/zhehekeji/web/service/client/DeleteServer.java b/web/src/main/java/com/zhehekeji/web/service/client/DeleteServer.java new file mode 100644 index 0000000..a4e74d8 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/DeleteServer.java @@ -0,0 +1,217 @@ +package com.zhehekeji.web.service.client; + + +import com.sourceforge.snap7.moka7.S7Client; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.io.File; +import java.io.IOException; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadPoolExecutor; + +@Slf4j +@Service +public class DeleteServer { + + + @PostConstruct + public void PlcConnectionPool() { + CompletableFuture.runAsync(() -> cleanStorage("E:\\data\\mp4\\record\\live")); + } + + /** + * 清理存储空间 + * @param rootPath 根文件夹路径,例如 "D:/storage" + */ + private void cleanStorage(String rootPath) { + try { + log.info("开始清理存储空间: {}", rootPath); + + Path root = Paths.get(rootPath); + if (!Files.exists(root) || !Files.isDirectory(root)) { + log.error("存储路径不存在或不是目录: {}", rootPath); + return; + } + + // 获取所有相机文件夹下的日期文件夹 + List allDateFolders = getAllDateFolders(root); + log.info("共找到 {} 个日期文件夹", allDateFolders.size()); + + // 按日期升序排序(最早的在前面) + allDateFolders.sort(Comparator.comparing(DateFolder::getDate)); + + // 第一轮清理:删除两个月前的文件夹 + LocalDate twoMonthsAgo = LocalDate.now().minusMonths(2); + int deletedOld = deleteFoldersBeforeDate(allDateFolders, twoMonthsAgo); + log.info("删除两个月前的文件夹: {} 个", deletedOld); + + // 检查磁盘空间 + if (getDiskUsagePercent(root) < 70.0) { + log.info("磁盘空间充足({}%),清理完成", getDiskUsagePercent(root)); + return; + } + + // 第二轮清理:继续删除最旧的文件夹,直到空间充足或删完 + int deletedMore = deleteOldestFolders(allDateFolders, twoMonthsAgo, root); + log.info("继续删除最旧的文件夹: {} 个", deletedMore); + + log.info("存储空间清理完成,当前使用率: {}%", getDiskUsagePercent(root)); + + } catch (Exception e) { + log.error("清理存储空间失败", e); + } + } + + /** + * 获取所有相机文件夹下的日期文件夹 + */ + private List getAllDateFolders(Path root) throws IOException { + List result = new ArrayList<>(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + // 遍历根目录下的相机文件夹 + Files.list(root) + .filter(Files::isDirectory) + .forEach(cameraDir -> { + try { + // 遍历相机文件夹下的日期文件夹 + Files.list(cameraDir) + .filter(Files::isDirectory) + .forEach(dateDir -> { + try { + // 尝试解析文件夹名为日期 + LocalDate date = LocalDate.parse(dateDir.getFileName().toString(), formatter); + result.add(new DateFolder(dateDir.toFile(), date, cameraDir.getFileName().toString())); + } catch (DateTimeParseException e) { + // 文件夹名不符合日期格式,跳过 + } + }); + } catch (IOException e) { + log.error("遍历相机文件夹失败: {}", cameraDir, e); + } + }); + + return result; + } + + /** + * 删除指定日期之前的文件夹 + */ + private int deleteFoldersBeforeDate(List dateFolders, LocalDate beforeDate) { + int count = 0; + Iterator iterator = dateFolders.iterator(); + + while (iterator.hasNext()) { + DateFolder folder = iterator.next(); + if (folder.getDate().isBefore(beforeDate)) { + if (deleteFolder(folder.getFile())) { + count++; + } + iterator.remove(); + } + } + + return count; + } + + /** + * 继续删除最旧的文件夹,直到空间充足 + */ + private int deleteOldestFolders(List dateFolders, LocalDate minDate, Path root) { + int count = 0; + Iterator iterator = dateFolders.iterator(); + + while (iterator.hasNext() && getDiskUsagePercent(root) >= 70.0) { + DateFolder folder = iterator.next(); + + if (deleteFolder(folder.getFile())) { + count++; + log.info("删除文件夹: {}/{} (日期: {}), 当前使用率: {}%", + folder.getCameraName(), folder.getFile().getName(), folder.getDate(), getDiskUsagePercent(root)); + } + + iterator.remove(); + } + + return count; + } + + /** + * 删除文件夹及其内容 + */ + private boolean deleteFolder(File folder) { + try { + if (folder.exists() && folder.isDirectory()) { + Files.walk(folder.toPath()) + .sorted(Comparator.reverseOrder()) + .map(Path::toFile) + .forEach(File::delete); + + log.info("已删除文件夹: {}", folder.getAbsolutePath()); + return true; + } + return false; + } catch (IOException e) { + log.error("删除文件夹失败: {}", folder.getAbsolutePath(), e); + return false; + } + } + + /** + * 获取磁盘使用率百分比 + */ + private double getDiskUsagePercent(Path path) { + try { + FileStore store = Files.getFileStore(path); + long total = store.getTotalSpace(); + long free = store.getUsableSpace(); + long used = total - free; + return (used * 100.0) / total; + } catch (IOException e) { + log.error("获取磁盘空间失败", e); + return 100.0; + } + } + + /** + * 日期文件夹辅助类 + */ + private static class DateFolder { + private final File file; + private final LocalDate date; + private final String cameraName; + + public DateFolder(File file, LocalDate date, String cameraName) { + this.file = file; + this.date = date; + this.cameraName = cameraName; + } + + public File getFile() { + return file; + } + + public LocalDate getDate() { + return date; + } + + public String getCameraName() { + return cameraName; + } + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/cron/PLCConnectionExample.java b/web/src/main/java/com/zhehekeji/web/service/cron/PLCConnectionExample.java index a550390..c3ce8be 100644 --- a/web/src/main/java/com/zhehekeji/web/service/cron/PLCConnectionExample.java +++ b/web/src/main/java/com/zhehekeji/web/service/cron/PLCConnectionExample.java @@ -13,11 +13,17 @@ import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.Resource; import java.io.BufferedReader; +import java.io.File; import java.io.FileReader; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.nio.file.FileStore; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.*; import java.util.concurrent.*; @Configuration @@ -57,6 +63,7 @@ public class PLCConnectionExample { client.ConnectTo(plcIp,plcRack,plcSlot); // IP, Rack, Slot connectionPool.offer(client); } + } @@ -247,12 +254,11 @@ public class PLCConnectionExample { // 读数据 @Scheduled(fixedDelay = POLL_INTERVAL) void server(){ + // 批量读取所有C1/C2/C3地址 + Map dataMap = readPlcDataTaskIds(); - for (String key : addressMap.keySet()){ - if (key.contains("out") || !key.contains("C")) { - continue; - } - int i = readPlcDataTaskId(addressMap.get(key)); + for (String key : dataMap.keySet()){ + int i = dataMap.get(key); if (i == 0) continue; if (taskMap.get(key) == null || taskMap.get(key) != i) { log.info("任务号变化" + key + ":" + i); @@ -262,9 +268,7 @@ public class PLCConnectionExample { executorService.submit(() -> processKey(i, plcId, key)); if(key.contains("C1")){ - writePlcDataTaskId(key+"-out", i); - } } } @@ -335,6 +339,43 @@ public class PLCConnectionExample { } return false; } + /** + * 批量读取所有C1/C2/C3地址的任务号 + * @return Map,例如:{"001-C1": 100, "001-C2": 200, ...} + */ + public Map readPlcDataTaskIds(){ + S7Client client = getConnection(); + try { + // 批量读取0-44字节区域 + byte[] buffer = new byte[48]; + int result = client.ReadArea(S7.S7AreaDB, dbNumber, 0, 48, buffer); + + if (result != 0) { + updateConnection(client); + log.info("批量读取失败,错误码: " + result); + return new HashMap<>(); + } + + // 解析所有C1/C2/C3地址的值 + Map resultMap = new HashMap<>(); + for (String key : addressMap.keySet()) { + if (key.contains("out") || !key.contains("C")) { + continue; + } + int offset = addressMap.get(key); + int value = S7.GetDIntAt(buffer, offset); + resultMap.put(key, value); + } + + return resultMap; + } catch (Exception e) { + log.error("批量读取异常", e); + return new HashMap<>(); + } finally { + returnConnection(client); + } + } + public boolean writePlcDataStatusErr(String plcId,int digit){ if (digit==1){