From 3a0c976a11a37dd6197d210bc14b8969fc910b28 Mon Sep 17 00:00:00 2001 From: MT <3075067877@qq.com> Date: Mon, 22 Sep 2025 10:44:08 +0800 Subject: [PATCH] add 485 test --- 485通信超时问题排查指南.md | 195 +++++++++++++++++++++ .../com/ouxuan/oxface/debug/Ox485DebugHelper.java | 136 ++++++++++++++ .../main/java/com/ouxuan/oxface/device/Ox485.java | 150 ++++++++++++++-- 3 files changed, 469 insertions(+), 12 deletions(-) create mode 100644 485通信超时问题排查指南.md create mode 100644 app/src/main/java/com/ouxuan/oxface/debug/Ox485DebugHelper.java diff --git a/485通信超时问题排查指南.md b/485通信超时问题排查指南.md new file mode 100644 index 0000000..5452e03 --- /dev/null +++ b/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通信超时的根本原因。 \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/debug/Ox485DebugHelper.java b/app/src/main/java/com/ouxuan/oxface/debug/Ox485DebugHelper.java new file mode 100644 index 0000000..3a84173 --- /dev/null +++ b/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); + } + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/device/Ox485.java b/app/src/main/java/com/ouxuan/oxface/device/Ox485.java index fd119b5..6c26ab5 100644 --- a/app/src/main/java/com/ouxuan/oxface/device/Ox485.java +++ b/app/src/main/java/com/ouxuan/oxface/device/Ox485.java @@ -181,6 +181,19 @@ public class Ox485 { * @param 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() { @Override @@ -189,11 +202,11 @@ public class Ox485 { lastRecvTime = System.nanoTime(); 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]开头的正常响应数据包 if (!isNormalResponsePacket(bytes)) { - LogManager.logInfo(TAG, "Ox485接收到非正常响应数据包,已过滤: " + hexData); + LogManager.logInfo(TAG, "Ox485接收到非正常响应数据包,已过滤: " + hexData + ", 期望格式: [15, 3, 4, 0]"); return; } @@ -206,7 +219,7 @@ public class Ox485 { // 解析人数数据 int peopleNum = get485PeopleNum(bytes); if (peopleNum >= 0) { - LogManager.logInfo(TAG, "15:45:22.734 Ox485 D 485获取人数成功: " + peopleNum); + LogManager.logInfo(TAG, "485获取人数成功: " + peopleNum); if (callback != null) { mainHandler.post(() -> callback.onSuccess(peopleNum)); } @@ -222,15 +235,29 @@ public class Ox485 { @Override public void onDataSent(byte[] bytes) { String hexData = ByteUtils.bytesToHexString(bytes); - LogManager.logInfo(TAG, "Ox485发送数据: " + hexData); + LogManager.logInfo(TAG, "Ox485发送数据成功: " + hexData + ", 数据长度: " + bytes.length); } }); // 发送HEX命令 try { 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); @@ -255,9 +282,17 @@ public class Ox485 { private void setupTimeout(PeopleNumCallback callback) { cancelTimeout(); // 先取消之前的超时任务 + long startTime = System.currentTimeMillis(); + LogManager.logInfo(TAG, "设置485查询超时处理,超时时间: " + (TIMEOUT_485_MILLIS / 1000) + "秒"); + timeoutRunnable = () -> { - String errorMsg = "485人数查询超时"; + long elapsedTime = System.currentTimeMillis() - startTime; + String errorMsg = "485人数查询超时(等待时间: " + elapsedTime + "ms)"; 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; @@ -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串口状态(懒加载连接管理模式) * @return 串口状态描述 */ @@ -513,17 +618,37 @@ public class Ox485 { currentRetryCount = 0; 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); + 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); - LogManager.logInfo(TAG, "15:45:22.734 Ox485 D 485串口打开结果: " + openResult); + LogManager.logInfo(TAG, "485串口打开结果: " + openResult); if (!openResult) { 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); callback.onError(errorMsg); return; @@ -539,7 +664,8 @@ public class Ox485 { lastSuccessTime = System.currentTimeMillis(); 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(); } catch (Exception e) {