Browse Source

add 485 test

devab
赵明涛 3 weeks ago
parent
commit
3a0c976a11
  1. 195
      485通信超时问题排查指南.md
  2. 136
      app/src/main/java/com/ouxuan/oxface/debug/Ox485DebugHelper.java
  3. 150
      app/src/main/java/com/ouxuan/oxface/device/Ox485.java

195
485通信超时问题排查指南.md

@ -0,0 +1,195 @@
# 485通信超时问题排查指南
## 问题描述
根据您提供的日志,485模块在查询摄像头人数时出现超时问题:
```
10:34:13.824 Ox485 D 发送485人数查询命令: 0F030001000294E5
10:34:23.825 Ox485 E 485人数查询超时
```
## 问题分析
1. **命令发送成功**:日志显示命令 `0F030001000294E5` 已成功发送
2. **无响应数据**:在10秒超时时间内,没有收到任何响应数据
3. **可能原因**
- 485设备未正常工作或断电
- 485通信线路故障
- 设备地址配置错误
- 波特率不匹配
- 串口权限或占用问题
## 代码修改内容
### 1. 增强的发送命令方法
- 添加了串口状态检查
- 增加了详细的发送日志
- 验证命令发送结果
### 2. 改进的连接建立方法
- 增加了设备文件存在性检查
- 添加了文件权限检查
- 验证串口打开状态
### 3. 详细的超时处理
- 记录准确的等待时间
- 输出详细的状态信息
- 提供故障诊断信息
### 4. 新增诊断功能
- `diagnose485Connection()` 方法:全面检查485连接状态
- `Ox485DebugHelper` 类:提供完整的诊断工具
## 使用调试工具
### 在代码中调用诊断方法
```java
// 方法1:完整诊断(推荐)
Ox485DebugHelper.runFullDiagnosis();
// 方法2:快速测试
Ox485DebugHelper.quickTest();
// 方法3:单独诊断连接
Ox485.getInstance().diagnose485Connection();
```
### 查看诊断结果
诊断工具会检查以下内容:
1. **串口设备文件**
- 文件是否存在:`/dev/ttyS6`
- 文件权限:可读/可写权限
2. **串口管理器状态**
- 串口是否打开
- 内部连接状态
- 连接中状态
3. **485配置**
- 485模式开关状态
- 串口路径、波特率等参数
- 查询命令格式
4. **连接历史**
- 最后成功通信时间
- 重试次数统计
5. **实际通信测试**
- 尝试发送命令并等待响应
## 排查步骤
### 第一步:运行诊断工具
在您的Activity中添加以下代码:
```java
// 在onCreate或其他适当位置调用
Ox485DebugHelper.runFullDiagnosis();
```
### 第二步:检查硬件连接
1. 确认485设备电源是否正常
2. 检查485通信线路连接
3. 验证设备地址设置(当前使用0F)
4. 确认设备波特率设置(当前使用9600)
### 第三步:检查系统权限
```bash
# 检查串口设备文件
ls -l /dev/ttyS6
# 检查权限
chmod 666 /dev/ttyS6 # 如果权限不足
```
### 第四步:测试通信协议
当前使用的485命令:
- **命令**:`0F030001000294E5`
- **含义**:查询地址为0F的设备的人数数据
- **期望响应**:9字节数据,格式为 `[15, 3, 4, 0, 人数, *, *, *, *]`
### 第五步:逐步排查
1. **确认设备响应**
```java
// 发送简单的测试命令
Ox485.getInstance().send485HexCommand("0F030001000294E5");
```
2. **监控所有接收数据**
- 查看日志中是否有任何 `Ox485接收到数据` 的记录
- 即使是错误格式的数据也会被记录
3. **尝试不同的设备地址**
```java
// 如果0F不响应,尝试其他地址
Ox485.getInstance().send485HexCommand("01030001000295F4"); // 地址01
```
## 常见问题解决方案
### 问题1:串口设备文件不存在
**解决方案**:
- 检查硬件连接
- 确认设备树配置
- 重启设备
### 问题2:权限不足
**解决方案**:
```bash
chmod 666 /dev/ttyS6
chown system:system /dev/ttyS6
```
### 问题3:设备不响应
**解决方案**:
- 检查485设备电源
- 验证设备地址配置
- 使用万用表检查485信号
- 尝试不同的波特率
### 问题4:数据格式错误
**解决方案**:
- 确认设备协议文档
- 检查命令格式
- 验证校验码计算
## 预期的正常日志
修改后,正常的485通信日志应该类似:
```
Ox485 D 485串口状态检查通过,开始发送命令
Ox485 D 准备发送485命令: 0F030001000294E5, 转换后字节数组: [15, 3, 0, 1, 0, 2, -108, -27]
Ox485 D 485命令发送结果: true
Ox485 D Ox485发送数据成功: 0f030001000294e5, 数据长度: 8
Ox485 D Ox485接收到数据: 0f03040000xxxx, 字节数组: [15, 3, 4, 0, X, ...], 数据长度: 9
Ox485 D 485获取人数成功: X
```
## 联系支持
如果按照上述步骤仍无法解决问题,请提供:
1. 完整的诊断日志
2. 硬件连接图
3. 485设备型号和协议文档
4. 系统版本信息
修改后的代码增加了大量诊断信息,应该能帮助您快速定位485通信超时的根本原因。

