zouyu
8 天以前 bf95078dab81dcd0639fdb1a41e998b31c940fb1
采集器调整3
已添加5个文件
已重命名2个文件
已修改7个文件
已删除1个文件
806 ■■■■ 文件已修改
pom.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/config/SerialPortStartupRunner.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/config/TomcatConfig.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/controller/DocxController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/handler/GlobalExceptionHandler.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/handler/SerialPortListener.java 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/pojo/Chemical.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/pojo/TestBatch.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/service/DocxService.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/service/impl/DocxServiceImpl.java 149 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/util/CharsetChangeUtil.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/util/Result.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/util/TakeWords.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/chinaztt/mes/docx/util/XMLFileListener.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml
@@ -15,6 +15,16 @@
    </properties>
    <dependencies>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.19.0</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.23</version>
@@ -62,7 +72,12 @@
            <artifactId>poi-ooxml-full</artifactId>
            <version>5.2.5</version>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>12.4.1.jre8</version> <!-- é€‚配 Java 8,其他版本可调整 -->
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
src/main/java/com/chinaztt/mes/docx/config/SerialPortStartupRunner.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.chinaztt.mes.docx.config;
import com.chinaztt.mes.docx.handler.SerialPortListener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
 * é¡¹ç›®å¯åŠ¨å®ŒæˆåŽæ‰§è¡Œä¸²å£ç›‘å¬
 */
