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
new file mode 100644
index 0000000..cdcf5d8
--- /dev/null
+++ b/wms-common/src/main/java/top/wms/admin/common/enums/CommandTypeEnum.java
@@ -0,0 +1,62 @@
+package top.wms.admin.common.enums;
+
+
+/**
+ * DPA6024V-2T-1.0 数字控制器命令类型枚举
+ * 定义控制器支持的指令字
+ */
+public enum CommandTypeEnum {
+
+ /**
+ * 打开对应通道 (指令字: 1)
+ */
+ ON('1'),
+
+ /**
+ * 关闭对应通道 (指令字: 2)
+ */
+ OFF('2'),
+
+ /**
+ * 设置对应通道亮度参数 (指令字: 3)
+ */
+ SET_BRIGHTNESS('3'),
+
+ /**
+ * 读出对应通道亮度参数 (指令字: 4)
+ */
+ READ('4');
+
+ private final char commandCode;
+
+ CommandTypeEnum(char commandCode) {
+ this.commandCode = commandCode;
+ }
+
+ /**
+ * 获取指令字字符
+ * @return 指令字 (如 '1', '2', '3', '4')
+ */
+ public char getCommandCode() {
+ return commandCode;
+ }
+
+ /**
+ * 根据指令字获取对应的命令类型
+ * @param commandCode 指令字字符
+ * @return 对应的CommandType,找不到返回null
+ */
+ public static CommandTypeEnum fromCommandCode(char commandCode) {
+ for (CommandTypeEnum type : values()) {
+ if (type.commandCode == commandCode) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(commandCode);
+ }
+}
\ No newline at end of file
diff --git a/wms-module-system/pom.xml b/wms-module-system/pom.xml
index 0f5c9a6..2e58377 100644
--- a/wms-module-system/pom.xml
+++ b/wms-module-system/pom.xml
@@ -25,5 +25,12 @@
1.3.2
provided
+
+
+
+ com.fazecast
+ jSerialComm
+ 2.10.5
+
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
new file mode 100644
index 0000000..184ea18
--- /dev/null
+++ b/wms-module-system/src/main/java/top/wms/admin/light/LightService.java
@@ -0,0 +1,268 @@
+package top.wms.admin.light;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import top.wms.admin.common.enums.CommandTypeEnum;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * DPA6024V-2T-1.0 数字控制器 Java 控制类
+ * 支持通过RS232串口控制LED光源的亮度(0-255级)
+ */
+@Slf4j
+@Component
+public class LightService {
+
+ private SerialPortHandler serialHandler;
+
+ /**
+ * 连接控制器
+ * 前端页面进入"称重管理"页面时调用
+ */
+ public boolean connect() {
+ try {
+ // 使用COM1串口
+ String portName = "COM1";
+ // 默认波特率9600
+ int baudRate = 9600;
+ serialHandler = new SerialPortHandler(portName, baudRate);
+ boolean connected = serialHandler.open();
+ if (connected) {
+ log.info("数字控制器连接成功");
+ } else {
+ log.error("数字控制器连接失败");
+ }
+ return connected;
+ } catch (Exception e) {
+ log.error("连接失败: {}", e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * 断开连接
+ * 前端页面离开"称重管理"页面时调用
+ */
+ public void disconnect() {
+ if (serialHandler != null) {
+ log.info("正在断开数字控制器连接");
+ serialHandler.close();
+ serialHandler = null;
+ log.info("数字控制器连接已断开");
+ }
+ }
+
+ /**
+ * 检查是否已连接
+ * @return 已连接返回true,否则返回false
+ */
+ public boolean isConnected() {
+ return serialHandler != null && serialHandler.isOpen();
+ }
+
+ /**
+ * 打开指定通道
+ * @param channel 通道号 (1-2)
+ */
+ public boolean turnOn(int channel) {
+ return sendCommand(CommandTypeEnum.ON, channel, 0);
+ }
+
+ /**
+ * 关闭指定通道
+ * @param channel 通道号 (1-2)
+ */
+ public boolean turnOff(int channel) {
+ return sendCommand(CommandTypeEnum.OFF, channel, 0);
+ }
+
+ /**
+ * 设置通道亮度
+ * @param channel 通道号 (1-2)
+ * @param brightness 亮度等级 (0-255)
+ */
+ public boolean setBrightness(int channel, int brightness) {
+ if (brightness < 0 || brightness > 255) {
+ log.error("亮度等级必须在0-255之间");
+ return false;
+ }
+ log.info("设置通道 {} 亮度为: {}", channel, brightness);
+ return sendCommand(CommandTypeEnum.SET_BRIGHTNESS, channel, brightness);
+ }
+
+ /**
+ * 读取通道亮度
+ * @param channel 通道号 (1-2)
+ * @return 亮度等级 (0-255),失败返回-1
+ */
+ public int getBrightness(int channel) {
+ int brightness = sendReadCommand(channel);
+ if (brightness != -1) {
+ log.info("通道 {} 当前亮度: {}", channel, brightness);
+ }
+ return brightness;
+ }
+
+ /**
+ * 发送命令到控制器
+ */
+ private boolean sendCommand(CommandTypeEnum type, int channel, int brightness) {
+ if (serialHandler == null || !serialHandler.isOpen()) {
+ log.error("控制器未连接");
+ return false;
+ }
+
+ byte[] command = buildCommand(type, channel, brightness);
+
+ // 发送命令
+ serialHandler.sendData(command);
+
+ // 等待响应(对于ON/OFF/SET命令,返回'$'表示成功)
+ if (type != CommandTypeEnum.READ) {
+ String response = serialHandler.receiveResponse(100);
+ boolean success = response != null && response.contains("$");
+ if (success) {
+ log.debug("命令执行成功");
+ } else {
+ log.error("命令执行失败,响应: {}", response);
+ }
+ return success;
+ }
+
+ return true;
+ }
+
+ /**
+ * 发送读取命令并获取返回值
+ */
+ private int sendReadCommand(int channel) {
+ if (serialHandler == null || !serialHandler.isOpen()) {
+ log.error("控制器未连接");
+ return -1;
+ }
+
+ byte[] command = buildCommand(CommandTypeEnum.READ, channel, 0);
+ if (command == null) {
+ return -1;
+ }
+
+ // 发送命令
+ serialHandler.sendData(command);
+
+ // 接收响应(返回格式同发送格式,包含亮度数据)
+ String response = serialHandler.receiveResponse(200);
+ if (response == null || !response.startsWith("$")) {
+ log.error("读取亮度失败,响应: {}", response);
+ return -1;
+ }
+
+ // 解析返回的亮度值
+ int brightness = parseBrightnessFromResponse(response);
+ if (brightness == -1) {
+ log.error("解析亮度值失败,响应: {}", response);
+ }
+ return brightness;
+ }
+
+ /**
+ * 构建命令字节数组
+ * 格式: 特征字(1) + 指令字(1) + 通道字(1) + 数据(3) + 异或校验字(2)
+ * 所有字节采用ASCII码
+ * 数据部分:3个ASCII字符,表示亮度的十六进制值(高位在前)
+ */
+ private byte[] buildCommand(CommandTypeEnum type, int channel, int brightness) {
+ // 特征字: $ (ASCII 36)
+ char featureChar = '$';
+
+ // 指令字
+ char cmdChar = type.getCommandCode();
+
+ // 通道字 (1-2)
+ char channelChar = (char) (channel + '0');
+
+ // 数据: 3字节,亮度的十六进制表示,高位在前
+ // 亮度值范围0-255,需要转换为3个十六进制ASCII字符
+ String dataStr;
+ if (type == CommandTypeEnum.SET_BRIGHTNESS) {
+ // 将亮度值转换为十六进制字符串,并确保是3位
+ dataStr = String.format("%03X", brightness);
+ } else {
+ // ON/OFF/READ命令的数据字段固定为 "000"
+ dataStr = "000";
+ }
+
+ // 构建命令字符串用于计算校验和
+ String cmdStr = featureChar + String.valueOf(cmdChar) + channelChar + dataStr;
+ byte[] cmdBytes = cmdStr.getBytes(StandardCharsets.US_ASCII);
+
+ // 计算异或校验和
+ byte xorSum = 0;
+ for (int i = 0; i < cmdBytes.length; i++) {
+ xorSum ^= cmdBytes[i];
+ }
+
+ // 将校验和转换为2位十六进制ASCII码
+ String xorStr = String.format("%02X", xorSum & 0xFF).toUpperCase();
+
+ // 最终命令字符串
+ String fullCommand = cmdStr + xorStr;
+
+ log.debug("构建命令: {} (亮度: {} -> 十六进制: {})", fullCommand, brightness, dataStr);
+ return fullCommand.getBytes(StandardCharsets.US_ASCII);
+
+ }
+
+ /**
+ * 从响应字符串解析亮度值
+ * 响应格式: $ + 指令字 + 通道字 + 数据(3) + 校验字(2)
+ * 例如: "$43038E" 表示通道3亮度56
+ */
+ private int parseBrightnessFromResponse(String response) {
+ if (response == null || response.length() < 8) {
+ return -1;
+ }
+
+ try {
+ // 提取数据部分(第4-6个字符,索引3-5)
+ String dataStr = response.substring(3, 6);
+ return Integer.parseInt(dataStr);
+ } catch (NumberFormatException e) {
+ log.error("解析亮度值失败: {}", e.getMessage());
+ return -1;
+ }
+ }
+
+ /**
+ * 便捷方法:设置所有通道亮度
+ * @param brightness 亮度等级 (0-255)
+ */
+ public void setAllBrightness(int brightness) {
+ log.info("设置所有通道亮度为: {}", brightness);
+ for (int channel = 1; channel <= 2; channel++) {
+ setBrightness(channel, brightness);
+ }
+ }
+
+ /**
+ * 便捷方法:关闭所有通道
+ */
+ public void turnAllOff() {
+ log.info("关闭所有通道");
+ for (int channel = 1; channel <= 2; channel++) {
+ turnOff(channel);
+ }
+ }
+
+ /**
+ * 便捷方法:打开所有通道
+ */
+ public void turnAllOn() {
+ log.info("打开所有通道");
+ for (int channel = 1; channel <= 2; channel++) {
+ 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
new file mode 100644
index 0000000..aa0a29e
--- /dev/null
+++ b/wms-module-system/src/main/java/top/wms/admin/light/SerialPortHandler.java
@@ -0,0 +1,241 @@
+package top.wms.admin.light;
+
+
+import com.fazecast.jSerialComm.SerialPort;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 串口通信处理器
+ * 负责与DPA6024V-2T-1.0数字控制器进行串口通信
+ */
+@Slf4j
+public class SerialPortHandler {
+
+ private InputStream inputStream;
+ private OutputStream outputStream;
+ private SerialPort serialPort;
+
+ /**
+ * 串口名称
+ */
+ @Getter
+ private final String portName;
+
+ /**
+ * 波特率
+ */
+ @Getter
+ private final int baudRate;
+
+ /**
+ * 构造函数
+ * @param portName 串口名称,如 "COM3" (Windows) 或 "/dev/ttyUSB0" (Linux)
+ * @param baudRate 波特率
+ */
+ public SerialPortHandler(String portName, int baudRate) {
+ this.portName = portName;
+ this.baudRate = baudRate;
+ }
+
+ /**
+ * 打开串口连接
+ * @return 打开成功返回true,失败返回false
+ */
+ public boolean open() {
+ try {
+ // 获取所有可用的串口
+ SerialPort[] ports = SerialPort.getCommPorts();
+
+ // 查找指定的串口
+ for (SerialPort port : ports) {
+ if (port.getSystemPortName().equals(portName)) {
+ serialPort = port;
+ break;
+ }
+ }
+
+ if (serialPort == null) {
+ log.error("未找到串口: {}", portName);
+ return false;
+ }
+
+ // 配置串口参数
+ serialPort.setBaudRate(baudRate);
+ serialPort.setNumDataBits(8);
+ serialPort.setNumStopBits(1);
+ serialPort.setParity(SerialPort.NO_PARITY);
+ serialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
+
+ // 打开串口
+ boolean opened = serialPort.openPort();
+ if (opened) {
+ // 设置读取超时时间(毫秒)
+ serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 1000, 0);
+ inputStream = serialPort.getInputStream();
+ outputStream = serialPort.getOutputStream();
+ log.info("串口 {} 打开成功,波特率: {}", portName, baudRate);
+ }
+ return opened;
+
+ } catch (Exception e) {
+ log.error("打开串口失败: {}", e.getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * 检查串口是否已打开
+ * @return 已打开返回true,否则返回false
+ */
+ public boolean isOpen() {
+ return serialPort != null && serialPort.isOpen();
+ }
+
+ /**
+ * 发送数据
+ * @param data 要发送的字节数组
+ */
+ public void sendData(byte[] data) {
+ try {
+ if (outputStream == null) {
+ log.error("输出流未初始化");
+ return;
+ }
+ outputStream.write(data);
+ outputStream.flush();
+ log.debug("发送: {}", new String(data));
+ } catch (IOException e) {
+ log.error("发送数据失败: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 发送字符串数据
+ * @param data 要发送的字符串
+ */
+ public void sendData(String data) {
+ try {
+ sendData(data.getBytes(StandardCharsets.US_ASCII));
+ } catch (Exception e) {
+ log.error("发送字符串数据失败: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 接收响应数据
+ * @param timeoutMs 超时时间(毫秒)
+ * @return 接收到的字符串,超时或无数据返回null
+ */
+ public String receiveResponse(int timeoutMs) {
+ try {
+ // 等待数据到达
+ long startTime = System.currentTimeMillis();
+ while (inputStream.available() == 0 &&
+ (System.currentTimeMillis() - startTime) < timeoutMs) {
+ Thread.sleep(10);
+ }
+
+ // 读取数据
+ byte[] buffer = new byte[1024];
+ int available = inputStream.available();
+ if (available > 0) {
+ int len = inputStream.read(buffer, 0, Math.min(available, buffer.length));
+ String response = new String(buffer, 0, len);
+ log.debug("接收: {}", response);
+ return response;
+ }
+ } catch (Exception e) {
+ log.error("接收响应失败: ", e);
+ }
+ return null;
+ }
+
+ /**
+ * 接收指定长度的响应数据
+ * @param length 期望接收的字节数
+ * @param timeoutMs 超时时间(毫秒)
+ * @return 接收到的字符串,超时或无数据返回null
+ */
+ public String receiveResponse(int length, int timeoutMs) {
+ try {
+ byte[] buffer = new byte[length];
+ int bytesRead = 0;
+ long startTime = System.currentTimeMillis();
+
+ while (bytesRead < length &&
+ (System.currentTimeMillis() - startTime) < timeoutMs) {
+ if (inputStream.available() > 0) {
+ int read = inputStream.read(buffer, bytesRead, length - bytesRead);
+ if (read > 0) {
+ bytesRead += read;
+ }
+ }
+ Thread.sleep(10);
+ }
+
+ if (bytesRead > 0) {
+ String response = new String(buffer, 0, bytesRead);
+ log.debug("接收: {}", response);
+ return response;
+ }
+ } catch (Exception e) {
+ log.error("接收响应失败: {}", e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * 清空输入缓冲区
+ */
+ public void clearInputBuffer() {
+ try {
+ if (inputStream != null && inputStream.available() > 0) {
+ byte[] buffer = new byte[inputStream.available()];
+ inputStream.read(buffer);
+ log.debug("清空输入缓冲区: {}", new String(buffer));
+ }
+ } catch (IOException e) {
+ log.error("清空输入缓冲区失败: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 清空输出缓冲区
+ */
+ public void clearOutputBuffer() {
+ try {
+ if (outputStream != null) {
+ outputStream.flush();
+ }
+ } catch (IOException e) {
+ log.error("清空输出缓冲区失败: {}", e.getMessage());
+ }
+ }
+
+ /**
+ * 关闭串口连接
+ */
+ public void close() {
+ try {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ if (serialPort != null && serialPort.isOpen()) {
+ serialPort.closePort();
+ log.info("串口 {} 已关闭", portName);
+ }
+ } catch (IOException e) {
+ log.error("关闭串口失败: {}", e.getMessage());
+ }
+ }
+
+}
\ No newline at end of file
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 0c03c84..677eb3a 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
@@ -4,7 +4,6 @@ import lombok.Data;
import com.baomidou.mybatisplus.annotation.TableName;
-import top.wms.admin.common.enums.LightLevelEnum;
import top.wms.admin.common.model.entity.BaseDO;
import java.io.Serial;
diff --git a/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java b/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java
index c037741..ad1d075 100644
--- a/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java
+++ b/wms-module-system/src/main/java/top/wms/admin/material/service/MaterialInfoService.java
@@ -28,6 +28,14 @@ public interface MaterialInfoService extends BaseService {
if (row.getLightLevelName() != null) {
Integer valueByDescription = LightLevelEnum.getValueByDescription(row.getLightLevelName());
- if(null != valueByDescription){
+ if (null != valueByDescription) {
lightLevelMap.put(row.getLightLevelName(), valueByDescription);
}
}
diff --git a/wms-webapi/pom.xml b/wms-webapi/pom.xml
index e18fe7e..08c71ba 100644
--- a/wms-webapi/pom.xml
+++ b/wms-webapi/pom.xml
@@ -94,12 +94,6 @@
1.12.780
-
- com.fazecast
- jSerialComm
- 2.9.2
-
-
jakarta.websocket
diff --git a/wms-webapi/src/main/java/top/wms/admin/controller/light/LightController.java b/wms-webapi/src/main/java/top/wms/admin/controller/light/LightController.java
new file mode 100644
index 0000000..7bbb01a
--- /dev/null
+++ b/wms-webapi/src/main/java/top/wms/admin/controller/light/LightController.java
@@ -0,0 +1,150 @@
+package top.wms.admin.controller.light;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import top.continew.starter.log.annotation.Log;
+import top.continew.starter.web.model.R;
+import top.wms.admin.light.LightService;
+import top.wms.admin.material.service.MaterialInfoService;
+
+/**
+ * 灯光控制器 API
+ * 提供前端页面调用的接口
+ */
+@Slf4j
+@RestController
+@RequestMapping("/api/light")
+@Log(ignore = true)
+@RequiredArgsConstructor
+@Tag(name = "灯光控制器", description = "灯光控制器相关接口")
+public class LightController {
+
+ private final LightService lightService;
+
+ private final MaterialInfoService materialInfoService;
+
+ /**
+ * 连接控制器
+ * 前端页面进入"称重管理"页面时调用
+ */
+ @PostMapping("/connect")
+ @Operation(summary = "连接控制器", description = "前端页面进入'称重管理'页面时调用")
+ public R connect() {
+ boolean connected = lightService.connect();
+ if (connected) {
+ return R.ok();
+ } else {
+ return R.fail("500","灯光连接失败");
+ }
+ }
+
+ /**
+ * 断开连接
+ * 前端页面离开"称重管理"页面时调用
+ */
+ @PostMapping("/disconnect")
+ @Operation(summary = "断开连接", description = "前端页面离开'称重管理'页面时调用")
+ public R disconnect() {
+ lightService.disconnect();
+ return R.ok();
+ }
+
+ /**
+ * 检查连接状态
+ */
+ @GetMapping("/status")
+ @Operation(summary = "检查连接状态", description = "检查控制器是否已连接")
+ public R status() {
+ boolean connected = lightService.isConnected();
+ return R.ok(connected);
+ }
+
+ /**
+ * 设置通道亮度
+ *
+ */
+ @PostMapping("/brightness")
+ @Operation(summary = "设置通道亮度", description = "设置指定通道的亮度")
+ public R setBrightness(@RequestBody JSONObject js) {
+ Long materialId = js.getLong("materialId");
+ Integer brightness = materialInfoService.getBrightness(materialId);
+ if (brightness == null) {
+ return R.ok();
+ }
+ boolean success = lightService.setBrightness(1, brightness);
+ if (success) {
+ return R.ok();
+ } else {
+ return R.fail("500","设置灯光亮度失败,请检查连接状态");
+ }
+ }
+
+ /**
+ * 读取通道亮度
+ * @param channel 通道号 (1-2)
+ */
+ @GetMapping("/brightness")
+ @Operation(summary = "读取通道亮度", description = "读取指定通道的当前亮度")
+ public R getBrightness(@RequestParam int channel) {
+ int brightness = lightService.getBrightness(channel);
+ return R.ok(brightness);
+ }
+
+ /**
+ * 打开指定通道
+ * @param channel 通道号 (1-2)
+ */
+ @PostMapping("/turn-on")
+ @Operation(summary = "打开通道", description = "打开指定通道")
+ public R turnOn(@RequestParam int channel) {
+ boolean success = lightService.turnOn(channel);
+ return R.ok(success);
+ }
+
+ /**
+ * 关闭指定通道
+ * @param channel 通道号 (1-2)
+ */
+ @PostMapping("/turn-off")
+ @Operation(summary = "关闭通道", description = "关闭指定通道")
+ public R turnOff(@RequestParam int channel) {
+ boolean success = lightService.turnOff(channel);
+ return R.ok(success);
+ }
+
+ /**
+ * 设置所有通道亮度
+ * @param brightness 亮度等级 (0-255)
+ */
+ @PostMapping("/all-brightness")
+ @Operation(summary = "设置所有通道亮度", description = "设置所有通道的亮度")
+ public R setAllBrightness(@RequestParam int brightness) {
+ lightService.setAllBrightness(brightness);
+ return R.ok();
+ }
+
+ /**
+ * 打开所有通道
+ */
+ @PostMapping("/all-turn-on")
+ @Operation(summary = "打开所有通道", description = "打开所有通道")
+ public R turnAllOn() {
+ lightService.turnAllOn();
+ return R.ok();
+ }
+
+ /**
+ * 关闭所有通道
+ */
+ @PostMapping("/all-turn-off")
+ @Operation(summary = "关闭所有通道", description = "关闭所有通道")
+ public R turnAllOff() {
+ lightService.turnAllOff();
+ return R.ok();
+ }
+}
\ No newline at end of file