diff --git a/yanzhu-modules/yanzhu-led/LED系统技术文档.md b/yanzhu-modules/yanzhu-led/LED系统技术文档.md
new file mode 100644
index 00000000..410731fc
--- /dev/null
+++ b/yanzhu-modules/yanzhu-led/LED系统技术文档.md
@@ -0,0 +1,489 @@
+# LED 屏管理系统技术文档
+
+## 一、项目概述
+
+本项目是基于 Onbon Bx06 系列 LED 控制器开发的 LED 屏管理系统,实现了 LED 屏的自动连接、内容绘制、定时刷新和状态监控等功能。
+
+## 二、核心依赖
+
+### Maven 依赖配置
+项目使用 Onbon Bx06 系列 SDK,主要依赖包括:
+
+```xml
+
+
+ bx06
+ bx06
+ 0.6.5-SNAPSHOT
+
+
+
+
+ bx06.message
+ bx06.message
+ 0.6.5-SNAPSHOT
+
+
+
+
+ uia-comm
+ uia-comm
+ 0.5.3-SNAPSHOT
+
+
+
+```
+
+## 三、核心架构
+
+### 3.1 主要服务类
+
+| 类名 | 职责 | 文件路径 |
+|------|------|----------|
+| LedServerService | LED 服务器管理,启动/停止/监控 | service/LedServerService.java |
+| LedDrawService | LED 内容绘制和屏幕连接管理 | service/LedDrawService.java |
+| LedServerListener | LED 屏连接/断开事件监听 | listener/LedServerListener.java |
+| LedScreenServiceImpl | LED 屏配置管理和数据服务 | service/impl/LedScreenServiceImpl.java |
+| LedController | REST API 接口 | controller/LedController.java |
+| LedConfig | 配置类,启用定时任务 | config/LedConfig.java |
+| LedProperties | 配置属性管理 | config/LedProperties.java |
+
+### 3.2 工具类
+
+| 类名 | 职责 |
+|------|------|
+| UniLedDrawer | LED 屏内容绘制工具(类型1) |
+| Uni2LedDrawer | LED 屏内容绘制工具(类型2) |
+| LedFileUtils | LED 文件操作工具 |
+| LedContentUpdater | LED 内容更新工具 |
+
+## 四、Onbon 接口使用详解
+
+### 4.1 环境初始化接口
+
+#### Bx6GEnv.initial()
+```java
+// 初始化 Bx6G 环境
+Bx6GEnv.initial(ledProperties.getEnvTimeout());
+```
+- **功能**:初始化 Onbon Bx06 SDK 环境
+- **参数**:超时时间(毫秒)
+- **使用位置**:LedServerService.initializeServer()
+
+### 4.2 服务器管理接口
+
+#### Bx6GServer
+```java
+// 创建服务器实例
+Bx6GServer server = new Bx6GServer("LED_Server", PORT, new Bx6E());
+
+// 启动服务器
+boolean started = server.start();
+
+// 停止服务器
+server.stop();
+
+// 添加监听器
+server.addListener(ledServerListener);
+```
+- **功能**:创建和管理 LED 服务器
+- **参数**:
+ - 服务器名称
+ - 监听端口(默认 3801)
+ - 控制器类型(Bx6E/Bx6M/Bx6Q)
+- **使用位置**:LedServerService
+
+#### 控制器系列类
+```java
+import onbon.bx06.series.Bx6E;
+import onbon.bx06.series.Bx6M;
+import onbon.bx06.series.Bx6Q;
+```
+- **功能**:定义支持的控制器类型
+- **使用位置**:LedServerService.getServerByType()
+
+### 4.3 屏幕操作接口
+
+#### Bx6GScreen
+```java
+// Ping 测试
+Bx6GScreen.Result> result = screen.ping();
+
+// 检查控制器状态
+screen.checkControllerStatus();
+
+// 检查固件版本
+screen.checkFirmware();
+
+// 读取控制器 ID
+screen.readControllerId();
+
+// 写入节目
+screen.writeProgram(programFile);
+
+// 获取屏幕配置
+Bx6GScreenProfile profile = screen.getProfile();
+```
+- **功能**:LED 屏幕操作接口
+- **使用位置**:LedDrawService, LedServerListener
+
+### 4.4 节目文件接口
+
+#### ProgramBxFile
+```java
+// 创建节目文件
+ProgramBxFile programFile = new ProgramBxFile("P000", profile);
+
+// 添加区域
+programFile.addArea(area);
+```
+- **功能**:创建和管理节目文件
+- **参数**:
+ - 节目名称
+ - 屏幕配置文件(Bx6GScreenProfile)
+- **使用位置**:LedDrawService.createProgramByControllerType()
+
+### 4.5 显示区域接口
+
+#### TextCaptionBxArea(文本区域)
+```java
+// 创建文本区域
+TextCaptionBxArea area = new TextCaptionBxArea(x, y, width, height, profile);
+
+// 添加页面
+area.addPage(textPage);
+area.addPage(imagePage);
+```
+- **功能**:创建文本和图片显示区域
+- **参数**:x坐标, y坐标, 宽度, 高度, 屏幕配置
+- **使用位置**:LedDrawService.createBx6MProgram()
+
+#### DateTimeBxArea(日期时间区域)
+```java
+// 创建日期时间区域
+DateTimeBxArea timeBxArea = new DateTimeBxArea(0, height-16, width, 16, profile);
+
+// 设置样式
+timeBxArea.setMultiline(false);
+timeBxArea.setDateStyle(DateStyle.YYYY_MM_DD_3);
+timeBxArea.setTimeStyle(TimeStyle.HH_MM_SS_1);
+timeBxArea.setWeekStyle(WeekStyle.CHINESE);
+```
+- **功能**:创建日期时间显示区域
+- **样式选项**:
+ - DateStyle: YYYY_MM_DD_3 等多种日期格式
+ - TimeStyle: HH_MM_SS_1 等多种时间格式
+ - WeekStyle: CHINESE(中文)等星期格式
+- **使用位置**:LedDrawService.createBx6MProgram()
+
+### 4.6 页面接口
+
+#### TextBxPage(文本页)
+```java
+// 创建文本页
+TextBxPage textPage = new TextBxPage(text, new Font("宋体", Font.PLAIN, 12));
+
+// 设置颜色
+textPage.setForeground(Color.WHITE);
+textPage.setBackground(Color.BLACK);
+```
+- **功能**:创建文本显示页面
+- **参数**:文本内容, 字体
+- **使用位置**:LedDrawService.createBx6MProgram()
+
+#### ImageFileBxPage(图片页)
+```java
+// 创建图片页
+ImageFileBxPage imagePage = new ImageFileBxPage(imagePath);
+```
+- **功能**:创建图片显示页面
+- **参数**:图片文件路径
+- **使用位置**:LedDrawService.createBx6MProgram()
+
+### 4.7 服务器监听接口
+
+#### Bx6GServerListener
+```java
+public class LedServerListener implements Bx6GServerListener {
+ @Override
+ public void connected(String socketId, String netId, Bx6GScreen screen) {
+ // LED 屏连接成功处理
+ }
+
+ @Override
+ public void disconnected(String socketId, String netId, Bx6GScreen screen) {
+ // LED 屏断开连接处理
+ }
+}
+```
+- **功能**:监听 LED 屏的连接和断开事件
+- **回调方法**:
+ - connected(): 屏幕连接时触发
+ - disconnected(): 屏幕断开时触发
+- **使用位置**:LedServerListener
+
+### 4.8 屏幕配置接口
+
+#### Bx6GScreenProfile
+```java
+// 从屏幕获取配置
+Bx6GScreenProfile profile = screen.getProfile();
+
+// 使用配置创建区域
+TextCaptionBxArea area = new TextCaptionBxArea(x, y, width, height, profile);
+```
+- **功能**:LED 屏幕配置文件
+- **使用位置**:LedDrawService.createProgramByControllerType()
+
+## 五、系统流程
+
+### 5.1 服务器启动流程
+
+```
+1. 应用启动
+ ↓
+2. LedServerService.initializeServer() (@PostConstruct)
+ ↓
+3. 初始化 Bx6G 环境
+ Bx6GEnv.initial(envTimeout)
+ ↓
+4. 创建 LED 服务器
+ new Bx6GServer("LED_Server", PORT, new Bx6E())
+ ↓
+5. 添加监听器
+ server.addListener(ledServerListener)
+ ↓
+6. 启动服务器
+ server.start()
+ ↓
+7. 服务器监听端口 3801,等待 LED 屏连接
+```
+
+### 5.2 LED 屏连接流程
+
+```
+1. LED 屏主动连接到服务器(端口 3801)
+ ↓
+2. Bx6GServerListener.connected() 触发
+ ↓
+3. 清理旧连接(如果存在)
+ ledDrawService.removeConnectedScreen(netId)
+ ↓
+4. 更新数据库状态为在线
+ ledScreen.setOnline(true)
+ ledScreenService.updateLedScreen(ledScreen)
+ ↓
+5. 添加到连接管理器
+ ledDrawService.addConnectedScreen(netId, screen)
+ ↓
+6. 执行设备状态检查
+ - screen.ping()
+ - screen.checkControllerStatus()
+ - screen.checkFirmware()
+ - screen.readControllerId()
+```
+
+### 5.3 LED 内容绘制流程
+
+```
+1. 触发绘制请求
+ ↓
+2. drawLedScreenContent(ledScreen)
+ ↓
+3. 从连接管理器获取屏幕对象
+ Bx6GScreen screen = connectedScreens.get(deviceSn)
+ ↓
+4. 获取屏幕配置
+ Bx6GScreenProfile profile = screen.getProfile()
+ ↓
+5. 创建节目文件
+ ProgramBxFile programFile = new ProgramBxFile("P000", profile)
+ ↓
+6. 创建显示区域
+ a) 头部文本区域 (0, 0, width, 16)
+ - 创建 TextCaptionBxArea
+ - 添加 TextBxPage(标题)
+
+ b) 中间图片区域 (0, 17, width, height-32)
+ - 创建 TextCaptionBxArea
+ - 添加多个 ImageFileBxPage(图片列表)
+
+ c) 底部时间区域 (0, height-16, width, 16)
+ - 创建 DateTimeBxArea
+ - 设置日期/时间/星期样式
+ ↓
+7. 将区域添加到节目文件
+ programFile.addArea(area)
+ ↓
+8. 发送节目到 LED 屏
+ screen.writeProgram(programFile)
+```
+
+### 5.4 定时检测流程
+
+#### 服务器状态检测(每30秒)
+```
+@Scheduled(fixedRate = 30000)
+checkServerStatus()
+ ↓
+检查服务器是否运行
+ ↓
+如果未运行,尝试重启
+ - server.start()
+ - 失败则重新创建服务器实例
+```
+
+#### 设备在线状态检测(每30秒)
+```
+@Scheduled(fixedRate = 30000)
+checkConnectedDevicesStatus()
+ ↓
+遍历所有已连接设备
+ ↓
+对每个设备执行 ping 测试
+ screen.ping()
+ ↓
+如果 ping 失败
+ - 从连接管理器移除
+ - 更新数据库状态为离线
+```
+
+### 5.5 LED 屏断开流程
+
+```
+1. LED 屏断开连接
+ ↓
+2. Bx6GServerListener.disconnected() 触发
+ ↓
+3. 更新数据库状态为离线
+ ledScreen.setOnline(false)
+ ledScreenService.updateLedScreen(ledScreen)
+ ↓
+4. 从连接管理器移除
+ ledDrawService.removeConnectedScreen(netId)
+```
+
+## 六、配置说明
+
+### 6.1 application.yml 配置
+
+```yaml
+led:
+ server-host: localhost
+ server-port: 3801
+ refresh-interval: 30 # 刷新间隔(秒)
+ enabled: true
+ max-connection-retries: 3
+ connection-timeout: 5000 # 连接超时(毫秒)
+ response-timeout: 10000 # 响应超时(毫秒)
+ env-timeout: 30000 # 环境初始化超时(毫秒)
+ save-path: /Users/haha/code/leddata/ # LED 图片保存路径
+```
+
+### 6.2 屏幕布局说明
+
+LED 屏内容分为三个区域:
+
+| 区域 | 位置 | 尺寸 | 内容 |
+|------|------|------|------|
+| 头部区域 | (0, 0) | width × 16 | 标题文本 |
+| 中间区域 | (0, 17) | width × (height-32) | 图片列表 |
+| 底部区域 | (0, height-16) | width × 16 | 日期时间 |
+
+## 七、数据库表结构
+
+### SysLedscreen 表
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| deviceSn | String | 设备序列号(Net ID) |
+| title | String | LED 屏标题 |
+| width | Integer | 屏幕宽度 |
+| height | Integer | 屏幕高度 |
+| projectId | Long | 项目 ID |
+| workareaId | Long | 工区 ID |
+| drawType | Integer | 绘制类型(1/2) |
+| online | Boolean | 在线状态 |
+| enabled | Long | 是否启用 |
+| isDel | Long | 是否删除 |
+
+## 八、API 接口
+
+### 8.1 获取服务器状态
+```
+GET /led/status?projectId={projectId}
+```
+返回:服务器运行状态和已连接屏幕数量
+
+### 8.2 获取所有 LED 屏信息
+```
+GET /led/screens?projectId={projectId}
+```
+返回:LED 屏列表
+
+### 8.3 获取特定 LED 屏信息
+```
+GET /led/screens/{netId}
+```
+返回:指定 LED 屏信息
+
+### 8.4 手动刷新 LED 屏
+```
+POST /led/screens/{netId}/refresh
+```
+返回:刷新结果
+
+### 8.5 更新 LED 屏配置
+```
+PUT /led/screens
+Body: SysLedscreen
+```
+返回:更新结果
+
+## 九、注意事项
+
+1. **端口占用**:确保端口 3801 未被其他程序占用
+2. **文件路径**:LED 图片保存路径需要有读写权限
+3. **网络连接**:LED 屏需要能够访问服务器 IP 和端口
+4. **定时任务**:确保 Spring Scheduling 已启用(@EnableScheduling)
+5. **线程安全**:使用 ConcurrentHashMap 管理连接,确保线程安全
+6. **异常处理**:所有 Onbon 接口调用都应包含异常处理
+7. **资源释放**:应用停止时正确关闭服务器(@PreDestroy)
+
+## 十、常见问题
+
+### 10.1 服务器启动失败
+- 检查端口 3801 是否被占用
+- 检查 Bx6G 环境初始化是否成功
+- 查看日志中的错误信息
+
+### 10.2 LED 屏无法连接
+- 检查网络连接
+- 检查 LED 屏的 IP 配置
+- 检查防火墙设置
+- 确认 LED 屏的设备序列号正确
+
+### 10.3 内容显示异常
+- 检查图片路径是否正确
+- 检查图片格式是否支持
+- 检查屏幕尺寸配置是否正确
+- 查看绘制日志
+
+## 十一、技术栈
+
+- **后端框架**:Spring Boot
+- **LED SDK**:Onbon Bx06 0.6.5-SNAPSHOT
+- **数据库**:MySQL(通过 MyBatis)
+- **定时任务**:Spring Scheduling
+- **日志**:SLF4J + Logback
+- **工具库**:Hutool
+
+## 十二、版本信息
+
+- SDK 版本:bx06-0.6.5-SNAPSHOT
+- Java 版本:1.8
+- Spring Boot 版本:3.6.2
+
+---
+
+文档生成时间:2026-03-23
diff --git a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/LedMainApplication.java b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/LedMainApplication.java
index a9f25fd7..4fb3d50a 100644
--- a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/LedMainApplication.java
+++ b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/LedMainApplication.java
@@ -183,16 +183,11 @@ public class LedMainApplication {
if (!isActuallyOnline) {
cancelScheduledTask(ledScreen.getDeviceSn());
} else {
+ // 设备上线,创建刷新任务
createRefreshTask(ledScreen);
+ logger.info("设备 {} 已上线,已创建刷新任务", ledScreen.getDeviceSn());
}
}
-
- // 主动检测设备状态,如果设备已连接但ping失败,则认为设备离线
- if (ledDrawService.isConnected(ledScreen.getDeviceSn()) && !isActuallyOnline) {
- // 从连接管理器移除屏幕
- ledDrawService.removeConnectedScreen(ledScreen.getDeviceSn());
- logger.info("从连接管理器移除离线设备: {}", ledScreen.getDeviceSn());
- }
}
} catch (Exception e) {
logger.error("定期刷新LED屏状态时出错", e);
diff --git a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/config/LedProperties.java b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/config/LedProperties.java
index 60c16396..23e5c664 100644
--- a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/config/LedProperties.java
+++ b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/config/LedProperties.java
@@ -14,6 +14,7 @@ public class LedProperties {
private int maxConnectionRetries = 3;
private int connectionTimeout = 5000; // 连接超时时间,单位毫秒
private int responseTimeout = 10000; // 响应超时时间,单位毫秒
+ private int envTimeout = 30000; // Bx6G环境初始化超时时间,单位毫秒
private String savePath = "/Users/haha/code/leddata/"; // LED图片保存路径
// Getters and Setters
@@ -73,6 +74,14 @@ public class LedProperties {
this.responseTimeout = responseTimeout;
}
+ public int getEnvTimeout() {
+ return envTimeout;
+ }
+
+ public void setEnvTimeout(int envTimeout) {
+ this.envTimeout = envTimeout;
+ }
+
public String getSavePath() {
return savePath;
}
diff --git a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/listener/LedServerListener.java b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/listener/LedServerListener.java
index 3c65e933..a4dc0d35 100644
--- a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/listener/LedServerListener.java
+++ b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/listener/LedServerListener.java
@@ -25,6 +25,12 @@ public class LedServerListener implements Bx6GServerListener {
public void connected(String socketId, String netId, Bx6GScreen screen) {
logger.info("LED屏连接成功: Socket ID={}, Net ID={}", socketId, netId);
+ // 先检查并清理可能存在的旧连接(设备重连时)
+ if (ledDrawService.isConnected(netId)) {
+ logger.info("检测到设备 {} 存在旧连接,先清理旧连接", netId);
+ ledDrawService.removeConnectedScreen(netId);
+ }
+
// 更新LED屏在线状态
SysLedscreen ledScreen = ledScreenService.getLedScreenByNetId(netId);
if (ledScreen != null) {
diff --git a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/service/LedDrawService.java b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/service/LedDrawService.java
index 11b84a95..8e477a9d 100644
--- a/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/service/LedDrawService.java
+++ b/yanzhu-modules/yanzhu-led/src/main/java/com/yanzhu/led/service/LedDrawService.java
@@ -28,6 +28,9 @@ public class LedDrawService {
@Autowired
private ILedScreenService ledScreenService;
+ @Autowired
+ private com.yanzhu.led.config.LedProperties ledProperties;
+
private static final Logger logger = LoggerFactory.getLogger(LedDrawService.class);
// 存储已连接的屏幕,便于后续操作
@@ -37,8 +40,8 @@ public class LedDrawService {
* 初始化Bx6G环境
*/
public void initializeBx6GEnv() throws Exception {
- Bx6GEnv.initial(30000); // 30秒超时
- logger.info("Bx6G环境初始化成功");
+ Bx6GEnv.initial(ledProperties.getEnvTimeout());
+ logger.info("Bx6G环境初始化成功,超时时间: {}ms", ledProperties.getEnvTimeout());
}
/**
@@ -52,7 +55,6 @@ public class LedDrawService {
logger.error("屏幕未连接: {}", ledScreen.getDeviceSn());
return false;
}
-
// 根据控制器类型创建节目文件,使用屏幕的配置文件
ProgramBxFile programFile = createProgramByControllerType(ledScreen, screen);
@@ -162,7 +164,23 @@ public class LedDrawService {
* @return true表示已连接,false表示未连接
*/
public boolean isConnected(String deviceSn) {
- return connectedScreens.containsKey(deviceSn);
+ Bx6GScreen screen = connectedScreens.get(deviceSn);
+ if (screen == null) {
+ return false;
+ }
+
+ try {
+ // 通过真实的 ping 验证设备连接状态
+ Bx6GScreen.Result> result = screen.ping();
+ boolean isOnline = result != null && result.isOK();
+ if (!isOnline) {
+ logger.warn("设备 {} ping 失败,认为已离线", deviceSn);
+ }
+ return isOnline;
+ } catch (Exception e) {
+ logger.warn("检测设备 {} 在线状态时出错: {}", deviceSn, e.getMessage());
+ return false;
+ }
}
/**
@@ -191,16 +209,40 @@ public class LedDrawService {
// 设备离线,从连接管理器中移除
connectedScreens.remove(deviceSn);
logger.info("已从连接管理器移除离线设备: {}", deviceSn);
+
+ // 同步更新数据库状态
+ updateDeviceOnlineStatus(deviceSn, false);
}
} catch (Exception e) {
logger.warn("检测设备 {} 在线状态时出错: {}, 将从连接管理器中移除", deviceSn, e.getMessage());
// 发生异常,从连接管理器中移除设备
connectedScreens.remove(deviceSn);
logger.info("已从连接管理器移除异常设备: {}", deviceSn);
+
+ // 同步更新数据库状态
+ updateDeviceOnlineStatus(deviceSn, false);
}
}
}
+ /**
+ * 更新设备在线状态到数据库
+ * @param deviceSn 设备序列号
+ * @param isOnline 在线状态
+ */
+ private void updateDeviceOnlineStatus(String deviceSn, boolean isOnline) {
+ try {
+ SysLedscreen ledScreen = ledScreenService.getLedScreenByNetId(deviceSn);
+ if (ledScreen != null) {
+ ledScreen.setOnline(isOnline);
+ ledScreenService.updateLedScreen(ledScreen);
+ logger.info("设备 {} 数据库状态已更新为: {}", deviceSn, isOnline ? "在线" : "离线");
+ }
+ } catch (Exception e) {
+ logger.error("更新设备 {} 在线状态到数据库时出错: {}", deviceSn, e.getMessage(), e);
+ }
+ }
+
/**
* 获取所有已连接的设备SN列表
*/