@Slf4j
@Component
public class SerialPortStartupRunner implements ApplicationRunner {
    @Value("${serialPort.enable}")
    public Boolean enableSerialPort;
    @Resource
    private SerialPortListener serialPortListenerService;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Override
    public void run(ApplicationArguments args) {
        log.info("===== SpringBoot é¡¹ç›®å¯åŠ¨å®Œæˆï¼Œå¼€å§‹åˆå§‹åŒ–ä¸²å£ç›‘å¬ =====");
        // å¯åŠ¨ä¸²å£ç›‘å¬ï¼ˆæ ¸å¿ƒè°ƒç”¨ï¼‰
        if(enableSerialPort)threadPoolTaskExecutor.execute(() -> serialPortListenerService.serialPortDataListener());
    }
}
src/main/java/com/chinaztt/mes/docx/config/TomcatConfig.java
ÎļþÃû´Ó src/main/java/com/chinaztt/mes/docx/constant/TomcatConfig.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.chinaztt.mes.docx.constant;
package com.chinaztt.mes.docx.config;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
src/main/java/com/chinaztt/mes/docx/controller/DocxController.java
@@ -2,7 +2,7 @@
import com.chinaztt.mes.docx.dto.GetFileDto;
import com.chinaztt.mes.docx.service.DocxService;
import com.chinaztt.mes.docx.util.R;
import com.chinaztt.mes.docx.util.Result;
import net.sourceforge.tess4j.TesseractException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -21,19 +21,18 @@
    private DocxService docxService;
    @GetMapping("/getFile")
    public R<?> getFile(GetFileDto getFileDto) throws IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException, TesseractException {
    public Result<?> getFile(GetFileDto getFileDto) throws IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException, TesseractException {
        return docxService.getFile(getFileDto);
    }
    @GetMapping("/moveFile")
    public R<?> moveFile(String startFilePath, String endFilePath, String fileType) {
    public Result<?> moveFile(String startFilePath, String endFilePath, String fileType) {
        return docxService.moveFile(startFilePath, endFilePath, fileType);
    }
    @GetMapping("/test")
    public void test() {
         docxService.test();
    @GetMapping("/readXml")
    public void readXml() {
         docxService.readXml();
    }
}
src/main/java/com/chinaztt/mes/docx/handler/GlobalExceptionHandler.java
@@ -1,13 +1,13 @@
package com.chinaztt.mes.docx.handler;
import com.chinaztt.mes.docx.util.R;
import com.chinaztt.mes.docx.util.Result;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler({Exception.class})
    public R<?> exception(Exception e) {
        return R.failed(e.getMessage());
    public Result<?> exception(Exception e) {
        return Result.failed(e.getMessage());
    }
}
src/main/java/com/chinaztt/mes/docx/handler/SerialPortListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,145 @@
package com.chinaztt.mes.docx.handler;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Component
public class SerialPortListener {
    // ä¸²å£æ ¸å¿ƒå¯¹è±¡
    private static SerialPort serialPort;
    // å®šä¹‰ç¼“冲区,暂存零散数据
    private static final StringBuilder dataBuffer = new StringBuilder();
    // å®šä¹‰æ•°æ®ç»“束标志
    private static final String END_MARK = "g";
    public List<Double> dataList = new ArrayList<>(6);
    private static final int MAX_COUNT = 6;
    @Value("${serialPort.listenName}")
    public String listenName;
    // æ ‡è®°ç›‘听是否运行
    private volatile boolean isListening = false;
    public void serialPortDataListener(){
        // åŒé‡æ ¡éªŒé”ï¼Œé¿å…é‡å¤å¯åЍ
        if (isListening) {
            log.warn("串口 {} ç›‘听已启动,无需重复执行", listenName);
            return;
        }
        synchronized (this) {
            if (isListening) {
                return;
            }
            isListening = true;
        }
        // 2. é€‰æ‹©ç›®æ ‡ä¸²å£
        String targetPortName = listenName;
        serialPort = SerialPort.getCommPort(targetPortName);
        // 3. é…ç½®ä¸²å£å‚数(需与设备端一致)
        serialPort.setBaudRate(9600);       // æ³¢ç‰¹çއ
        serialPort.setNumDataBits(8);       // æ•°æ®ä½
        serialPort.setNumStopBits(1);       // åœæ­¢ä½
        serialPort.setParity(SerialPort.NO_PARITY); // æ ¡éªŒä½ï¼ˆæ— æ ¡éªŒï¼‰
        serialPort.setFlowControl(SerialPort.NO_PARITY); // å…³é—­æµæŽ§
        serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1000, 0); // è¯»å–è¶…æ—¶
        // 4. æ‰“开串口
        if (serialPort.openPort()) {
            log.info("串口打开成功:{}",targetPortName);
        } else {
            log.error("串口打开失败!请检查串口是否被占用或名称是否正确");
            isListening = false;
            return;
        }
        // 5. æ³¨å†Œä¸²å£æ•°æ®ç›‘听器(异步读取,避免阻塞主线程)
        try {
            serialPort.addDataListener(new SerialPortDataListener() {
                // æŒ‡å®šç›‘听的事件类型:数据可用时触发
                @Override
                public int getListeningEvents() {
                    return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
                }
                // æ•°æ®å¯ç”¨æ—¶çš„处理逻辑
                @Override
                public void serialEvent(SerialPortEvent event) {
                    if (event.getEventType() == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
                        // è¯»å–缓冲区中的所有字节
                        byte[] buffer = new byte[serialPort.bytesAvailable()];
                        int readBytes = serialPort.readBytes(buffer, buffer.length);
                        if (readBytes > 0) {
                            // æŠŠæœ¬æ¬¡æ”¶åˆ°çš„零散数据转成字符串,追加到缓冲区
                            String received = new String(buffer, 0, readBytes, StandardCharsets.UTF_8);
                            dataBuffer.append(received);
                            // æ£€æŸ¥ç¼“冲区是否包含“结束标志”,循环处理所有完整数据
                            int endIndex = dataBuffer.indexOf(END_MARK);
                            while (endIndex != -1) {
                                // æå–“开头到结束标志”的完整数据
                                String completeData = dataBuffer.substring(0, endIndex).replace(" ", "").replace("\n","");
                                // æ‰“印/处理合并后的完整数据
                                double parseDouble = Double.parseDouble(completeData);
                                log.info("接收到的数据-->{}",parseDouble);
                                if(dataList.size()<MAX_COUNT){
                                    dataList.add(parseDouble);
                                }
                                // ç§»é™¤ç¼“冲区中已处理的部分(保留剩余未完成的内容)
                                dataBuffer.delete(0, endIndex + END_MARK.length());
                                // ç»§ç»­æ£€æŸ¥ç¼“冲区是否还有其他完整数据
                                endIndex = dataBuffer.indexOf(END_MARK);
                            }
                        }
                    }
                }
            });
        } catch (Exception e) {
            log.warn("串口监听子线程被中断", e);
            Thread.currentThread().interrupt();
        }
    }
    /**
     * å…³é—­ä¸²å£
     */
    private void closeSerialPort() {
        if (serialPort != null && serialPort.isOpen()) {
            serialPort.removeDataListener(); // ç§»é™¤ç›‘听
            serialPort.closePort(); // å…³é—­ä¸²å£
            log.info("串口 {} å·²å…³é—­", listenName);
        }
        isListening = false;
    }
    /**
     * é¡¹ç›®åœæ­¢æ—¶å…³é—­ä¸²å£
     */
    @PreDestroy
    public void destroy() {
        log.info("项目停止,关闭串口 {} ç›‘听", listenName);
        isListening = false; // ç»ˆæ­¢å­çº¿ç¨‹å¾ªçޝ
        closeSerialPort();
    }
}
src/main/java/com/chinaztt/mes/docx/pojo/Chemical.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package com.chinaztt.mes.docx.pojo;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * @Description åŒ–学成分信息
 * @Author fz
 **/
@Data
public class Chemical implements Serializable {
    private String name;// å…ƒç´ å
    private String value = "-";// æ£€éªŒæ•°å€¼
    private String record = "-";// è®°å½•数值
    private String detail = "-";// æ ‡å‡†æ˜Žç»†
    private String result = "-";// æ£€éªŒç»“论
    private Date testTime;
    private Date equipmentTime;
}
src/main/java/com/chinaztt/mes/docx/pojo/TestBatch.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package com.chinaztt.mes.docx.pojo;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
 * @Description å…‰è°±ä»ªä¸Šçš„æ£€éªŒä¿¡æ¯
 * @Author fz
 **/
@Data
public class TestBatch implements Serializable {
    private String batchNo;// æ£€éªŒæ‰¹æ¬¡ä¿¡æ¯
    private Date equipmentTime;// è®°å½•æ—¶é—´
    private List<Chemical> chemicals;// åŒ–学成分列表
}
src/main/java/com/chinaztt/mes/docx/service/DocxService.java
@@ -1,17 +1,17 @@
package com.chinaztt.mes.docx.service;
import com.chinaztt.mes.docx.dto.GetFileDto;
import com.chinaztt.mes.docx.util.R;
import com.chinaztt.mes.docx.util.Result;
import net.sourceforge.tess4j.TesseractException;
import java.io.IOException;
import java.sql.SQLException;
public interface DocxService {
    R<?> getFile(GetFileDto getFileDto) throws IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException, TesseractException;
    Result<?> getFile(GetFileDto getFileDto) throws IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException, TesseractException;
    R<?> moveFile(String startFilePath, String endFilePath, String fileType);
    Result<?> moveFile(String startFilePath, String endFilePath, String fileType);
    void test();
    void readXml();
}
src/main/java/com/chinaztt/mes/docx/service/impl/DocxServiceImpl.java
@@ -3,79 +3,93 @@
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import com.chinaztt.mes.docx.dto.GetFileDto;
import com.chinaztt.mes.docx.handler.SerialPortListener;
import com.chinaztt.mes.docx.pojo.TestBatch;
import com.chinaztt.mes.docx.service.DocxService;
import com.chinaztt.mes.docx.util.R;
import com.chinaztt.mes.docx.util.XMLFileListener;
import com.chinaztt.mes.docx.util.Result;
import com.chinaztt.mes.docx.util.TakeWords;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.TesseractException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@Service
@Slf4j
public class DocxServiceImpl implements DocxService {
    @Autowired
    private SerialPortListener serialPortListener;
    @Override
    public R<?> getFile(GetFileDto getFileDto) throws IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException, TesseractException {
    public Result<?> getFile(GetFileDto getFileDto) throws IOException, SQLException, ClassNotFoundException, InstantiationException, IllegalAccessException, TesseractException {
        String fileExtension = getFileDto.getFileExtension();
        if(StringUtils.equals(".pngInExcel",getFileDto.getFileExtension())){
            fileExtension = ".xls";
        }
        File file = getFileStart(getFileDto.getFilePath(), fileExtension);
        if (file != null && !file.exists()) {
            return R.failed("未查询到该路径:" + getFileDto.getFilePath() + "下存在:" + getFileDto.getFileExtension() + "结尾的文件!");
            return Result.failed("未查询到该路径:" + getFileDto.getFilePath() + "下存在:" + getFileDto.getFileExtension() + "结尾的文件!");
        }
        switch (getFileDto.getFileExtension()) {
            case ".docx":
                return R.ok(TakeWords.readWordFile(file));
                return Result.ok(TakeWords.readWordFile(file));
            case ".pngInExcel":
                return R.ok(TakeWords.readPngContextInExcel(file));
                return Result.ok(TakeWords.readPngContextInExcel(file));
            case ".xls":
            case ".xlsx":
                try {
                    return R.ok(TakeWords.readExcelFile(file));
                    return Result.ok(TakeWords.readExcelFile(file));
                } catch (Exception e) {
                    return R.failed("另一个程序正在使用此文件,无法进行数据采集。");
                    return Result.failed("另一个程序正在使用此文件,无法进行数据采集。");
                }
            case ".txt":
                return R.ok(TakeWords.readTxtFile(file));
                return Result.ok(TakeWords.readTxtFile(file));
            case ".csv":
                return R.ok(TakeWords.readCsvFile(file));
                return Result.ok(TakeWords.readCsvFile(file));
            case ".mdb":
                if (ObjectUtil.isEmpty(getFileDto.getDbFileName()) || Objects.equals(getFileDto.getDbFileName(), "null")) {
                    return R.failed("未配置.mdb采集文件名称!");
                    return Result.failed("未配置.mdb采集文件名称!");
                }
                return R.ok(TakeWords.readMdbFile(file, getFileDto));
                return Result.ok(TakeWords.readMdbFile(file, getFileDto));
            case ".db":
                if (ObjectUtil.isEmpty(getFileDto.getDbFileName()) || Objects.equals(getFileDto.getDbFileName(), "null")) {
                    return R.failed("未配置.db采集文件名称!");
                    return Result.failed("未配置.db采集文件名称!");
                }
                return R.ok(TakeWords.readDbFile(file, getFileDto));
                return Result.ok(TakeWords.readDbFile(file, getFileDto));
            case ".mysql":
                if (ObjectUtil.isEmpty(getFileDto.getDbFileName()) || Objects.equals(getFileDto.getDbFileName(), "null")) {
                    return R.failed("未配置.mysql采集文件名称!");
                    return Result.failed("未配置.mysql采集文件名称!");
                }
                return R.ok(TakeWords.getMysqlFile(getFileDto));
                return Result.ok(TakeWords.getMysqlFile(getFileDto));
            case ".png":
                return R.ok(TakeWords.readPngFile(file));
                return Result.ok(TakeWords.readPngFile(file));
            case ".mqtt":
                return R.ok("mqtt---");
                return Result.ok("mqtt---");
            case ".serialPort":
                List<Double> valList = new ArrayList<>(serialPortListener.dataList);
                serialPortListener.dataList.clear();
                return Result.ok(valList);
            case ".sqlserver":
                if (ObjectUtil.isEmpty(getFileDto.getDbFileName()) || Objects.equals(getFileDto.getDbFileName(), "null")) {
                    return Result.failed("未配置.sqlserver采集文件名称!");
                }
                return Result.ok(TakeWords.readSqlServerDataBase(getFileDto));
            default:
                return R.failed("后缀名配置错误!");
                return Result.failed("后缀名配置错误!");
        }
    }
    @Override
    public R<?> moveFile(String startFilePath, String endFilePath, String fileType) {
    public Result<?> moveFile(String startFilePath, String endFilePath, String fileType) {
        // æºæ–‡ä»¶è·¯å¾„
        File startFile= getFileStart(startFilePath, fileType);
        // ç›®çš„目录路径
@@ -90,15 +104,14 @@
        try {
        // è°ƒç”¨File类的核心方法renameTo
            if (startFile.renameTo(endFile)) {
                return R.ok("文件移动成功!");
                return Result.ok("文件移动成功!");
            } else {
                return R.failed("文件移动失败!");
                return Result.failed("文件移动失败!");
            }
        }catch(Exception e) {
            return R.failed("文件移动出现异常!");
            return Result.failed("文件移动出现异常!");
        }
    }
    public static File getLatestFile(List<File> files) {
        File latestFile = null;
@@ -140,84 +153,10 @@
    }
    @Override
    public void test(){
        // 1. èŽ·å–æ‰€æœ‰å¯ç”¨çš„ä¸²å£
        SerialPort[] ports = SerialPort.getCommPorts();
        if (ports.length == 0) {
            System.err.println("未找到可用的串口!");
            return;
        }
        // æ‰“印所有可用串口(供用户选择)
        System.out.println("可用串口列表:");
        SerialPort targetPort = null;
        for (int i = 0; i < ports.length; i++) {
            if(ports[i].getSystemPortName().equals("COM7")){
                targetPort = ports[i];
            }
            System.out.printf("%d: %s (%s)\n", i + 1, ports[i].getSystemPortName(), ports[i].getPortDescription());
        }
        // 2. é€‰æ‹©ç›®æ ‡ä¸²å£ï¼ˆç¤ºä¾‹ï¼šé€‰æ‹©ç¬¬ä¸€ä¸ªä¸²å£ï¼‰
        System.out.println("\n选择的串口:" + targetPort.getSystemPortName());
        // 3. é…ç½®ä¸²å£å‚数(必须与设备一致,否则通信失败)
        targetPort.setComPortParameters(
                9600,                // æ³¢ç‰¹çŽ‡ï¼ˆå¸¸è§å€¼ï¼š9600、19200、38400、115200)
                8,                   // æ•°æ®ä½ï¼ˆé€šå¸¸ä¸º 8)
                SerialPort.ONE_STOP_BIT,  // åœæ­¢ä½ï¼ˆ1 ä½ï¼‰
                SerialPort.NO_PARITY     // æ ¡éªŒä½ï¼ˆæ— æ ¡éªŒï¼‰
        );
        targetPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 100, 0); // è¯»å–超时设置
        // 4. æ‰“开串口
        if (!targetPort.openPort()) {
            System.err.println("串口打开失败!请检查端口是否被占用或权限是否足够。");
            return;
        }
        System.out.println("串口打开成功!");
        // 5. æ³¨å†Œæ•°æ®ç›‘听(异步读取数据)
        SerialPort finalTargetPort = targetPort;
        targetPort.addDataListener(new SerialPortDataListener() {
            @Override
            public int getListeningEvents() {
                return SerialPort.LISTENING_EVENT_DATA_AVAILABLE; // ç›‘听数据可用事件
            }
            @Override
            public void serialEvent(SerialPortEvent event) {
                System.out.println("asasa"+event.toString());
                if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
                    return;
                }
                // è¯»å–可用数据
                byte[] readBuffer = new byte[finalTargetPort.bytesAvailable()];
                int bytesRead = finalTargetPort.readBytes(readBuffer, readBuffer.length);
                if (bytesRead > 0) {
                    // è½¬æ¢ä¸ºå­—符串(根据设备编码调整,常见:UTF-8、GBK)
                    String data = new String(readBuffer, 0, bytesRead, StandardCharsets.UTF_8);
                    System.out.printf("收到数据(%d å­—节):%s", bytesRead, data);
                }
            }
        });
        // 6. ä¿æŒç¨‹åºè¿è¡Œï¼ˆé¿å…ä¸»çº¿ç¨‹é€€å‡ºï¼‰
        try {
            while (true) {
                Thread.sleep(1000); // ä¸»çº¿ç¨‹ä¼‘眠,不影响监听线程
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            System.out.println("程序被中断。");
        } finally {
            // 7. å…³é—­ä¸²å£ï¼ˆç¨‹åºé€€å‡ºæ—¶é‡Šæ”¾èµ„源)
            targetPort.closePort();
            System.out.println("串口已关闭。");
        }
    public void readXml() {
        XMLFileListener fileListener = new XMLFileListener();
        List<TestBatch> testBatches = fileListener.explainXml(new File("C:\\Users\\27233\\Desktop\\251117数采文件\\数采文件\\光谱仪Results_20251018133246.xml"));
        testBatches.forEach(System.out::println);
    }
}
src/main/java/com/chinaztt/mes/docx/util/CharsetChangeUtil.java
ÎļþÒÑɾ³ý
src/main/java/com/chinaztt/mes/docx/util/Result.java
ÎļþÃû´Ó src/main/java/com/chinaztt/mes/docx/util/R.java ÐÞ¸Ä
@@ -9,7 +9,11 @@
import java.io.Serializable;
public class R<T> implements Serializable {
/**
 * é€šç”¨è¿”回对象
 * @param <T>
 */
public class Result<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    // "返回标记:成功标记=0,失败标记=1"
    private int code;
@@ -18,36 +22,36 @@
    // "数据"
    private T data;
    public static <T> R<T> ok() {
    public static <T> Result<T> ok() {
        return restResult(null, CommonConstants.SUCCESS, (String) null);
    }
    public static <T> R<T> ok(T data) {
    public static <T> Result<T> ok(T data) {
        return restResult(data, CommonConstants.SUCCESS, (String) null);
    }
    public static <T> R<T> ok(T data, String msg) {
    public static <T> Result<T> ok(T data, String msg) {
        return restResult(data, CommonConstants.SUCCESS, msg);
    }
    public static <T> R<T> failed() {
    public static <T> Result<T> failed() {
        return restResult(null, CommonConstants.FAIL, (String) null);
    }
    public static <T> R<T> failed(String msg) {
    public static <T> Result<T> failed(String msg) {
        return restResult(null, CommonConstants.FAIL, msg);
    }
    public static <T> R<T> failed(T data) {
    public static <T> Result<T> failed(T data) {
        return restResult(data, CommonConstants.FAIL, (String) null);
    }
    public static <T> R<T> failed(T data, String msg) {
    public static <T> Result<T> failed(T data, String msg) {
        return restResult(data, CommonConstants.FAIL, msg);
    }
    private static <T> R<T> restResult(T data, int code, String msg) {
        R<T> apiResult = new R();
    private static <T> Result<T> restResult(T data, int code, String msg) {
        Result<T> apiResult = new Result();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
@@ -62,10 +66,10 @@
        return "R(code=" + this.getCode() + ", msg=" + this.getMsg() + ", data=" + this.getData() + ")";
    }
    public R() {
    public Result() {
    }
    public R(int code, String msg, T data) {
    public Result(int code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
@@ -75,7 +79,7 @@
        return this.code;
    }
    public R<T> setCode(int code) {
    public Result<T> setCode(int code) {
        this.code = code;
        return this;
    }
@@ -84,7 +88,7 @@
        return this.msg;
    }
    public R<T> setMsg(String msg) {
    public Result<T> setMsg(String msg) {
        this.msg = msg;
        return this;
    }
@@ -93,7 +97,7 @@
        return this.data;
    }
    public R<T> setData(T data) {
    public Result<T> setData(T data) {
        this.data = data;
        return this;
    }
@@ -121,8 +125,8 @@
            return this;
        }
        public R<T> build() {
            return new R(this.code, this.msg, this.data);
        public Result<T> build() {
            return new Result(this.code, this.msg, this.data);
        }
        public String toString() {
src/main/java/com/chinaztt/mes/docx/util/TakeWords.java
@@ -17,7 +17,6 @@
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.ooxml.extractor.POIXMLTextExtractor;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
@@ -236,7 +235,7 @@
            if(StringUtils.isNotBlank(getFileDto.getMdbSampleCode())){
                s+=" and " + getFileDto.getMdbSampleCode() + " = '" + getFileDto.getSampleCode() + "'";
            }
            if(StringUtils.isNotBlank(getFileDto.getBushingColorField())){
            if(ObjectUtils.allNotNull(getFileDto.getBushingColorField(), getFileDto.getBushingColor()) && !getFileDto.getBushingColorField().equals("null")){
                s+=" and " + getFileDto.getBushingColorField() + " = '" + getFileDto.getBushingColor() + "'";
            }
@@ -286,7 +285,7 @@
        String table = getFileDto.getDbTable();
        // æ£€æŸ¥æ•°æ®åº“名和表名是否为空
        if (dbName == null || dbName.isEmpty() || table == null || table.isEmpty()) {
            return R.failed("数据库名或表名不能为空");
            return Result.failed("数据库名或表名不能为空");
        }
        // æ•°æ®åº“连接信息
        String url = "jdbc:mysql://localhost:3306/"+dbName+"?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true&characterEncoding=utf8";
@@ -332,7 +331,7 @@
        } catch (Exception e) {
            e.printStackTrace();
            // å‡è®¾ R ç±»æœ‰ failed æ–¹æ³•,若没有需补充实现
            return R.failed("数据库查询出错: " + e.getMessage());
            return Result.failed("数据库查询出错: " + e.getMessage());
        } finally {
            try {
                if (resultSet != null) {
@@ -371,9 +370,13 @@
            List<Object> list = new ArrayList<>();
            //转义中文
            String sampleCode = changeCharset(getFileDto.getSampleCode(), "GBK");
            String sql = "select * from " + getFileDto.getDbFileName() +
                    " where " + getFileDto.getMdbEntrustCode() + " = '" + getFileDto.getEntrustCode() +
                    "' and " + getFileDto.getMdbSampleCode() + " = '" + sampleCode + "'";
            String sql = "select * from " + getFileDto.getDbFileName() + " where 1=1";
            if(StringUtils.isNotBlank(getFileDto.getMdbEntrustCode())){
                sql+=" and " + getFileDto.getMdbEntrustCode() + " = '" + getFileDto.getEntrustCode() +"'";
            }
            if(StringUtils.isNotBlank(getFileDto.getMdbSampleCode())){
                sql+=" and " + getFileDto.getMdbSampleCode() + " = '" + sampleCode + "'";
            }
            if (ObjectUtils.allNotNull(getFileDto.getFiberOpticRibbonCode(), getFileDto.getFiberOpticRibbon()) && !getFileDto.getFiberOpticRibbonCode().equals("null")) {
                sql = sql + " and " + getFileDto.getFiberOpticRibbonCode() + " = '" + getFileDto.getFiberOpticRibbon() + "'";
            }
@@ -456,4 +459,82 @@
        }
    }
    /**
     * è¯»å–sqlserver数据
     * @param getFileDto è¯·æ±‚参数对象
     * @return
     */
    public static Object readSqlServerDataBase(GetFileDto getFileDto) {
        Map<String, Object> tableMap = new HashMap<>(16);
        // ä»Ž GetFileDto èŽ·å–æ•°æ®åº“åï¼Œå¯¹åº”ã€æ–‡ä»¶åç§°ã€‘å­—æ®µ
        String dbName = getFileDto.getDbFileName();
        String user = getFileDto.getDbUserName();
        String password = getFileDto.getDbPassword();
        // ä»Ž GetFileDto èŽ·å–æ•°æ®è¡¨åï¼Œå¯¹åº”ã€æ•°æ®åº“è¡¨åã€‘å­—æ®µ
        String table = getFileDto.getDbTable();
        // æ£€æŸ¥æ•°æ®åº“名和表名是否为空
        if (dbName == null || dbName.isEmpty() || table == null || table.isEmpty()) {
            return Result.failed("数据库名或表名不能为空");
        }
        // æ•°æ®åº“连接信息
        String url = "jdbc:sqlserver://localhost:1433;DatabaseName="+dbName+";encrypt=true;trustServerCertificate=true";
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<Map<String, Object>> dataList = new ArrayList<>();
        try {
            // å»ºç«‹è¿žæŽ¥
            connection = DriverManager.getConnection(url, user, password);
            // æž„建基础 SQL
            String sql = "SELECT * FROM "+table+" WHERE 1=1";
            if(StringUtils.isNotBlank(getFileDto.getMdbEntrustCode()) ){
                sql+=" AND (" + getFileDto.getMdbEntrustCode() + " = TRIM('" + getFileDto.getEntrustCode()+ "')";
                if(StringUtils.isNotBlank(getFileDto.getLotBatchNo())){
                    sql+=" OR "+ getFileDto.getMdbEntrustCode() + " = TRIM('" + getFileDto.getLotBatchNo()+ "')";
                }
                sql+=")";
            }
            if(StringUtils.isNotBlank(getFileDto.getMdbSampleCode())){
                sql+=" AND " + getFileDto.getMdbSampleCode() + " = '" + getFileDto.getSampleCode() + "'";
            }
            if(StringUtils.isNotBlank(getFileDto.getCableTag())){
                sql+=" AND Color = '" + getFileDto.getCableTag() + "'";
            }
            // åˆ›å»º PreparedStatement å¯¹è±¡æ‰§è¡Œ SQL
            preparedStatement = connection.prepareStatement(sql);
            resultSet = preparedStatement.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            // éåŽ†ç»“æžœé›†èŽ·å–æ•°æ®
            while (resultSet.next()) {
                Map<String, Object> rowData = new HashMap<>();
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = metaData.getColumnName(i);
                    rowData.put(columnName, resultSet.getObject(i));
                }
                dataList.add(rowData);
            }
            tableMap.put("data", dataList);
        } catch (Exception e) {
            e.printStackTrace();
            // å‡è®¾ R ç±»æœ‰ failed æ–¹æ³•,若没有需补充实现
            return Result.failed("数据库查询出错: " + e.getMessage());
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return tableMap;
    }
}
src/main/java/com/chinaztt/mes/docx/util/XMLFileListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,231 @@
package com.chinaztt.mes.docx.util;
import com.chinaztt.mes.docx.pojo.Chemical;
import com.chinaztt.mes.docx.pojo.TestBatch;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.SerializationUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
 * @Description æ–‡ä»¶å˜åŒ–处理
 * @Author fz
 **/
@Slf4j
public class XMLFileListener {
    private final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    /**
     * è§£æžxml文件
     *
     * @param file
     */
    public List<TestBatch> explainXml(File file) {
        // é€šè¿‡åŽç¼€ååˆ¤æ–­æ˜¯å¦æ˜¯xml文件
        if (file.exists() && file.isFile()) {
            String fileName = file.getName();
            if (".xml".equals(fileName.substring(fileName.lastIndexOf(".")))) {
                // åˆ›å»ºreader对象
                SAXReader reader = new SAXReader();
                // åŠ è½½xml文件
                Document document;
                try {
                    document = reader.read(file);
                } catch (Exception e) {
                    log.error("文档内容非xml格式,无法解析!");
                    return null;
                }
                // æ ¹èŠ‚ç‚¹ SampleResults
                Element root = document.getRootElement();
                // æ£€éªŒæ‰¹æ¬¡ä¿¡æ¯èŠ‚ç‚¹ SampleResult
                Element sampleResult = root.element("SampleResult");
                List<Attribute> attributes = sampleResult.attributes();
                String name;
                String value;
                String batchNo = null;
                String mark = null;
                String reportTime = null;
                for (Attribute attribute : attributes) {
                    name = attribute.getName();
                    value = attribute.getValue();
                    if ("Type".equals(name) && "TypeStandardization".equals(value)) {
                        log.info("校准数据,不进行采集...");
                        return null;
                    } else if ("Name".equals(name)) {
                        batchNo = value;
                    } else if ("TypeCorrSample".equals(name)) {
                        mark = value;
                    } else if ("RecalculationDateTime".equals(name)) {
                        reportTime = value;
                    }
                }
                if (batchNo == null || batchNo.trim().length() == 0) {
                    log.warn("无法解析批次号信息,此为必要信息所以终止解析!");
                    return null;
                }
                // å…ˆåˆ¤æ–­æ˜¯å¦æ˜¯æ±Ÿä¸œé‡‘具的委托订单
//                List<String> batchNos = saveTestBatchService.explainBatchNo(batchNo.trim());
//                if (batchNos == null || batchNos.size() == 0) {
//                    return null;
//                }
//                if (batchNos.get(0).equals(batchNo.trim())) {
//                    log.warn(batchNo.trim() + " éžæ£€æµ‹å•号,不进行解析...");
//                    return null;
//                }
                // å†åŽ»é™¤èžºæ “èžºæ¯çš„å§”æ‰˜è®¢å•(现不做采集)
                /*batchNos = saveTestBatchService.excludeBatchNo(batchNos);
                if (batchNos == null || batchNos.size() == 0) {
                    log.warn("该批次为螺栓/螺母的检测单,现不做采集...");
                    return null;
                }*/
                // åŒ…含具体检验信息节点 MeasurementReplicates
                Element measurement = sampleResult.element("MeasurementReplicates");
                List<Element> elements = measurement.elements();
                List<TestBatch> batches = new ArrayList<>(elements.size());
                TestBatch batch;
                Date time;
                List<Element> children;
                for (Element element : elements) {
                    batch = new TestBatch();
                    try {
                        value = element.attributeValue("MeasureDateTime").replaceFirst("T", " ");
                        time = SDF.parse(value);
                    } catch (ParseException e) {
                        e.printStackTrace();
                        log.error("报告时间格式非 yyyy-MM-ddTHH:mm:ss æˆ– yyyy-MM-dd HH:mm:ss,无法解析!");
                        return null;
                    }
                    batch.setEquipmentTime(time);
                    // å–未被删除的检验信息
                    value = element.attributeValue("IsDeleted");
                    if ("No".equals(value)) {
                        element = element.element("Measurement");
                        // çœŸæ­£åŒ…含检验结果的节点 Elements
                        element = element.element("Elements");
                        children = element.elements("Element");
                        batch.setChemicals(explainElements(children, time));
                        batches.add(batch);
                    }
                }
                if (!batches.isEmpty()) {
                    // åŒ…含检验数据平均值节点 MeasurementStatistics
                    elements = null;
                    try {
                        measurement = sampleResult.element("MeasurementStatistics").element("Measurement").element("Elements");
                        elements = measurement.elements("Element");
                    } catch (Exception e) {
                        log.error("检验数据平均值无法解析!");
                    }
                    if (elements != null && !elements.isEmpty()) {
                        batch = new TestBatch();
                        time = batches.get(batches.size() - 1).getEquipmentTime();
                        batch.setEquipmentTime(time);
                        batch.setChemicals(explainElements(elements, time));
                        batches.add(batch);
                    }
//                    List<List<TestBatch>> testBatches = new ArrayList<>(batchNos.size());
//                    if (batchNos.size() > 1) {
//                        List<TestBatch> batchList;
//                        for (String no : batchNos) {
//                            log.info("报告检验批次:" + no + "\t材料牌号:" + mark + "\t生成时间:" + reportTime);
//                            batchList = cloneBatches(batches, no);
//                            testBatches.add(batchList);
//                        }
//                    } else if (batchNos.size() == 1) {
//                        log.info("报告检验批次:" + batchNos.get(0) + "\t材料牌号:" + mark + "\t生成时间:" + reportTime);
//                        for (TestBatch testBatch : batches) {
//                            testBatch.setBatchNo(batchNos.get(0));
//                        }
//                        testBatches.add(batches);
//                    }
//                    log.info(fileName + " è§£æžå®Œæˆ...");
//                    return testBatches;
                    return batches;
                } else {
                    log.warn(fileName + " æœªè§£æžå‡ºæœ‰æ•ˆæ•°æ®ï¼");
                    return null;
                }
            }
            log.warn(fileName + " éžxml文件,无法解析!");
        }
        return null;
    }
    private List<Chemical> explainElements(List<Element> elements, Date time) {
        List<Chemical> chemicals = new ArrayList<>(elements.size());
        Chemical chemical;
        String value;
        List<Element> results;
        for (Element child : elements) {
            chemical = new Chemical();
            value = child.attributeValue("ElementName");
            chemical.setName(value);
            results = child.elements();
            if (results.size() == 3) {
                for (Element result : results) {
                    value = result.attributeValue("StatType");
                    if ("Reported".equals(value)) {
                        /*value = result.attributeValue("CalibrationStatus");
                        if (!"InRange".equals(value)) {
                            chemical.setRemark(value);
                        }*/
                        result = result.element("ResultValue");
                        value = result.getTextTrim();
                        chemical.setValue(value);
                        chemical.setEquipmentTime(time);
                    }
                }
            } else {
                for (Element result : results) {
                    value = result.attributeValue("Type");
                    if ("ConcTypeCorr".equals(value)) {
                        value = result.attributeValue("StatType");
                        if ("Reported".equals(value)) {
                            /*value = result.attributeValue("CalibrationStatus");
                            if (!"InRange".equals(value)) {
                                chemical.setRemark(value);
                            }*/
                            result = result.element("ResultValue");
                            value = result.getTextTrim();
                            chemical.setValue(value);
                            chemical.setEquipmentTime(time);
                        }
                    }
                }
            }
            if (!"-".equals(chemical.getValue())) {
                chemicals.add(chemical);
            }
        }
        return chemicals;
    }
    /**
     * å¯¹è±¡æ•°ç»„深拷贝
     *
     * @param batches
     * @param batchNo
     * @return
     */
    private List<TestBatch> cloneBatches(List<TestBatch> batches, String batchNo) {
        List<TestBatch> testBatches = new ArrayList<>(batches.size());
        TestBatch testBatch;
        for (TestBatch batch : batches) {
            testBatch = SerializationUtils.clone(batch);
            testBatch.setBatchNo(batchNo);
            testBatches.add(testBatch);
        }
        return testBatches;
    }
}
src/main/resources/application.yml
@@ -1,2 +1,6 @@
server:
  port: 9527
  port: 9527
serialPort:
  enable: true # æ˜¯å¦å¼€å¯ä¸²å£ç›‘听
  listenName: COM4 # ç›‘听的串口名称