LED问题修复

dev_xd
lj7788 2026-03-23 18:23:50 +08:00
parent dd12a3741c
commit 9e66b8422a
5 changed files with 552 additions and 11 deletions

View File

@ -0,0 +1,489 @@
# LED 屏管理系统技术文档
## 一、项目概述
本项目是基于 Onbon Bx06 系列 LED 控制器开发的 LED 屏管理系统,实现了 LED 屏的自动连接、内容绘制、定时刷新和状态监控等功能。
## 二、核心依赖
### Maven 依赖配置
项目使用 Onbon Bx06 系列 SDK主要依赖包括
```xml
<!-- Bx06 核心 SDK -->
<dependency>
<groupId>bx06</groupId>
<artifactId>bx06</artifactId>
<version>0.6.5-SNAPSHOT</version>
</dependency>
<!-- Bx06 消息处理 -->
<dependency>
<groupId>bx06.message</groupId>
<artifactId>bx06.message</artifactId>
<version>0.6.5-SNAPSHOT</version>
</dependency>
<!-- 通信相关 -->
<dependency>
<groupId>uia-comm</groupId>
<artifactId>uia-comm</artifactId>
<version>0.5.3-SNAPSHOT</version>
</dependency>
<!-- 其他依赖jaxb、rxtx、simple-xml、slf4j 等 -->
```
## 三、核心架构
### 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

View File

@ -183,16 +183,11 @@ public class LedMainApplication {
if (!isActuallyOnline) { if (!isActuallyOnline) {
cancelScheduledTask(ledScreen.getDeviceSn()); cancelScheduledTask(ledScreen.getDeviceSn());
} else { } else {
// 设备上线,创建刷新任务
createRefreshTask(ledScreen); createRefreshTask(ledScreen);
logger.info("设备 {} 已上线,已创建刷新任务", ledScreen.getDeviceSn());
} }
} }
// 主动检测设备状态如果设备已连接但ping失败则认为设备离线
if (ledDrawService.isConnected(ledScreen.getDeviceSn()) && !isActuallyOnline) {
// 从连接管理器移除屏幕
ledDrawService.removeConnectedScreen(ledScreen.getDeviceSn());
logger.info("从连接管理器移除离线设备: {}", ledScreen.getDeviceSn());
}
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("定期刷新LED屏状态时出错", e); logger.error("定期刷新LED屏状态时出错", e);

View File

@ -14,6 +14,7 @@ public class LedProperties {
private int maxConnectionRetries = 3; private int maxConnectionRetries = 3;
private int connectionTimeout = 5000; // 连接超时时间,单位毫秒 private int connectionTimeout = 5000; // 连接超时时间,单位毫秒
private int responseTimeout = 10000; // 响应超时时间,单位毫秒 private int responseTimeout = 10000; // 响应超时时间,单位毫秒
private int envTimeout = 30000; // Bx6G环境初始化超时时间单位毫秒
private String savePath = "/Users/haha/code/leddata/"; // LED图片保存路径 private String savePath = "/Users/haha/code/leddata/"; // LED图片保存路径
// Getters and Setters // Getters and Setters
@ -73,6 +74,14 @@ public class LedProperties {
this.responseTimeout = responseTimeout; this.responseTimeout = responseTimeout;
} }
public int getEnvTimeout() {
return envTimeout;
}
public void setEnvTimeout(int envTimeout) {
this.envTimeout = envTimeout;
}
public String getSavePath() { public String getSavePath() {
return savePath; return savePath;
} }

View File

@ -25,6 +25,12 @@ public class LedServerListener implements Bx6GServerListener {
public void connected(String socketId, String netId, Bx6GScreen screen) { public void connected(String socketId, String netId, Bx6GScreen screen) {
logger.info("LED屏连接成功: Socket ID={}, Net ID={}", socketId, netId); logger.info("LED屏连接成功: Socket ID={}, Net ID={}", socketId, netId);
// 先检查并清理可能存在的旧连接(设备重连时)
if (ledDrawService.isConnected(netId)) {
logger.info("检测到设备 {} 存在旧连接,先清理旧连接", netId);
ledDrawService.removeConnectedScreen(netId);
}
// 更新LED屏在线状态 // 更新LED屏在线状态
SysLedscreen ledScreen = ledScreenService.getLedScreenByNetId(netId); SysLedscreen ledScreen = ledScreenService.getLedScreenByNetId(netId);
if (ledScreen != null) { if (ledScreen != null) {

View File

@ -28,6 +28,9 @@ public class LedDrawService {
@Autowired @Autowired
private ILedScreenService ledScreenService; private ILedScreenService ledScreenService;
@Autowired
private com.yanzhu.led.config.LedProperties ledProperties;
private static final Logger logger = LoggerFactory.getLogger(LedDrawService.class); private static final Logger logger = LoggerFactory.getLogger(LedDrawService.class);
// 存储已连接的屏幕,便于后续操作 // 存储已连接的屏幕,便于后续操作
@ -37,8 +40,8 @@ public class LedDrawService {
* Bx6G * Bx6G
*/ */
public void initializeBx6GEnv() throws Exception { public void initializeBx6GEnv() throws Exception {
Bx6GEnv.initial(30000); // 30秒超时 Bx6GEnv.initial(ledProperties.getEnvTimeout());
logger.info("Bx6G环境初始化成功"); logger.info("Bx6G环境初始化成功,超时时间: {}ms", ledProperties.getEnvTimeout());
} }
/** /**
@ -52,7 +55,6 @@ public class LedDrawService {
logger.error("屏幕未连接: {}", ledScreen.getDeviceSn()); logger.error("屏幕未连接: {}", ledScreen.getDeviceSn());
return false; return false;
} }
// 根据控制器类型创建节目文件,使用屏幕的配置文件 // 根据控制器类型创建节目文件,使用屏幕的配置文件
ProgramBxFile programFile = createProgramByControllerType(ledScreen, screen); ProgramBxFile programFile = createProgramByControllerType(ledScreen, screen);
@ -162,7 +164,23 @@ public class LedDrawService {
* @return truefalse * @return truefalse
*/ */
public boolean isConnected(String deviceSn) { 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); connectedScreens.remove(deviceSn);
logger.info("已从连接管理器移除离线设备: {}", deviceSn); logger.info("已从连接管理器移除离线设备: {}", deviceSn);
// 同步更新数据库状态
updateDeviceOnlineStatus(deviceSn, false);
} }
} catch (Exception e) { } catch (Exception e) {
logger.warn("检测设备 {} 在线状态时出错: {}, 将从连接管理器中移除", deviceSn, e.getMessage()); logger.warn("检测设备 {} 在线状态时出错: {}, 将从连接管理器中移除", deviceSn, e.getMessage());
// 发生异常,从连接管理器中移除设备 // 发生异常,从连接管理器中移除设备
connectedScreens.remove(deviceSn); connectedScreens.remove(deviceSn);
logger.info("已从连接管理器移除异常设备: {}", 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 * SN
*/ */