From 90eb4a1af8fd3b1c18777211bf65c1d81dfff0b8 Mon Sep 17 00:00:00 2001 From: zc Date: Thu, 9 Apr 2026 11:05:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A7=B0=E9=87=8D=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/common/enums/CommandTypeEnum.java | 3 +- .../top/wms/admin/light/LightService.java | 16 ++++-- .../wms/admin/light/SerialPortHandler.java | 28 +++++----- .../material/model/entity/MaterialInfoDO.java | 9 +++- .../model/req/MaterialImportRowReq.java | 12 +++-- .../material/model/req/MaterialInfoReq.java | 12 +++-- .../material/model/resp/MaterialInfoResp.java | 15 ++++-- .../service/impl/MaterialInfoServiceImpl.java | 8 +-- .../weighManage/model/resp/WorkOrderResp.java | 6 +-- .../controller/light/LightController.java | 8 ++- .../controller/vm/VmCommandController.java | 50 +++++++++++++++--- .../templates/import/materialInfo.xlsx | Bin 9407 -> 9463 bytes 12 files changed, 124 insertions(+), 43 deletions(-) diff --git a/wms-common/src/main/java/top/wms/admin/common/enums/CommandTypeEnum.java b/wms-common/src/main/java/top/wms/admin/common/enums/CommandTypeEnum.java index cdcf5d8..09af9a9 100644 --- a/wms-common/src/main/java/top/wms/admin/common/enums/CommandTypeEnum.java +++ b/wms-common/src/main/java/top/wms/admin/common/enums/CommandTypeEnum.java @@ -1,6 +1,5 @@ package top.wms.admin.common.enums; - /** * DPA6024V-2T-1.0 数字控制器命令类型枚举 * 定义控制器支持的指令字 @@ -35,6 +34,7 @@ public enum CommandTypeEnum { /** * 获取指令字字符 + * * @return 指令字 (如 '1', '2', '3', '4') */ public char getCommandCode() { @@ -43,6 +43,7 @@ public enum CommandTypeEnum { /** * 根据指令字获取对应的命令类型 + * * @param commandCode 指令字字符 * @return 对应的CommandType,找不到返回null */ diff --git a/wms-module-system/src/main/java/top/wms/admin/light/LightService.java b/wms-module-system/src/main/java/top/wms/admin/light/LightService.java index 4c26f20..7e8dbae 100644 --- a/wms-module-system/src/main/java/top/wms/admin/light/LightService.java +++ b/wms-module-system/src/main/java/top/wms/admin/light/LightService.java @@ -30,7 +30,7 @@ public class LightService { serialHandler.close(); serialHandler = null; } - + // 使用COM1串口 String portName = "COM1"; // 默认波特率9600 @@ -74,6 +74,7 @@ public class LightService { /** * 检查是否已连接 + * * @return 已连接返回true,否则返回false */ public boolean isConnected() { @@ -84,6 +85,7 @@ public class LightService { /** * 检查连接状态并尝试重连 + * * @return 重连成功返回true,否则返回false */ public boolean checkAndReconnect() { @@ -98,6 +100,7 @@ public class LightService { /** * 重新连接控制器 + * * @return 重连成功返回true,否则返回false */ public boolean reconnect() { @@ -117,6 +120,7 @@ public class LightService { /** * 打开指定通道 + * * @param channel 通道号 (1-2) */ public boolean turnOn(int channel) { @@ -125,6 +129,7 @@ public class LightService { /** * 关闭指定通道 + * * @param channel 通道号 (1-2) */ public boolean turnOff(int channel) { @@ -133,7 +138,8 @@ public class LightService { /** * 设置通道亮度 - * @param channel 通道号 (1-2) + * + * @param channel 通道号 (1-2) * @param brightness 亮度等级 (0-255) */ public boolean setBrightness(int channel, int brightness) { @@ -147,6 +153,7 @@ public class LightService { /** * 读取通道亮度 + * * @param channel 通道号 (1-2) * @return 亮度等级 (0-255),失败返回-1 */ @@ -230,7 +237,7 @@ public class LightService { char cmdChar = type.getCommandCode(); // 通道字 (1-2) - char channelChar = (char) (channel + '0'); + char channelChar = (char)(channel + '0'); // 数据: 3字节,亮度的十六进制表示,高位在前 // 亮度值范围0-255,需要转换为3个十六进制ASCII字符 @@ -286,6 +293,7 @@ public class LightService { /** * 便捷方法:设置所有通道亮度 + * * @param brightness 亮度等级 (0-255) */ public void setAllBrightness(int brightness) { @@ -314,5 +322,5 @@ public class LightService { turnOn(channel); } } - + } \ No newline at end of file diff --git a/wms-module-system/src/main/java/top/wms/admin/light/SerialPortHandler.java b/wms-module-system/src/main/java/top/wms/admin/light/SerialPortHandler.java index be618b4..63de24b 100644 --- a/wms-module-system/src/main/java/top/wms/admin/light/SerialPortHandler.java +++ b/wms-module-system/src/main/java/top/wms/admin/light/SerialPortHandler.java @@ -1,6 +1,5 @@ package top.wms.admin.light; - import com.fazecast.jSerialComm.SerialPort; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -20,13 +19,13 @@ public class SerialPortHandler { private InputStream inputStream; private OutputStream outputStream; private SerialPort serialPort; - + /** * 串口名称 */ @Getter private final String portName; - + /** * 波特率 */ @@ -35,6 +34,7 @@ public class SerialPortHandler { /** * 构造函数 + * * @param portName 串口名称,如 "COM3" (Windows) 或 "/dev/ttyUSB0" (Linux) * @param baudRate 波特率 */ @@ -45,6 +45,7 @@ public class SerialPortHandler { /** * 打开串口连接 + * * @return 打开成功返回true,失败返回false */ public boolean open() { @@ -99,6 +100,7 @@ public class SerialPortHandler { /** * 检查串口是否已打开 + * * @return 已打开返回true,否则返回false */ public boolean isOpen() { @@ -107,6 +109,7 @@ public class SerialPortHandler { /** * 发送数据 + * * @param data 要发送的字节数组 */ public void sendData(byte[] data) { @@ -116,7 +119,7 @@ public class SerialPortHandler { log.error("串口 {} 未打开,无法发送数据", portName); return; } - + if (outputStream == null) { log.error("串口 {} 输出流未初始化", portName); return; @@ -131,6 +134,7 @@ public class SerialPortHandler { /** * 发送字符串数据 + * * @param data 要发送的字符串 */ public void sendData(String data) { @@ -144,6 +148,7 @@ public class SerialPortHandler { /** * 接收响应数据 + * * @param timeoutMs 超时时间(毫秒) * @return 接收到的字符串,超时或无数据返回null */ @@ -154,7 +159,7 @@ public class SerialPortHandler { log.error("串口 {} 未打开,无法接收数据", portName); return null; } - + // 验证输入流是否初始化 if (inputStream == null) { log.error("串口 {} 输入流未初始化", portName); @@ -163,8 +168,7 @@ public class SerialPortHandler { // 等待数据到达 long startTime = System.currentTimeMillis(); - while (inputStream.available() == 0 && - (System.currentTimeMillis() - startTime) < timeoutMs) { + while (inputStream.available() == 0 && (System.currentTimeMillis() - startTime) < timeoutMs) { Thread.sleep(10); } @@ -185,7 +189,8 @@ public class SerialPortHandler { /** * 接收指定长度的响应数据 - * @param length 期望接收的字节数 + * + * @param length 期望接收的字节数 * @param timeoutMs 超时时间(毫秒) * @return 接收到的字符串,超时或无数据返回null */ @@ -196,7 +201,7 @@ public class SerialPortHandler { log.error("串口 {} 未打开,无法接收数据", portName); return null; } - + // 验证输入流是否初始化 if (inputStream == null) { log.error("串口 {} 输入流未初始化", portName); @@ -207,8 +212,7 @@ public class SerialPortHandler { int bytesRead = 0; long startTime = System.currentTimeMillis(); - while (bytesRead < length && - (System.currentTimeMillis() - startTime) < timeoutMs) { + while (bytesRead < length && (System.currentTimeMillis() - startTime) < timeoutMs) { if (inputStream.available() > 0) { int read = inputStream.read(buffer, bytesRead, length - bytesRead); if (read > 0) { @@ -276,7 +280,7 @@ public class SerialPortHandler { } } catch (IOException e) { log.error("关闭串口失败: {}", e.getMessage()); - }finally { + } finally { serialPort = null; } } diff --git a/wms-module-system/src/main/java/top/wms/admin/material/model/entity/MaterialInfoDO.java b/wms-module-system/src/main/java/top/wms/admin/material/model/entity/MaterialInfoDO.java index 004d4ee..88457f2 100644 --- a/wms-module-system/src/main/java/top/wms/admin/material/model/entity/MaterialInfoDO.java +++ b/wms-module-system/src/main/java/top/wms/admin/material/model/entity/MaterialInfoDO.java @@ -38,9 +38,9 @@ public class MaterialInfoDO extends BaseDO { private BigDecimal unitWeight; /* - 物料规格 + 物料直径 */ - private String materialSpec; + private Double materialSpec; /** * 物料照片地址 @@ -61,4 +61,9 @@ public class MaterialInfoDO extends BaseDO { * 灯光等级 */ private Integer lightLevel; + + /** + * 颜色 + */ + private String color; } diff --git a/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialImportRowReq.java b/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialImportRowReq.java index 30d8ac3..393f356 100644 --- a/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialImportRowReq.java +++ b/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialImportRowReq.java @@ -46,10 +46,10 @@ public class MaterialImportRowReq implements Serializable { private String lightLevelName; /* - * 物料规格 + * 物料直径 * */ - @Schema(description = "物料规格") - private String materialSpec; + @Schema(description = "物料直径") + private Double materialSpec; /** * 物料类型名称 @@ -63,4 +63,10 @@ public class MaterialImportRowReq implements Serializable { @Schema(description = "物料流程") private String materialProcess; + /** + * 颜色 + */ + @Schema(description = "颜色") + private String color; + } diff --git a/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialInfoReq.java b/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialInfoReq.java index dd7b88a..986957c 100644 --- a/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialInfoReq.java +++ b/wms-module-system/src/main/java/top/wms/admin/material/model/req/MaterialInfoReq.java @@ -48,10 +48,10 @@ public class MaterialInfoReq implements Serializable { private Double unitWeight; /* - * 物料规格 + * 物料直径 * */ - @Schema(description = "物料规格") - private String materialSpec; + @Schema(description = "物料直径") + private Double materialSpec; /** * 物料照片地址 @@ -79,4 +79,10 @@ public class MaterialInfoReq implements Serializable { */ @Schema(description = "灯光等级") private Integer lightLevel; + + /** + * 颜色 + */ + @Schema(description = "颜色") + private String color; } diff --git a/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoResp.java b/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoResp.java index 31bd33b..059145a 100644 --- a/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoResp.java +++ b/wms-module-system/src/main/java/top/wms/admin/material/model/resp/MaterialInfoResp.java @@ -45,11 +45,11 @@ public class MaterialInfoResp extends BaseDetailResp { private Double unitWeight; /** - * 物料规格 + * 物料直径 */ - @Schema(description = "物料规格") - @ExcelProperty(value = "物料规格", order = 5) - private String materialSpec; + @Schema(description = "物料直径") + @ExcelProperty(value = "物料直径", order = 5) + private Double materialSpec; /** * 物料照片地址 @@ -100,4 +100,11 @@ public class MaterialInfoResp extends BaseDetailResp { @ExcelProperty(value = "灯光等级", converter = LightLevelEnumConverter.class, order = 4) private Integer lightLevel; + /** + * 颜色 + */ + @Schema(description = "颜色") + @ExcelProperty(value = "颜色", order = 6) + private String color; + } diff --git a/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java b/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java index 65e5279..ece9466 100644 --- a/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java +++ b/wms-module-system/src/main/java/top/wms/admin/material/service/impl/MaterialInfoServiceImpl.java @@ -51,7 +51,6 @@ import top.wms.admin.material.model.resp.MaterialImportParseResp; import top.wms.admin.material.model.resp.MaterialInfoImportResp; import top.wms.admin.material.model.resp.MaterialInfoResp; import top.wms.admin.material.service.MaterialInfoService; -import top.wms.admin.materialProcess.model.entity.MaterialProcessDO; import top.wms.admin.materialType.mapper.MaterialTypeMapper; import top.wms.admin.materialType.model.entity.MaterialTypeDO; import top.wms.admin.system.service.FileService; @@ -238,8 +237,11 @@ public class MaterialInfoServiceImpl extends BaseServiceImpl().eq("encoding", materialCode)); + CheckUtils.throwIf(ObjectUtil.isEmpty(materialInfoDO), "物料数据异常"); + CheckUtils.throwIf(StrUtil.isBlank(materialInfoDO.getMaterialProcess()), "物料流程编码不能为空"); // 1. 检查连接 Channel channel = channelManager.getFirstChannel(); @@ -38,16 +50,42 @@ public class VmCommandController { } // 2. 清空之前的响应队列,避免影响当前请求 requestMatcher.clear(); - channel.writeAndFlush(materialProcess); + channel.writeAndFlush(materialInfoDO.getMaterialProcess()); // 3. 等待响应 String response = requestMatcher.waitForResponse(20); log.info("sendAndWait-收到响应: {}", response); CheckUtils.throwIf("TIMEOUT".equals(response), "响应超时,请重试"); CheckUtils.throwIf("ERROR".equals(response), "设备连接异常!"); + //如果是圆管,数据格式为yg_yellow_139.092或者yg_yellow + if (response.contains("yg_")) { + CheckUtils.throwIf(null == materialInfoDO.getMaterialSpec(), "物料直径不能为空"); + + String[] split = response.split("_"); + if (split.length != 3) { + throw new BusinessException("位置摆放不标准,未检查出直径"); + } + if (!StrUtil.equals(split[1], materialInfoDO.getColor())) { + throw new BusinessException("颜色不匹配"); + } + + try { + double measuredDiameter = Double.parseDouble(split[2]); + double difference = Math.abs(measuredDiameter - materialInfoDO.getMaterialSpec()); + if (difference > 5) { + throw new BusinessException("直径不匹配"); + } + } catch (NumberFormatException e) { + throw new BusinessException("相机识别异常"); + } + + } else if (StrUtil.equals(response, materialCode)) { + response = "success"; + } else { + response = "failed"; + } return response; } - @PostMapping("/start") public boolean startTask() { return saveBmpTaskService.startTask(); @@ -57,4 +95,4 @@ public class VmCommandController { public boolean stopTask() { return saveBmpTaskService.stopTask(); } -} +} \ No newline at end of file diff --git a/wms-webapi/src/main/resources/templates/import/materialInfo.xlsx b/wms-webapi/src/main/resources/templates/import/materialInfo.xlsx index c9f532d354c718dec35148fb9d85df618599d5f8..785e6c7a18c038f5c548e909ac634c1a1c929021 100644 GIT binary patch delta 3539 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 NmLwwvVIlwk002bdrr7`h delta 3507 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 Pk8oe$0(2t$e|`T0D8!st