136
app/src/main/java/com/ouxuan/oxface/debug/Ox485DebugHelper.java

@ -0,0 +1,136 @@
package com.ouxuan.oxface.debug;
import com.ouxuan.oxface.device.Ox485;
import com.ouxuan.oxface.utils.LogManager;
/**
* Ox485调试助手
* 用于排查485通信问题
*
* @author AI Assistant
* @version 1.0
* @date 2024/09/22
*/
public class Ox485DebugHelper {
private static final String TAG = "Ox485DebugHelper";
/**
* 执行完整的485诊断流程
*/
public static void runFullDiagnosis() {
LogManager.logInfo(TAG, "========== 开始485完整诊断流程 ==========");
Ox485 ox485 = Ox485.getInstance();
// 1. 基础状态检查
LogManager.logInfo(TAG, "步骤1: 基础状态检查");
String status = ox485.get485Status();
LogManager.logInfo(TAG, "当前状态: " + status);
// 2. 连接诊断
LogManager.logInfo(TAG, "步骤2: 连接诊断");
ox485.diagnose485Connection();
// 3. 等待3秒后进行测试
LogManager.logInfo(TAG, "步骤3: 等待3秒后进行通信测试");
new android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(() -> {
testCommunication();
}, 3000);
LogManager.logInfo(TAG, "========== 485完整诊断流程结束 ==========");
}
/**
* 测试485通信
*/
private static void testCommunication() {
LogManager.logInfo(TAG, "========== 开始485通信测试 ==========");
Ox485 ox485 = Ox485.getInstance();
ox485.sendHex485ForPeopleNum(new Ox485.PeopleNumCallback() {
@Override
public void onSuccess(int peopleNum) {
LogManager.logInfo(TAG, "✓ 485通信测试成功 - 获取到人数: " + peopleNum);
LogManager.logInfo(TAG, "========== 485通信测试完成(成功) ==========");
}
@Override
public void onError(String errorMessage) {
LogManager.logError(TAG, "✗ 485通信测试失败: " + errorMessage);
// 失败时打印详细状态
String status = ox485.get485Status();
LogManager.logError(TAG, "失败时状态: " + status);
// 建议解决方案
printTroubleshootingSuggestions(errorMessage);
LogManager.logInfo(TAG, "========== 485通信测试完成(失败) ==========");
}
});
}
/**
* 打印故障排查建议
*/
private static void printTroubleshootingSuggestions(String errorMessage) {
LogManager.logInfo(TAG, "========== 故障排查建议 ==========");
if (errorMessage.contains("超时")) {
LogManager.logInfo(TAG, "超时问题排查建议:");
LogManager.logInfo(TAG, "1. 检查485设备是否正常工作");
LogManager.logInfo(TAG, "2. 检查485设备连接线路");
LogManager.logInfo(TAG, "3. 确认设备地址是否正确(当前命令中设备地址为0F)");
LogManager.logInfo(TAG, "4. 检查485设备波特率是否匹配(当前: 9600)");
LogManager.logInfo(TAG, "5. 使用万用表或示波器检查485信号");
} else if (errorMessage.contains("串口")) {
LogManager.logInfo(TAG, "串口问题排查建议:");
LogManager.logInfo(TAG, "1. 检查串口设备文件是否存在: /dev/ttyS6");
LogManager.logInfo(TAG, "2. 检查应用权限是否足够");
LogManager.logInfo(TAG, "3. 确认串口未被其他程序占用");
LogManager.logInfo(TAG, "4. 重启设备后重试");
} else if (errorMessage.contains("解析")) {
LogManager.logInfo(TAG, "数据解析问题排查建议:");
LogManager.logInfo(TAG, "1. 检查接收到的数据格式");
LogManager.logInfo(TAG, "2. 确认485设备返回的数据协议");
LogManager.logInfo(TAG, "3. 验证数据长度和校验码");
} else {
LogManager.logInfo(TAG, "通用排查建议:");
LogManager.logInfo(TAG, "1. 重启应用程序");
LogManager.logInfo(TAG, "2. 检查硬件连接");
LogManager.logInfo(TAG, "3. 联系技术支持");
}
LogManager.logInfo(TAG, "========== 故障排查建议结束 ==========");
}
/**
* 快速测试485连接
*/
public static void quickTest() {
LogManager.logInfo(TAG, "========== 485快速测试 ==========");
Ox485 ox485 = Ox485.getInstance();
// 如果485模式未开启则开启
if (!ox485.isGateCamera485OxOn()) {
LogManager.logInfo(TAG, "485模式未开启,正在开启...");
ox485.setGateCamera485OxOn(true);
}
// 执行一次查询
ox485.sendHex485ForPeopleNum(new Ox485.PeopleNumCallback() {
@Override
public void onSuccess(int peopleNum) {
LogManager.logInfo(TAG, "✓ 快速测试成功 - 人数: " + peopleNum);
}
@Override
public void onError(String errorMessage) {
LogManager.logError(TAG, "✗ 快速测试失败: " + errorMessage);
}
});
}
}

