From 98e5df51cf26b3c0d45e0f7b5e843251d7ceb7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LAPTOP-S9HJSOEB=5C=E6=98=8A=E5=A4=A9?= Date: Tue, 21 May 2024 18:11:01 +0800 Subject: [PATCH] =?UTF-8?q?ftp=E5=86=99=E5=87=BA=E6=9D=A5=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/pom.xml | 6 +- .../com/zhehekeji/web/entity/CheckLog.java | 3 + .../zhehekeji/web/service/InitService.java | 3 + .../com/zhehekeji/web/service/PlcService.java | 24 +++- .../zhehekeji/web/service/client/Decoder.java | 26 ++++ .../zhehekeji/web/service/client/Encoder.java | 2 +- .../web/service/client/NettyServer.java | 2 +- .../web/service/client/TransmissionPojo.java | 10 +- .../client/image/ImageQueueReplacer.java | 121 +++++++++++------- .../web/service/client/image/ImageSaver.java | 86 +++++++++++-- web/src/main/resources/application-prod.yml | 4 +- web/src/main/resources/application-test.yml | 2 +- 12 files changed, 219 insertions(+), 70 deletions(-) diff --git a/web/pom.xml b/web/pom.xml index f6e84a7..b956420 100644 --- a/web/pom.xml +++ b/web/pom.xml @@ -18,7 +18,11 @@ - + + commons-net + commons-net + 3.6 + com.zhehekeji base-assembly diff --git a/web/src/main/java/com/zhehekeji/web/entity/CheckLog.java b/web/src/main/java/com/zhehekeji/web/entity/CheckLog.java index 2d412c2..049b5f3 100644 --- a/web/src/main/java/com/zhehekeji/web/entity/CheckLog.java +++ b/web/src/main/java/com/zhehekeji/web/entity/CheckLog.java @@ -46,6 +46,9 @@ public class CheckLog { private Integer status; + @TableId(type = IdType.AUTO) + private Integer taskId; + private String pic; diff --git a/web/src/main/java/com/zhehekeji/web/service/InitService.java b/web/src/main/java/com/zhehekeji/web/service/InitService.java index 307609d..17731b6 100644 --- a/web/src/main/java/com/zhehekeji/web/service/InitService.java +++ b/web/src/main/java/com/zhehekeji/web/service/InitService.java @@ -13,6 +13,7 @@ import com.zhehekeji.web.lib.joyware.NetSDKLib; import com.zhehekeji.web.mapper.CameraMapper; import com.zhehekeji.web.mapper.SensorGunMapper; import com.zhehekeji.web.mapper.StreetMapper; +import com.zhehekeji.web.service.client.NettyServer; import com.zhehekeji.web.service.ksec.KsecNettyClient; import com.zhehekeji.web.service.robotic.NettyClient; import com.zhehekeji.web.service.sick.SickNettyClient; @@ -116,6 +117,8 @@ public class InitService implements ApplicationRunner { log.error("ksec no config"); } } + NettyServer nettyServer = new NettyServer(); + nettyServer.CreateNettyServer(9040); TaskDelayExecutor.runMp4DownloadExecutor(); } 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 8f43b06..421ebca 100644 --- a/web/src/main/java/com/zhehekeji/web/service/PlcService.java +++ b/web/src/main/java/com/zhehekeji/web/service/PlcService.java @@ -12,6 +12,8 @@ import com.zhehekeji.web.pojo.CameraPtzPojo; import com.zhehekeji.web.pojo.OrderVO; import com.zhehekeji.web.service.RFID.RFIDMap; import com.zhehekeji.web.service.RFID.RFIDSocket; +import com.zhehekeji.web.service.client.ClientChanel; +import com.zhehekeji.web.service.client.image.ImageSaver; import com.zhehekeji.web.service.damLightSource.JYDAMEquip; import com.zhehekeji.web.service.damLightSource.JYDamHelper; import com.zhehekeji.web.service.hikLightSource.HikControlSocket; @@ -642,14 +644,15 @@ public class PlcService { long startTime = System.currentTimeMillis(); Street street = streetService.getStreetByPlcId(plcCmdInfo.getPlcId()); - CronTab.putTime(street.getId()); + //CronTab.putTime(street.getId()); List lightSources = lightSourceMapper.selectList(new QueryWrapper().eq("street_id",street.getId())); lightSources.forEach(lightSource -> { HikControlSocket.openLight(lightSource.getIp(),lightSource.getPort(),configProperties.getLightSource().getIndex(),1); }); - Integer cameraId = getCameraByPlcCmd(plcCmdInfo,plcCmdInfo.getLeftRight1()); - //蜜雪冰城拍摄货物顶部时用同侧相机 + //南通通威拍摄货物顶部时按列号的单双来判断单数左侧 + Integer cameraId = getCameraByPlcCmd(plcCmdInfo,plcCmdInfo.getColumn1()%2==1?1:2); + Integer leftRightTop = plcCmdInfo.getLeftRight1() == 1 ? 2 : 1; Integer cameraIdTop = getCameraByPlcCmd(plcCmdInfo, leftRightTop); if(plcCmdInfo.getSeparation1() == 1 && configProperties.getScanCodeMode().getTray() == 2){ @@ -731,15 +734,25 @@ public class PlcService { stockMapper.updateById(stock); log.info("stockmapper update stock info."); } - checkLog(stock); + CheckLog checkLog = checkLog(stock); // StockCheckRunnable stockCheckRunnable = new StockCheckRunnable(street,plcCmdInfo,cmdCode,stockMapper,path,checkLogMapper,configProperties.getScanCodeMode().getGoods(),wmsCode,wmsTrayCode,trayCode,trayCheck,configProperties,sensorGun); // threadPoolExecutor.execute(stockCheckRunnable); long end = System.currentTimeMillis(); long s = end - startTime; + //发送图片,触发拍照和id + imageSaver.uploadFileToFtp(street.getPlcIp(), configProperties.getSavePath().getMediaPath() + path); + ClientChanel.get(street.getPlcId()).writeAndFlush("id:"+stock.getCheckNum()); + ClientChanel.get(street.getPlcId()).writeAndFlush("tar:1"); + + + + log.info("time:{}millisecond",s); return true; } - public void checkLog(Stock stock){ + @Resource + ImageSaver imageSaver; + public CheckLog checkLog(Stock stock){ CheckLog checkLog = new CheckLog(); checkLog.setCheckNum(stock.getCheckNum()); checkLog.setPic(stock.getCheckPic()); @@ -758,6 +771,7 @@ public class PlcService { log.info("checklogmapper start insert new checklog info."); checkLogMapper.insert(checkLog); log.info("checklogmapper insert new checklog info finished."); + return checkLog; } public void truncateStock(){ stockMapper.truncate(); 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 2b48c4b..033d3d1 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 @@ -1,6 +1,10 @@ package com.zhehekeji.web.service.client; +import com.zhehekeji.core.util.Assert; +import com.zhehekeji.web.entity.Stock; import com.zhehekeji.web.lib.CameraDelayTask; +import com.zhehekeji.web.mapper.StockMapper; +import com.zhehekeji.web.pojo.stock.StockStatus; import com.zhehekeji.web.service.PlcService; import com.zhehekeji.web.service.ksec.KsecInfo; import com.zhehekeji.web.service.ksec.KsecNettyClient; @@ -11,7 +15,9 @@ import io.netty.handler.codec.DelimiterBasedFrameDecoder; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import javax.annotation.Resource; import java.nio.charset.Charset; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -21,6 +27,7 @@ import java.util.concurrent.TimeUnit; * 客户端解码器 连接用 */ @Slf4j +@Service public class Decoder extends DelimiterBasedFrameDecoder { private static final Logger tcpLogger = LoggerFactory.getLogger("tcp"); @@ -48,6 +55,8 @@ public class Decoder extends DelimiterBasedFrameDecoder { Unpooled.copiedBuffer("$".getBytes())); this.plcService = plcService; } + @Resource + StockMapper stockMapper; @Override protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { @@ -87,6 +96,23 @@ public class Decoder extends DelimiterBasedFrameDecoder { } //将新图片旧图片从队列删除,并保存相关信息 else if(RETURN_CHECK.equals(transmissionPojo.getHeader())){ + if(transmissionPojo.getLength()>10){ + Stock stock = stockInfo(stockCheck); + Assert.isTrue(stock != null && stock.getId() != null, "该货位暂时没有记录"); + Integer oldStatus = stock.getStatus(); + //Assert.isTrue(StockStatus.ERROR.getStatus().equals(oldStatus), "无需核对"); + + log.info("check stock correct, street_id:{},direction:{},side:{},row:{},column:{}", stockCheck.getStreetId(), stockCheck.getDirection(), stockCheck.getSide(), stockCheck.getRow(), stockCheck.getColumn()); + if (stockCheck.getOk() == 1) { + stock.setStatus(StockStatus.MANUAL.getStatus()); + } else { + stock.setStatus(StockStatus.ERROR.getStatus()); + } + + stockMapper.updateById(stock); + checkLog(stock); + } + diff --git a/web/src/main/java/com/zhehekeji/web/service/client/Encoder.java b/web/src/main/java/com/zhehekeji/web/service/client/Encoder.java index a523252..f9ff727 100644 --- a/web/src/main/java/com/zhehekeji/web/service/client/Encoder.java +++ b/web/src/main/java/com/zhehekeji/web/service/client/Encoder.java @@ -12,7 +12,7 @@ import java.nio.charset.StandardCharsets; * 客户端传输协议 以;结尾 */ public class Encoder extends MessageToByteEncoder { - private static String END_STRING = ">"; + private static String END_STRING = ""; private static final Logger tcpLogger = LoggerFactory.getLogger("tcp"); @Override diff --git a/web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java b/web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java index da90a3d..1e698a6 100644 --- a/web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java +++ b/web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java @@ -42,7 +42,7 @@ public class NettyServer { ch.pipeline().addLast(new Encoder()); }}); // 子处理器,用于处理workerGroup - // 启动server,并且设置8088为启动的端口号,同时启动方式为同步 + // 启动server,并且设置启动的端口号,同时启动方式为同步 ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); // 监听关闭的channel,设置位同步方式 diff --git a/web/src/main/java/com/zhehekeji/web/service/client/TransmissionPojo.java b/web/src/main/java/com/zhehekeji/web/service/client/TransmissionPojo.java index fd5b808..3f7c34d 100644 --- a/web/src/main/java/com/zhehekeji/web/service/client/TransmissionPojo.java +++ b/web/src/main/java/com/zhehekeji/web/service/client/TransmissionPojo.java @@ -14,6 +14,9 @@ public class TransmissionPojo { private String header; private String streetNumber; + //长度 + private double id; + //长度 private double length; private String[] dataArray; @@ -39,6 +42,7 @@ public class TransmissionPojo { public TransmissionPojo(String data) { + data = data.replace("<",""); this.data = data; data = data.replace(">", ""); this.dataArray = data.split(";"); @@ -47,7 +51,11 @@ public class TransmissionPojo { this.streetNumber = (dataArray[1]); if (dataArray.length > 2) { - this.length = Double.parseDouble(dataArray[2]); + this.id = Double.parseDouble(dataArray[2]); + } + if (dataArray.length > 3) { + + this.length = Double.parseDouble(dataArray[3]); } } diff --git a/web/src/main/java/com/zhehekeji/web/service/client/image/ImageQueueReplacer.java b/web/src/main/java/com/zhehekeji/web/service/client/image/ImageQueueReplacer.java index a86be05..5073126 100644 --- a/web/src/main/java/com/zhehekeji/web/service/client/image/ImageQueueReplacer.java +++ b/web/src/main/java/com/zhehekeji/web/service/client/image/ImageQueueReplacer.java @@ -1,74 +1,99 @@ package com.zhehekeji.web.service.client.image; +import com.zhehekeji.common.util.SpringContextUtil; +import com.zhehekeji.web.lib.CameraControlModule; +import com.zhehekeji.web.lib.CameraDelayTask; +import com.zhehekeji.web.lib.TaskDelayExecutor; +import com.zhehekeji.web.service.PlcService; +import com.zhehekeji.web.service.client.TransmissionPojo; +import com.zhehekeji.web.service.ksec.KsecInfo; +import com.zhehekeji.web.service.ksec.KsecNettyClient; + import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.util.concurrent.LinkedBlockingQueue; +import java.time.LocalDateTime; +import java.util.concurrent.*; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; import javax.imageio.ImageIO; public class ImageQueueReplacer { - private final String targetDirectory; - private final LinkedBlockingQueue imageQueue; - private final Semaphore semaphore; - private volatile boolean running = true; + // 延时队列,存放CameraDelayTask任务 + public static DelayQueue cameraDelayTasks = new DelayQueue<>(); + // 线程池,用于执行延时任务 + private static ExecutorService exec = Executors.newFixedThreadPool(1); - public ImageQueueReplacer(String targetDirectory) { - this.targetDirectory = targetDirectory; - this.imageQueue = new LinkedBlockingQueue<>(); - this.semaphore = new Semaphore(0); // 初始信号量为0 - } + /** + * 向延时队列中添加一个任务。 + * + * @param cameraPlcId 相机的PLC编号 + * @param getPhotoCommand 获取照片的命令 + * @param time 任务延时时间 + */ + public static void addCameraDelayTask(String cameraPlcId, String getPhotoCommand, long time) { - public void queueImage(BufferedImage image) { - imageQueue.add(image); - semaphore.release(); // 图片入队时释放信号量,告知有图片可处理 } - public void startAutoReplace(String imageName) { - Thread autoReplaceThread = new Thread(() -> { - while (running) { - try { - semaphore.acquire(); // 等待信号量,表明有图片或开始指令 - BufferedImage imageToReplace; - while ((imageToReplace = imageQueue.poll()) != null) { - replaceImage(imageToReplace, imageName); - } - // 队列为空时,仅在第一次启动时执行覆盖(根据需求调整) - if (semaphore.availablePermits() == 0 && imageQueue.isEmpty()) { - replaceImage(null, imageName); // 使用null表示无图片可覆盖 - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } + /** + * 根据命令和相机PLC编号从延时队列中移除任务。 + * + * @param getPhotoCommand 获取巷道编号 + * @param cameraPlcId 命令行 + */ + public static void remove(String getPhotoCommand, String cameraPlcId) { + cameraPlcId = cameraPlcId.intern(); + synchronized (cameraPlcId) { + // 将延时队列中的任务转换为数组,便于遍历和移除 + Object[] cameraDelayTask = cameraDelayTasks.toArray(); + for (Object cameraDelayTask1 : cameraDelayTask) { + CameraDelayTask cameraDelayTask2 = (CameraDelayTask) cameraDelayTask1; + } - }); - autoReplaceThread.setDaemon(true); - autoReplaceThread.start(); + } } - private void replaceImage(BufferedImage image, String imageName) { - if (image != null) { - File outputFile = new File(targetDirectory, imageName); - try { - ImageIO.write(image, "jpg", outputFile); - System.out.println("Image saved: " + outputFile.getAbsolutePath()); - } catch (IOException e) { - System.err.println("Error saving image: " + e.getMessage()); - } - } else { - System.out.println("No image available to replace."); + /** + * 获取延时队列中最早的任务,并将其从队列中移除。 + * + * @param cameraPlcId 相机的PLC编号,该参数未被使用 + * @return 返回最早的任务,如果队列为空则返回null + */ + public static CameraDelayTask getNext(String cameraPlcId) { + Object[] cameraDelayTask = cameraDelayTasks.toArray(); + if (cameraDelayTask.length > 0) { + CameraDelayTask cameraDelayTask2 = (CameraDelayTask) cameraDelayTask[0]; + return cameraDelayTask2; } + return null; + } - public void stop() { - running = false; + /** + * 启动延时任务的执行器。 + */ + public static void runExecutor() { + exec.execute(new ImageQueueReplacer.Consumer()); } + + /** + * Consumer内部类,实现Runnable接口,用于消费延时队列中的任务。 + */ + private static class Consumer implements Runnable { + /** + * 无限循环,不断从延时队列中取出任务并执行。 + */ + @Override + public void run() { + while (true) { + + } + } + } + + } diff --git a/web/src/main/java/com/zhehekeji/web/service/client/image/ImageSaver.java b/web/src/main/java/com/zhehekeji/web/service/client/image/ImageSaver.java index 44c4b20..16ec48e 100644 --- a/web/src/main/java/com/zhehekeji/web/service/client/image/ImageSaver.java +++ b/web/src/main/java/com/zhehekeji/web/service/client/image/ImageSaver.java @@ -1,28 +1,94 @@ package com.zhehekeji.web.service.client.image; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.net.ftp.FTP; +import org.apache.commons.net.ftp.FTPClient; +import org.springframework.stereotype.Service; import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import javax.imageio.ImageIO; @Slf4j +@Service public class ImageSaver { - private final String targetDirectory; - public ImageSaver(String targetDirectory) { - this.targetDirectory = targetDirectory; + + private static final int BUFFER_SIZE = 4096; + public void copyFileToFolder(String sourceFilePath, String destinationFolderPath, String fileName) { + Path source = Paths.get(sourceFilePath); + Path destination = Paths.get(destinationFolderPath, fileName); + + try { + Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING); + System.out.println("文件复制成功:" + source + " -> " + destination); + } catch (IOException e) { + System.err.println("文件复制过程中发生错误:" + e.getMessage()); + e.printStackTrace(); + } + } + + public static void main(String[] args) { + ImageSaver imageSaver = new ImageSaver(); + imageSaver.uploadFileToFtp("192.168.1.52", + "d:\\\\data\\media\\14/3-7/20240524/20240524144747-3-7.jpg"); } - public void saveImage(BufferedImage image, String imageName) { - File outputFile = new File(targetDirectory, imageName); + public void uploadFileToFtp(String ip,String localFilePath) + { try { - ImageIO.write(image, "jpg", outputFile); - System.out.println("Image saved: " + outputFile.getAbsolutePath()); + Thread.sleep(120); + uploadFileToFtp(ip,"administrator","",localFilePath,"/compute.jpg"); } catch (IOException e) { - System.err.println("Error saving image: " + e.getMessage()); + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); } } + public void uploadFileToFtp(String ip,String username, String password,String localFilePath, String remoteFilePath ) throws IOException { + FTPClient ftpClient = new FTPClient(); + try { + // 连接到FTP服务器 + ftpClient.connect(ip); + boolean log =ftpClient.login(username, password); + + ftpClient.enterLocalPassiveMode(); // 设置被动模式,适应大多数网络环境 + ftpClient.setFileType(FTP.BINARY_FILE_TYPE); // 设置文件传输模式为二进制 + + // 读取本地文件 + File localFile = new File(localFilePath); + InputStream inputStream = new FileInputStream(localFile); + System.out.println(ftpClient.isConnected()); + + // 上传文件 + boolean uploadSuccess = ftpClient.storeFile(remoteFilePath, inputStream); + if (uploadSuccess) { + System.out.println("文件上传成功:" + remoteFilePath); + } else { + System.out.println("文件上传失败:" + remoteFilePath); + } + + // 关闭输入流 + inputStream.close(); + } catch (IOException ex) { + System.err.println("文件上传过程中发生错误:" + ex.getMessage()); + throw ex; + } finally { + // 登出并断开连接 + if (ftpClient.isConnected()) { + try { + ftpClient.logout(); + ftpClient.disconnect(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + } + } diff --git a/web/src/main/resources/application-prod.yml b/web/src/main/resources/application-prod.yml index 654b847..8767713 100644 --- a/web/src/main/resources/application-prod.yml +++ b/web/src/main/resources/application-prod.yml @@ -14,7 +14,7 @@ spring: testWhileIdle: false timeBetweenEvictionRunsMillis: 60000 type: com.alibaba.druid.pool.DruidDataSource - url: jdbc:mysql://127.0.0.1:3306/lia_duoji?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 + url: jdbc:mysql://127.0.0.1:3306/lia_duoji_local?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: root validationQuery: SELECT 1 FROM DUAL # --------本服务端口号 @@ -64,7 +64,7 @@ savePath: # ------------服务端类型 0:TCP(罗伯泰克) 1:KSEC(JSON)(昆船) serverMode: 1 ksec: - ip: 192.168.168.11 + ip: 127.0.0.1 port: 8001 #断点重连的次数:-1->不断重连 reconnectNum: -1 diff --git a/web/src/main/resources/application-test.yml b/web/src/main/resources/application-test.yml index 82e525f..dc570c5 100644 --- a/web/src/main/resources/application-test.yml +++ b/web/src/main/resources/application-test.yml @@ -14,7 +14,7 @@ spring: testWhileIdle: false timeBetweenEvictionRunsMillis: 60000 type: com.alibaba.druid.pool.DruidDataSource - url: jdbc:mysql://127.0.0.1:3306/lia_duoji?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 + url: jdbc:mysql://127.0.0.1:3306/lia_duoji_local?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 username: root validationQuery: SELECT 1 FROM DUAL # --------本服务端口号