diff --git a/modules/common/target/common-1.0.0.jar b/modules/common/target/common-1.0.0.jar index 507cdec..56ce2d6 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/filter/target/filter-1.0.0.jar b/modules/filter/target/filter-1.0.0.jar index 480fd6d..d2e9cab 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/EmptyCheckService.java b/web/src/main/java/com/zhehekeji/web/service/EmptyCheckService.java index c752ff4..a9e205c 100644 --- a/web/src/main/java/com/zhehekeji/web/service/EmptyCheckService.java +++ b/web/src/main/java/com/zhehekeji/web/service/EmptyCheckService.java @@ -57,11 +57,13 @@ public class EmptyCheckService { key+="C2"; } Street street = getStreet(transmission); - plcConnectionExample.writePlcDataTaskId(key + "-out"); + log.info("transmission,{}",transmission.toString()); + plcConnectionExample. writePlcDataTaskId(key + "-out"); if(street!= null){ Order order = orderMapper.getOneByStreetId(street.getId()); transmission.setUrl(transmission.getUrl().replace("E:","").replace("D:","")); transmission.setUrl("http://"+street.getPlcIp()+":9007/api/pic"+transmission.getUrl()); + order.setEndTime(LocalDateTime.now()); order.setPicPaths(order.getPicPaths()!=null ? order.getPicPaths() + "," +transmission.getUrl(): transmission.getUrl()); if (transmission.getNgBoolean()){ order.setStatus(1); diff --git a/web/src/main/java/com/zhehekeji/web/service/PlcService.java b/web/src/main/java/com/zhehekeji/web/service/PlcService.java index 56e329b..15775bf 100644 --- a/web/src/main/java/com/zhehekeji/web/service/PlcService.java +++ b/web/src/main/java/com/zhehekeji/web/service/PlcService.java @@ -904,33 +904,37 @@ public class PlcService { try { //执行动作 -// if(times==1) { + if(times==1) { +// 暂时关掉延时使用灵闪 // Thread.sleep(configProperties.getCameraConfig().getC1DelayCaptureTime()); -// -// ClientChanel.sendMessage(plcId, "C1:1"); -// } else + + ClientChanel.sendMessage(plcId, "C1:1"); + } else if (times==2) { - Thread.sleep(configProperties.getCameraConfig().getC2DelayCaptureTime()); +// 暂时关掉延时使用灵闪延时 +// Thread.sleep(configProperties.getCameraConfig().getC2DelayCaptureTime()); //C1底部拍照 - log.info(plcId+" C1"); ClientChanel.sendMessage(plcId, "C1:1"); + log.info(plcId+" C1 "+ taskId); Thread.sleep(configProperties.getCameraConfig().getC3DelayCaptureTime()); Order order = orderMapper.getOneByOrderNum(plcId+"_"+taskId); - ClientChanel.sendMessage(plcId, "C3:1"); + Street street = streetService.getStreetByPlcId(plcId); String path = zlmService.stopRecord(cameraService.getById(street.getCamera1Id())); order.setVideoPath1(path); orderMapper.updateById(order); }else if(times == 4){ - Thread.sleep(configProperties.getCameraConfig().getC4DelayCaptureTime()); + +// 暂时关掉延时使用灵闪延时 +// Thread.sleep(configProperties.getCameraConfig().getC4DelayCaptureTime()); ClientChanel.sendMessage(plcId, "C4:1"); } } catch (InterruptedException e) { - throw new RuntimeException(e); + log.error("拍照报错:{}",e); } } public void upStreet(KsecDataInfo dataInfo) { diff --git a/web/src/main/java/com/zhehekeji/web/service/client/Decoder.java b/web/src/main/java/com/zhehekeji/web/service/client/Decoder.java index 160d7cd..b281c60 100644 --- a/web/src/main/java/com/zhehekeji/web/service/client/Decoder.java +++ b/web/src/main/java/com/zhehekeji/web/service/client/Decoder.java @@ -107,17 +107,18 @@ public class Decoder extends DelimiterBasedFrameDecoder { //断连 ClientChanel.disConnect(transmission.getSRMNumber()); //ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); - }else if("ED".equals(transmission.getHeader())){ - //底部报警 - - emptyCheckService.visualJudgmentStatus(transmission,0); - //ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); - }else if("ET".equals(transmission.getHeader())){ - //顶部报警 - - emptyCheckService.visualJudgmentStatus(transmission,1); - //ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); } +// else if("ED".equals(transmission.getHeader())){ +// //底部报警 +// +// emptyCheckService.visualJudgmentStatus(transmission,0); +// //ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); +// }else if("ET".equals(transmission.getHeader())){ +// //顶部报警 +// +// emptyCheckService.visualJudgmentStatus(transmission,1); +// //ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); +// } } } 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 7894916..25c6e57 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 @@ -237,6 +237,7 @@ public class PLCConnectionExample { // 将原来的循环体逻辑提取到单独的方法中 private void processKey(Integer i, String plcId,String key) { + writePlcDataTaskId(key+"-out"); if (key.contains("C1")) { // 推烟录像触发任务号 plcService.orderStart(i, plcId); @@ -247,13 +248,12 @@ public class PLCConnectionExample { // 顶部拍照触发任务号 plcService.action(plcId, 4, i); } - try { - Thread.sleep(1000); - - writePlcDataTaskId(key+"-out"); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } +// try { +// Thread.sleep(1000); +// +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } } @@ -263,6 +263,7 @@ public class PLCConnectionExample { void server(){ // 批量读取所有C1/C2/C3地址 Map dataMap = readPlcDataTaskIds(); + log.info("dataMap: {} time:{}", dataMap, System.currentTimeMillis()); for (String key : dataMap.keySet()){ int i = dataMap.get(key); @@ -275,9 +276,7 @@ public class PLCConnectionExample { String plcId = key.split("-")[0]; executorService.submit(() -> processKey(i, plcId, key)); - if(key.contains("C1")){ - writePlcDataTaskId(key+"-out", i); - } + } } } diff --git a/web/src/main/java/com/zhehekeji/web/service/cron/PLCMonitorService.java b/web/src/main/java/com/zhehekeji/web/service/cron/PLCMonitorService.java new file mode 100644 index 0000000..dd0b1fd --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/cron/PLCMonitorService.java @@ -0,0 +1,372 @@ +package com.zhehekeji.web.service.cron; + +import com.sourceforge.snap7.moka7.S7; +import com.sourceforge.snap7.moka7.S7Client; +import io.swagger.models.auth.In; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.*; + +@Slf4j +@Component +public class PLCMonitorService { + + // 定义数据块和偏移量 + int dbNumber = 121; // DB121 + String plcIp = "10.69.105.122"; + int plcRack = 0; + int plcSlot = 1; + int sizeToRead = 4; // 读取4个字节 + + // 地址映射 + public static final Map addressMap = new ConcurrentHashMap<>(); + // 当前命令的上一个任务号 + public static final Map taskMap = new ConcurrentHashMap<>(); + + private final int POOL_SIZE = 5; // 连接池大小 + private BlockingQueue connectionPool; + + // 监控线程 + private volatile boolean running = true; + private Thread monitorThread; + // 重启计数器 + private volatile int restartCount = 0; + private static final int MAX_RESTART_COUNT = 10; // 最大重启次数 + + // 线程池 + int corePoolSize = 15; + int maximumPoolSize = 40; + long keepAliveTime = 60; + TimeUnit unit = TimeUnit.SECONDS; + BlockingQueue workQueue = new LinkedBlockingQueue<>(); + ExecutorService executorService = new ThreadPoolExecutor( + corePoolSize, + maximumPoolSize, + keepAliveTime, + unit, + workQueue + ); + +// @PostConstruct + public void init() { + // 初始化连接池 + connectionPool = new ArrayBlockingQueue<>(POOL_SIZE); + log.info("建立PLC监控连接池"); + + readDbConf(); + // 创建连接池中的连接 + for (int i = 0; i < POOL_SIZE; i++) { + S7Client client = new S7Client(); + client.ConnectTo(plcIp, plcRack, plcSlot); + connectionPool.offer(client); + } + + // 启动监控线程 + startMonitorThread(); + } + + /** + * 启动监控线程 + */ + private void startMonitorThread() { + monitorThread = new Thread(() -> { + log.info("PLC监控线程启动"); + while (running) { + try { + // 批量读取所有C1/C2/C3地址 + Map dataMap = readPlcDataTaskIds(); + + 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); + taskMap.put(key, i); + taskMap.put(key + "-out", i); + + } + } + + Thread.sleep(300); // 查询间隔 + } catch (InterruptedException e) { + log.info("PLC监控线程被中断"); + Thread.currentThread().interrupt(); + // 跳出循环后会检查是否需要重启 + break; + } catch (Exception e) { + log.error("PLC监控线程异常", e); + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + break; + } + } + } + log.info("PLC监控线程结束"); + + // 线程异常退出时,检查是否需要重启 + if (running && restartCount < MAX_RESTART_COUNT) { + restartCount++; + log.warn("PLC监控线程异常退出,{}秒后尝试第{}次重启", restartCount * 2, restartCount); + try { + Thread.sleep(restartCount * 2000L); // 递增延迟:2s, 4s, 6s... + // 重新初始化连接池 + reinitializeConnectionPool(); + startMonitorThread(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.info("重启PLC监控线程被中断"); + } + } else if (restartCount >= MAX_RESTART_COUNT) { + log.error("PLC监控线程重启次数超过上限({}),停止自动重启", MAX_RESTART_COUNT); + } + }, "PLC-Monitor-Thread"); + monitorThread.setDaemon(true); + monitorThread.start(); + } + + /** + * 重新初始化连接池 + */ + private void reinitializeConnectionPool() { + log.info("重新初始化PLC连接池..."); + // 清理旧连接 + connectionPool.clear(); + // 重建连接池 + for (int i = 0; i < POOL_SIZE; i++) { + S7Client client = new S7Client(); + client.ConnectTo(plcIp, plcRack, plcSlot); + connectionPool.offer(client); + } + log.info("PLC连接池重新初始化完成"); + } + + /** + * 停止监控线程 + */ + public void stopMonitor() { + running = false; + restartCount = MAX_RESTART_COUNT; // 停止自动重启 + if (monitorThread != null) { + monitorThread.interrupt(); + } + executorService.shutdown(); + log.info("PLC监控服务已停止"); + } + + /** + * 获取连接 + */ + public S7Client getConnection() { + try { + return connectionPool.take(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("获取 PLC 连接失败", e); + } + } + + /** + * 更新连接 + */ + public void updateConnection(S7Client client) { + try { + if (client != null) { + client.Disconnect(); + } + connectionPool.remove(client); + + S7Client newClient = new S7Client(); + int result = newClient.ConnectTo(plcIp, plcRack, plcSlot); + if (result == 0) { + connectionPool.offer(newClient); + log.info("成功创建并添加新PLC连接到连接池"); + } else { + log.error("创建新PLC连接失败,错误码: {}", result); + } + } catch (Exception e) { + log.error("更新PLC连接失败", e); + throw new RuntimeException("归还 PLC 连接失败", e); + } + } + + /** + * 归还连接 + */ + public void returnConnection(S7Client client) { + try { + if (client != null && client.Connected) { + connectionPool.offer(client); + } else { + updateConnection(client); + } + } catch (Exception e) { + log.error("归还PLC连接时发生错误", e); + throw new RuntimeException("归还 PLC 连接失败", e); + } + } + + /** + * 读取s7配置 + */ + void readDbConf() { + try (BufferedReader reader = new BufferedReader(new FileReader("./s7DB.txt"))) { + String line; + while ((line = reader.readLine()) != null) { + if (line.startsWith("#")) continue; + String[] parts = line.split(":", 2); + if (parts.length == 2) { + addressMap.put(parts[0].trim(), Integer.valueOf(parts[1].trim())); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * 读取指定偏移量的任务号 + */ + public int readPlcDataTaskId(int startOffset) { + S7Client client = getConnection(); + try { + byte[] buffer = new byte[sizeToRead]; + int result = client.ReadArea(S7.S7AreaDB, dbNumber, startOffset, sizeToRead, buffer); + int i = 0; + if (result == 0) { + i = ((buffer[0] & 0xFF) << 24) | + ((buffer[1] & 0xFF) << 16) | + ((buffer[2] & 0xFF) << 8) | + (buffer[3] & 0xFF); + } else { + updateConnection(client); + log.info("读取失败,错误码: " + result + " 位置: " + startOffset); + } + return i; + } catch (Exception e) { + log.error("连接池报错", e); + } finally { + returnConnection(client); + } + return 0; + } + + /** + * 写入任务号 + */ + public synchronized boolean writePlcDataTaskId(String key) { + log.info("写入任务号:" + key); + int value = taskMap.get(key); + if (value == 0) { + return false; + } else { + writePlcDataTaskId(key, value); + taskMap.put(key, 0); + } + return true; + } + + /** + * 写入任务号到指定位置 + */ + public boolean writePlcDataTaskId(String key, int value) { + S7Client client = getConnection(); + try { + byte[] buffer = new byte[sizeToRead]; + S7.SetDIntAt(buffer, 0, value); + int result = client.WriteArea(S7.S7AreaDB, dbNumber, addressMap.get(key), sizeToRead, buffer); + if (result == 0) { + log.info("写入成功" + key + ":" + value); + return true; + } else { + updateConnection(client); + log.info("写入失败,错误码: " + result); + return false; + } + } catch (Exception e) { + log.error("连接池报错", e); + } finally { + returnConnection(client); + } + return false; + } + + /** + * 批量读取所有C1/C2/C3地址的任务号 + */ + public Map readPlcDataTaskIds() { + S7Client client = getConnection(); + try { + 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<>(); + } + + 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); + } + } + + /** + * 写入PLC状态错误 + */ + public boolean writePlcDataStatusErr(String plcId, int digit) { + if (digit == 1) { + return writePlcDataStatus((plcId + "-ET-out"), digit, true); + } else { + return writePlcDataStatus((plcId + "-ED-out"), digit, true); + } + } + + /** + * 写入PLC状态 + */ + public boolean writePlcDataStatus(String startOffset, int digit, boolean value) { + S7Client client = getConnection(); + try { + int sizeToRead = 1; + byte[] buffer = new byte[sizeToRead]; + S7.SetBitAt(buffer, 0, digit, value); + int result = client.WriteArea(S7.S7AreaDB, dbNumber, addressMap.get(startOffset), sizeToRead, buffer); + if (result == 0) { + log.info("写入成功位置:" + startOffset + ":" + digit); + return true; + } else { + updateConnection(client); + log.info("写入失败,错误码: " + result); + return false; + } + } catch (Exception e) { + log.error("连接池报错", e); + } finally { + returnConnection(client); + } + return false; + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/cron/ZlmService.java b/web/src/main/java/com/zhehekeji/web/service/cron/ZlmService.java index 958b7a9..8c5ab2c 100644 --- a/web/src/main/java/com/zhehekeji/web/service/cron/ZlmService.java +++ b/web/src/main/java/com/zhehekeji/web/service/cron/ZlmService.java @@ -157,14 +157,14 @@ public class ZlmService { String url = buildUrl(zlmApiUrl + "getMp4RecordFile", params); String response = get(url); - log.info("getMp4RecordFiles camera:{} period:{} response:{}", camera.getId(), period, response); +// log.info("getMp4RecordFiles camera:{} period:{} response:{}", camera.getId(), period, response); try { Map result = JsonUtils.parseObject(response, Map.class); if (result != null && Integer.parseInt(result.get("code").toString()) == 0) { Map data = (Map) result.get("data"); String rootPath = data.get("rootPath").toString(); - rootPath = rootPath.replace("E:\\\\data", "http://127.0.0.1:9007/api"); + rootPath = rootPath.replace("E:\\data", "http://127.0.0.1:9007/api"); List paths = (List) data.get("paths"); // 构建完整路径 @@ -217,14 +217,11 @@ public class ZlmService { if (!recordFiles.isEmpty()) { // 返回最新(最后)的录像文件 String latestFile = recordFiles.get(recordFiles.size() - 1); - log.info("获取到最新录像文件: camera:{}, file:{}", camera.getId(), latestFile); +// log.info("获取到最新录像文件: camera:{}, file:{}", camera.getId(), latestFile); // 转换为相对路径(如果需要) // 假设保存路径配置中的 mp4Path 是录像根目录 - String basePath = configProperties.getSavePath().getMp4Path(); - if (latestFile.startsWith(basePath)) { - return latestFile.substring(basePath.length()); - } + return latestFile; } @@ -240,7 +237,7 @@ public class ZlmService { public void startRecord(Camera camera, int recordSeconds) { // 检查是否已经在录制中 if (isRecording(camera)) { - log.warn("camera:{} 已经在录制中,", camera.getId()); + log.error("camera:{} 已经在录制中,", camera.getId()); } @@ -273,7 +270,7 @@ public class ZlmService { * @param camera 摄像机对象 */ public void startRecord(Camera camera) { - startRecord(camera, 15); + startRecord(camera, 10); } }