150
app/src/main/java/com/ouxuan/oxface/device/Ox485.java

@ -181,6 +181,19 @@ public class Ox485 {
* @param callback 结果回调 * @param callback 结果回调
*/ */
private void sendCommandAndWaitResponse(PeopleNumCallback callback) { private void sendCommandAndWaitResponse(PeopleNumCallback callback) {
// 发送前检查串口状态
if (!serialPortManager.isOpened()) {
String errorMsg = "串口未打开,无法发送命令";
LogManager.logError(TAG, errorMsg);
isSerialPortOpen = false;
if (callback != null) {
mainHandler.post(() -> callback.onError(errorMsg));
}
return;
}
LogManager.logInfo(TAG, "485串口状态检查通过,开始发送命令");
// 设置数据监听器 // 设置数据监听器
serialPortManager.setOnSerialPortDataListener(new OnSerialPortDataListener() { serialPortManager.setOnSerialPortDataListener(new OnSerialPortDataListener() {
@Override @Override
@ -189,11 +202,11 @@ public class Ox485 {
lastRecvTime = System.nanoTime(); lastRecvTime = System.nanoTime();
String hexData = ByteUtils.bytesToHexString(bytes); String hexData = ByteUtils.bytesToHexString(bytes);
LogManager.logInfo(TAG, "Ox485接收到数据: " + hexData + ", 字节数组: " + java.util.Arrays.toString(bytes));
LogManager.logInfo(TAG, "Ox485接收到数据: " + hexData + ", 字节数组: " + java.util.Arrays.toString(bytes) + ", 数据长度: " + bytes.length);
// 数据包过滤只处理以[15, 3, 4, 0]开头的正常响应数据包 // 数据包过滤只处理以[15, 3, 4, 0]开头的正常响应数据包
if (!isNormalResponsePacket(bytes)) { if (!isNormalResponsePacket(bytes)) {
LogManager.logInfo(TAG, "Ox485接收到非正常响应数据包,已过滤: " + hexData);
LogManager.logInfo(TAG, "Ox485接收到非正常响应数据包,已过滤: " + hexData + ", 期望格式: [15, 3, 4, 0]");
return; return;
} }
@ -206,7 +219,7 @@ public class Ox485 {
// 解析人数数据 // 解析人数数据
int peopleNum = get485PeopleNum(bytes); int peopleNum = get485PeopleNum(bytes);
if (peopleNum >= 0) { if (peopleNum >= 0) {
LogManager.logInfo(TAG, "15:45:22.734 Ox485 D 485获取人数成功: " + peopleNum);
LogManager.logInfo(TAG, "485获取人数成功: " + peopleNum);
if (callback != null) { if (callback != null) {
mainHandler.post(() -> callback.onSuccess(peopleNum)); mainHandler.post(() -> callback.onSuccess(peopleNum));
} }
@ -222,15 +235,29 @@ public class Ox485 {
@Override @Override
public void onDataSent(byte[] bytes) { public void onDataSent(byte[] bytes) {
String hexData = ByteUtils.bytesToHexString(bytes); String hexData = ByteUtils.bytesToHexString(bytes);
LogManager.logInfo(TAG, "Ox485发送数据: " + hexData);
LogManager.logInfo(TAG, "Ox485发送数据成功: " + hexData + ", 数据长度: " + bytes.length);
} }
}); });
// 发送HEX命令 // 发送HEX命令
try { try {
byte[] hexBytes = hexStringToByteArray(HEX_COMMAND_GET_PEOPLE_NUM); byte[] hexBytes = hexStringToByteArray(HEX_COMMAND_GET_PEOPLE_NUM);
serialPortManager.sendBytes(hexBytes);
LogManager.logInfo(TAG, "15:45:22.734 Ox485 D 发送485人数查询命令: " + HEX_COMMAND_GET_PEOPLE_NUM);
// 验证要发送的数据
LogManager.logInfo(TAG, "准备发送485命令: " + HEX_COMMAND_GET_PEOPLE_NUM + ", 转换后字节数组: " + java.util.Arrays.toString(hexBytes));
boolean sendResult = serialPortManager.sendBytes(hexBytes);
LogManager.logInfo(TAG, "485命令发送结果: " + sendResult);
if (!sendResult) {
String errorMsg = "485命令发送失败,可能串口连接异常";
LogManager.logError(TAG, errorMsg);
isSerialPortOpen = false;
if (callback != null) {
mainHandler.post(() -> callback.onError(errorMsg));
}
return;
}
// 设置超时处理 // 设置超时处理
setupTimeout(callback); setupTimeout(callback);
@ -255,9 +282,17 @@ public class Ox485 {
private void setupTimeout(PeopleNumCallback callback) { private void setupTimeout(PeopleNumCallback callback) {
cancelTimeout(); // 先取消之前的超时任务 cancelTimeout(); // 先取消之前的超时任务
long startTime = System.currentTimeMillis();
LogManager.logInfo(TAG, "设置485查询超时处理,超时时间: " + (TIMEOUT_485_MILLIS / 1000) + "秒");
timeoutRunnable = () -> { timeoutRunnable = () -> {
String errorMsg = "485人数查询超时";
long elapsedTime = System.currentTimeMillis() - startTime;
String errorMsg = "485人数查询超时(等待时间: " + elapsedTime + "ms)";
LogManager.logError(TAG, errorMsg); LogManager.logError(TAG, errorMsg);
LogManager.logError(TAG, "485超时详情 - 串口状态: " + (serialPortManager != null ? serialPortManager.isOpened() : "null") +
", 内部连接状态: " + isSerialPortOpen +
", 最后成功时间: " + (lastSuccessTime > 0 ?
new java.text.SimpleDateFormat("HH:mm:ss.SSS").format(new java.util.Date(lastSuccessTime)) : "无"));
// 超时可能是连接问题重置连接状态 // 超时可能是连接问题重置连接状态
isSerialPortOpen = false; isSerialPortOpen = false;
@ -367,6 +402,76 @@ public class Ox485 {
} }
/** /**
* 485连接诊断方法
* 用于排查485通信问题
*/
public void diagnose485Connection() {
LogManager.logInfo(TAG, "开始485连接诊断");
// 1. 检查串口设备文件
File serialPortFile = new File(DEFAULT_SERIAL_PORT_PATH);
LogManager.logInfo(TAG, "诊断1 - 串口设备文件: " + DEFAULT_SERIAL_PORT_PATH);
LogManager.logInfo(TAG, " - 文件存在: " + serialPortFile.exists());
if (serialPortFile.exists()) {
LogManager.logInfo(TAG, " - 可读权限: " + serialPortFile.canRead());
LogManager.logInfo(TAG, " - 可写权限: " + serialPortFile.canWrite());
LogManager.logInfo(TAG, " - 文件大小: " + serialPortFile.length());
}
// 2. 检查串口管理器状态
LogManager.logInfo(TAG, "诊断2 - 串口管理器状态:");
LogManager.logInfo(TAG, " - 串口是否打开: " + (serialPortManager != null ? serialPortManager.isOpened() : "null"));
LogManager.logInfo(TAG, " - 内部连接状态: " + isSerialPortOpen);
LogManager.logInfo(TAG, " - 是否连接中: " + isConnecting);
// 3. 检查485模式开关
LogManager.logInfo(TAG, "诊断3 - 485配置:");
LogManager.logInfo(TAG, " - 485模式开关: " + gateCamera485OxOn);
LogManager.logInfo(TAG, " - 串口路径: " + DEFAULT_SERIAL_PORT_PATH);
LogManager.logInfo(TAG, " - 波特率: " + DEFAULT_BAUD_RATE);
LogManager.logInfo(TAG, " - 停止位: " + DEFAULT_STOP_BITS);
LogManager.logInfo(TAG, " - 查询命令: " + HEX_COMMAND_GET_PEOPLE_NUM);
// 4. 检查连接历史
LogManager.logInfo(TAG, "诊断4 - 连接历史:");
String lastSuccessStr = lastSuccessTime > 0 ?
new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new java.util.Date(lastSuccessTime)) :
"无";
LogManager.logInfo(TAG, " - 最后成功时间: " + lastSuccessStr);
LogManager.logInfo(TAG, " - 当前重试次数: " + currentRetryCount);
LogManager.logInfo(TAG, " - 最大重试次数: " + MAX_RETRY_COUNT);
// 5. 尝试重新建立连接进行测试
LogManager.logInfo(TAG, "诊断5 - 连接测试:");
if (gateCamera485OxOn) {
LogManager.logInfo(TAG, " - 485模式已启用,尝试测试连接...");
// 强制关闭现有连接
if (isSerialPortOpen) {
LogManager.logInfo(TAG, " - 关闭现有连接进行重新测试");
close485SerialPort();
}
// 尝试重新连接并发送测试命令
sendHex485ForPeopleNum(new PeopleNumCallback() {
@Override
public void onSuccess(int peopleNum) {
LogManager.logInfo(TAG, "诊断测试成功 - 获取到人数: " + peopleNum);
}
@Override
public void onError(String errorMessage) {
LogManager.logError(TAG, "诊断测试失败: " + errorMessage);
}
});
} else {
LogManager.logWarning(TAG, " - 485模式未启用,无法进行连接测试");
}
LogManager.logInfo(TAG, "485连接诊断完成");
}
/**
* 获取485串口状态懒加载连接管理模式 * 获取485串口状态懒加载连接管理模式
* @return 串口状态描述 * @return 串口状态描述
*/ */
@ -513,17 +618,37 @@ public class Ox485 {
currentRetryCount = 0; currentRetryCount = 0;
try { try {
LogManager.logInfo(TAG, "15:45:22.734 Ox485 D 开始建立485连接 - 路径: " + DEFAULT_SERIAL_PORT_PATH + ", 波特率: " + DEFAULT_BAUD_RATE);
LogManager.logInfo(TAG, "开始建立485连接 - 路径: " + DEFAULT_SERIAL_PORT_PATH + ", 波特率: " + DEFAULT_BAUD_RATE);
// 配置串口参数
// 检查串口文件是否存在
File serialPortFile = new File(DEFAULT_SERIAL_PORT_PATH); File serialPortFile = new File(DEFAULT_SERIAL_PORT_PATH);
if (!serialPortFile.exists()) {
isConnecting = false;
String errorMsg = "485串口设备文件不存在: " + DEFAULT_SERIAL_PORT_PATH;
LogManager.logError(TAG, errorMsg);
callback.onError(errorMsg);
return;
}
LogManager.logInfo(TAG, "485串口设备文件存在,文件权限: 可读=" + serialPortFile.canRead() + ", 可写=" + serialPortFile.canWrite());
// 配置串口参数
boolean openResult = serialPortManager.openSerialPort(serialPortFile, DEFAULT_BAUD_RATE); boolean openResult = serialPortManager.openSerialPort(serialPortFile, DEFAULT_BAUD_RATE);
LogManager.logInfo(TAG, "15:45:22.734 Ox485 D 485串口打开结果: " + openResult);
LogManager.logInfo(TAG, "485串口打开结果: " + openResult);
if (!openResult) { if (!openResult) {
isConnecting = false; isConnecting = false;
String errorMsg = "打开485串口失败: " + DEFAULT_SERIAL_PORT_PATH;
String errorMsg = "打开485串口失败: " + DEFAULT_SERIAL_PORT_PATH + ",可能原因:设备不存在、权限不足或设备被占用";
LogManager.logError(TAG, errorMsg);
callback.onError(errorMsg);
return;
}
// 验证串口是否真正打开
if (!serialPortManager.isOpened()) {
isConnecting = false;
String errorMsg = "485串口打开后状态检查失败,串口可能未正确初始化";
LogManager.logError(TAG, errorMsg); LogManager.logError(TAG, errorMsg);
callback.onError(errorMsg); callback.onError(errorMsg);
return; return;
@ -539,7 +664,8 @@ public class Ox485 {
lastSuccessTime = System.currentTimeMillis(); lastSuccessTime = System.currentTimeMillis();
currentRetryCount = 0; currentRetryCount = 0;
LogManager.logInfo(TAG, "485连接建立成功,进入懒加载复用模式");
LogManager.logInfo(TAG, "485连接建立成功,进入懒加载复用模式,最后成功时间: " +
new java.text.SimpleDateFormat("HH:mm:ss.SSS").format(new java.util.Date(lastSuccessTime)));
callback.onConnected(); callback.onConnected();
} catch (Exception e) { } catch (Exception e) {

Loading…
Cancel
Save