From 911d70f21a7dac7fbe85b9a1025a1543811ed826 Mon Sep 17 00:00:00 2001 From: "3075067877@qq.com" <3075067877@qq.com> Date: Fri, 12 Sep 2025 17:44:33 +0800 Subject: [PATCH] add Ox485 --- app/build.gradle | 3 + .../main/java/com/ouxuan/oxface/DebugActivity.java | 83 ++++- .../com/ouxuan/oxface/device/GateABController.java | 118 ++++++ .../main/java/com/ouxuan/oxface/device/Ox485.java | 406 +++++++++++++++++++++ app/src/main/res/layout/activity_debug.xml | 7 +- 5 files changed, 605 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/com/ouxuan/oxface/device/Ox485.java diff --git a/app/build.gradle b/app/build.gradle index d4225fa..443ecca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -91,6 +91,9 @@ dependencies { // 华为扫码库 implementation "com.huawei.hms:scanplus:2.12.0.301" + + // 串口通信库 + implementation 'com.github.kongqw:SerialPortLibrary:2.1.1' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/app/src/main/java/com/ouxuan/oxface/DebugActivity.java b/app/src/main/java/com/ouxuan/oxface/DebugActivity.java index d932041..ba0bcd6 100644 --- a/app/src/main/java/com/ouxuan/oxface/DebugActivity.java +++ b/app/src/main/java/com/ouxuan/oxface/DebugActivity.java @@ -22,8 +22,9 @@ import com.baidu.idl.face.main.finance.listener.SdkInitListener; import com.baidu.idl.face.main.finance.manager.FaceSDKManager; import com.ouxuan.oxface.device.DeviceUtils; +import com.ouxuan.oxface.device.GateABController; import com.ouxuan.oxface.device.HuaWeiScanManager; -import com.ouxuan.oxface.device.OxGpio; +import com.ouxuan.oxface.device.Ox485; import com.ouxuan.oxface.device.RelayController; import com.ouxuan.oxface.network.utils.NetworkUtils; import com.ouxuan.oxface.utils.AutoStartManager; @@ -42,7 +43,8 @@ public class DebugActivity extends Activity { private ScrollView logScrollView; private AutoStartManager autoStartManager; private RelayController relayController; - private OxGpio oxGpio; + private Ox485 ox485; + private GateABController gateABController; @Override protected void onCreate(Bundle savedInstanceState) { @@ -65,8 +67,10 @@ public class DebugActivity extends Activity { private void initManagers() { autoStartManager = AutoStartManager.getInstance(this); relayController = RelayController.getInstance(); - oxGpio = OxGpio.getInstance(); - oxGpio.initialize(this); + ox485 = Ox485.getInstance(); + ox485.initialize(this); + gateABController = GateABController.getInstance(); + gateABController.initialize(this); } private void setupClickListeners() { @@ -251,6 +255,15 @@ public class DebugActivity extends Activity { test485PeopleNum(); } }); + + // AB门485测试按钮 + Button btnTestGateAB485 = findViewById(R.id.btnTestGateAB485); + btnTestGateAB485.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + testGateAB485PeopleNum(); + } + }); } /** @@ -720,19 +733,19 @@ public class DebugActivity extends Activity { private void test485PeopleNum() { logMessage("开始485人数测试..."); - if (oxGpio == null) { - logMessage("OxGpio未初始化"); - showToast("OxGpio未初始化"); + if (ox485 == null) { + logMessage("Ox485未初始化"); + showToast("Ox485未初始化"); return; } try { // 设置485模式开关为启用状态 - oxGpio.setGateCamera485OxOn(true); + ox485.setGateCamera485OxOn(true); logMessage("已启用485模式"); // 调用sendHex485ForPeopleNum方法 - oxGpio.sendHex485ForPeopleNum(new OxGpio.PeopleNumCallback() { + ox485.sendHex485ForPeopleNum(new Ox485.PeopleNumCallback() { @Override public void onSuccess(int peopleNum) { logMessage("485人数获取成功: " + peopleNum + " 人"); @@ -757,4 +770,56 @@ public class DebugActivity extends Activity { showToast("485人数测试失败"); } } + + /** + * 测试GateABController的485人数获取功能 + */ + private void testGateAB485PeopleNum() { + logMessage("开始测试GateAB控制器485人数获取..."); + + if (gateABController == null) { + logMessage("GateABController未初始化"); + showToast("GateABController未初始化"); + return; + } + + try { + // 获取当前配置信息 + GateABController.GateABConfig config = gateABController.getCurrentConfig(); + logMessage("AB门当前配置 - 485模式: " + config.gateCamera485OXOn); + + // 检查485模式状态 + boolean is485Enabled = gateABController.is485ModeEnabled(); + logMessage("485模式状态: " + (is485Enabled ? "已启用" : "未启用")); + + // 获取485状态信息 + String status485 = gateABController.get485Status(); + logMessage("485状态信息: " + status485); + + // 调用GateABController的get485PeopleNum方法 + gateABController.get485PeopleNum(new GateABController.PeopleNumCallback() { + @Override + public void onSuccess(int peopleNum) { + logMessage("GateAB 485人数获取成功: " + peopleNum + " 人"); + Log.i(TAG, "GateAB 485人数获取成功: " + peopleNum + " 人"); + showToast("GateAB 485人数获取成功: " + peopleNum + " 人"); + } + + @Override + public void onError(String errorMessage) { + logMessage("GateAB 485人数获取失败: " + errorMessage); + Log.e(TAG, "GateAB 485人数获取失败: " + errorMessage); + showToast("GateAB 485人数获取失败: " + errorMessage); + } + }); + + logMessage("已通过GateABController发送485人数查询请求,等待响应..."); + showToast("通过GateAB查询485人数..."); + + } catch (Exception e) { + Log.e(TAG, "GateAB 485人数测试失败", e); + logMessage("GateAB 485人数测试失败: " + e.getMessage()); + showToast("GateAB 485人数测试失败"); + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/device/GateABController.java b/app/src/main/java/com/ouxuan/oxface/device/GateABController.java index 948052e..3ef4e89 100644 --- a/app/src/main/java/com/ouxuan/oxface/device/GateABController.java +++ b/app/src/main/java/com/ouxuan/oxface/device/GateABController.java @@ -45,6 +45,9 @@ public class GateABController { private RelayController relayController; private Object udpController; // 预留UDP控制器接口 + // 485通信控制器 + private Ox485 ox485; + // 网络API服务 private PadApiService acControlApiService; @@ -108,6 +111,7 @@ public class GateABController { syncHandler = new Handler(Looper.getMainLooper()); executorService = Executors.newSingleThreadExecutor(); relayController = RelayController.getInstance(); + ox485 = Ox485.getInstance(); currentConfig = new GateABConfig(); // 初始化中控网络服务 @@ -154,6 +158,9 @@ public class GateABController { this.context = context; this.deviceDataManager = DeviceSelectDataManager.getInstance(context); + // 初始化485通信模块 + ox485.initialize(context); + // 启动配置同步 startConfigSync(); @@ -516,6 +523,12 @@ public class GateABController { // 更新当前配置 currentConfig = newConfig; + // 同步485配置到Ox485实例 + if (ox485 != null) { + ox485.setGateCamera485OxOn(newConfig.gateCamera485OXOn); + LogManager.logInfo(TAG, "485配置已同步: " + newConfig.gateCamera485OXOn); + } + if (configChanged) { LogManager.logInfo(TAG, "配置发生变化,已更新: " + currentConfig.toString()); logConfigurationChanges(oldConfig, newConfig); @@ -562,6 +575,10 @@ public class GateABController { relayController.release(); } + if (ox485 != null) { + ox485.release(); + } + LogManager.logInfo(TAG, "GateABController资源已释放"); } @@ -627,4 +644,105 @@ public class GateABController { LogManager.logInfo(TAG, "=================="); } + + // ==================== 485人数检测相关方法 ==================== + + /** + * 获取485摄像头人数 + * 对应uniapp中sendHex485ForPeopleNum方法 + * @param callback 结果回调 + */ + public void get485PeopleNum(PeopleNumCallback callback) { + LogManager.logInfo(TAG, "开始获取485摄像头人数"); + + if (ox485 == null) { + String errorMsg = "Ox485未初始化"; + LogManager.logError(TAG, errorMsg); + if (callback != null) { + syncHandler.post(() -> callback.onError(errorMsg)); + } + return; + } + + // 检查485模式是否已启用 + if (!currentConfig.gateCamera485OXOn) { + String errorMsg = "485模式未开启,请检查配置"; + LogManager.logWarning(TAG, errorMsg); + if (callback != null) { + syncHandler.post(() -> callback.onError(errorMsg)); + } + return; + } + + // 调用Ox485获取人数 + ox485.sendHex485ForPeopleNum(new Ox485.PeopleNumCallback() { + @Override + public void onSuccess(int peopleNum) { + LogManager.logInfo(TAG, "485人数获取成功: " + peopleNum + " 人"); + if (callback != null) { + callback.onSuccess(peopleNum); + } + } + + @Override + public void onError(String errorMessage) { + LogManager.logError(TAG, "485人数获取失败: " + errorMessage); + if (callback != null) { + callback.onError(errorMessage); + } + } + }); + } + + /** + * 异步获取485摄像头人数(使用CompletableFuture) + * @return CompletableFuture 人数结果 + */ + public java.util.concurrent.CompletableFuture get485PeopleNumAsync() { + LogManager.logInfo(TAG, "异步获取485摄像头人数"); + + java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); + + get485PeopleNum(new PeopleNumCallback() { + @Override + public void onSuccess(int peopleNum) { + future.complete(peopleNum); + } + + @Override + public void onError(String errorMessage) { + future.completeExceptionally(new RuntimeException(errorMessage)); + } + }); + + return future; + } + + /** + * 检查485模式是否已启用 + * @return true表示485模式已启用 + */ + public boolean is485ModeEnabled() { + return currentConfig != null && currentConfig.gateCamera485OXOn; + } + + /** + * 获取485状态信息 + * @return 485状态描述 + */ + public String get485Status() { + if (ox485 != null) { + return ox485.get485Status(); + } else { + return "Ox485未初始化"; + } + } + + /** + * 485人数检测回调接口 + */ + public interface PeopleNumCallback { + void onSuccess(int peopleNum); + void onError(String 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 new file mode 100644 index 0000000..3c6a541 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/device/Ox485.java @@ -0,0 +1,406 @@ +package com.ouxuan.oxface.device; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.kongqw.serialportlibrary.SerialPortManager; +import com.kongqw.serialportlibrary.listener.OnSerialPortDataListener; +import com.kongqw.serialportlibrary.utils.ByteUtils; +import com.ouxuan.oxface.utils.LogManager; + +import java.io.File; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * Ox485 串口通信模块 + * 基于kongqw/SerialPortLibrary实现485通信功能 + * 主要用于获取摄像头人数等485设备通信 + * + * @author AI Assistant + * @version 1.0 + * @date 2024/09/12 + */ +public class Ox485 { + + private static final String TAG = "Ox485"; + + // 485通信配置 + private static final String DEFAULT_SERIAL_PORT_PATH = "/dev/ttyS6"; + private static final int DEFAULT_BAUD_RATE = 9600; + private static final int DEFAULT_STOP_BITS = 1; + private static final String HEX_COMMAND_GET_PEOPLE_NUM = "0F030001000294E5"; + private static final long TIMEOUT_485_MILLIS = 10000; // 10秒超时 + + // 单例实例 + private static volatile Ox485 instance; + + // 485通信相关 + private SerialPortManager serialPortManager; + private Handler mainHandler; + private boolean gateCamera485OxOn = false; + private Context context; + + // 数据接收相关 + private long lastRecvTime = 0; + private Handler timeoutHandler; + private Runnable timeoutRunnable; + + /** + * 485通信回调接口 + */ + public interface PeopleNumCallback { + void onSuccess(int peopleNum); + void onError(String errorMessage); + } + + private Ox485() { + mainHandler = new Handler(Looper.getMainLooper()); + timeoutHandler = new Handler(Looper.getMainLooper()); + serialPortManager = new SerialPortManager(); + } + + /** + * 获取Ox485单例实例 + * @return Ox485实例 + */ + public static Ox485 getInstance() { + if (instance == null) { + synchronized (Ox485.class) { + if (instance == null) { + instance = new Ox485(); + } + } + } + return instance; + } + + /** + * 初始化Ox485模块 + * @param context 上下文 + */ + public void initialize(Context context) { + this.context = context; + LogManager.logInfo(TAG, "Ox485模块初始化完成"); + } + + /** + * 设置485模式开关 + * @param enabled 是否启用485模式 + */ + public void setGateCamera485OxOn(boolean enabled) { + this.gateCamera485OxOn = enabled; + LogManager.logInfo(TAG, "设置485模式开关: " + enabled); + } + + /** + * 获取485模式开关状态 + * @return true表示485模式已启用 + */ + public boolean isGateCamera485OxOn() { + return gateCamera485OxOn; + } + + /** + * 通过485获取摄像头人数 + * 对应uniapp中的sendHex485ForPeopleNum方法 + * @param callback 结果回调 + */ + public void sendHex485ForPeopleNum(PeopleNumCallback callback) { + LogManager.logInfo(TAG, "开始通过485获取摄像头人数"); + + if (!gateCamera485OxOn) { + String errorMsg = "485模式未开启"; + LogManager.logError(TAG, errorMsg); + if (callback != null) { + mainHandler.post(() -> callback.onError(errorMsg)); + } + return; + } + + try { + // 检查串口是否已经打开 + if (serialPortManager.isOpen()) { + LogManager.logInfo(TAG, "串口已打开,直接发送命令"); + sendCommandAndWaitResponse(callback); + } else { + LogManager.logInfo(TAG, "串口未打开,尝试打开串口"); + openSerialPortAndSend(callback); + } + } catch (Exception e) { + String errorMsg = "485通信异常: " + e.getMessage(); + LogManager.logError(TAG, errorMsg, e); + if (callback != null) { + mainHandler.post(() -> callback.onError(errorMsg)); + } + } + } + + /** + * 打开串口并发送命令 + * @param callback 结果回调 + */ + private void openSerialPortAndSend(PeopleNumCallback callback) { + try { + // 配置串口参数 + File serialPortFile = new File(DEFAULT_SERIAL_PORT_PATH); + boolean openResult = serialPortManager.openSerialPort(serialPortFile, DEFAULT_BAUD_RATE); + + LogManager.logInfo(TAG, "串口打开结果: " + openResult + ", 路径: " + DEFAULT_SERIAL_PORT_PATH + ", 波特率: " + DEFAULT_BAUD_RATE); + + if (!openResult) { + String errorMsg = "打开串口失败: " + DEFAULT_SERIAL_PORT_PATH; + LogManager.logError(TAG, errorMsg); + if (callback != null) { + mainHandler.post(() -> callback.onError(errorMsg)); + } + return; + } + + // 启动读写线程 + serialPortManager.startReadThread(485); + serialPortManager.startSendThread(); + + // 发送命令并等待响应 + sendCommandAndWaitResponse(callback); + + } catch (Exception e) { + String errorMsg = "打开串口异常: " + e.getMessage(); + LogManager.logError(TAG, errorMsg, e); + if (callback != null) { + mainHandler.post(() -> callback.onError(errorMsg)); + } + } + } + + /** + * 发送命令并等待响应 + * @param callback 结果回调 + */ + private void sendCommandAndWaitResponse(PeopleNumCallback callback) { + // 设置数据监听器 + serialPortManager.setOnSerialPortDataListener(new OnSerialPortDataListener() { + @Override + public void onDataReceived(byte[] bytes) { + long timeInterval = (System.nanoTime() - lastRecvTime) / 1000000; + lastRecvTime = System.nanoTime(); + + String hexData = ByteUtils.bytesToHexString(bytes); + LogManager.logInfo(TAG, "接收到数据: " + hexData + ", 字节数组: " + java.util.Arrays.toString(bytes)); + + // 取消超时处理 + cancelTimeout(); + + // 解析人数数据 + int peopleNum = get485PeopleNum(bytes); + if (peopleNum >= 0) { + LogManager.logInfo(TAG, "485获取人数成功: " + peopleNum); + if (callback != null) { + mainHandler.post(() -> callback.onSuccess(peopleNum)); + } + } else { + String errorMsg = "485数据解析失败,数据: " + hexData; + LogManager.logError(TAG, errorMsg); + if (callback != null) { + mainHandler.post(() -> callback.onError(errorMsg)); + } + } + } + + @Override + public void onDataSent(byte[] bytes) { + String hexData = ByteUtils.bytesToHexString(bytes); + LogManager.logInfo(TAG, "发送数据: " + hexData); + } + }); + + // 发送HEX命令 + try { + byte[] hexBytes = hexStringToByteArray(HEX_COMMAND_GET_PEOPLE_NUM); + serialPortManager.sendBytes(hexBytes); + LogManager.logInfo(TAG, "发送485人数查询命令: " + HEX_COMMAND_GET_PEOPLE_NUM); + + // 设置超时处理 + setupTimeout(callback); + + } catch (Exception e) { + String errorMsg = "发送485命令异常: " + e.getMessage(); + LogManager.logError(TAG, errorMsg, e); + if (callback != null) { + mainHandler.post(() -> callback.onError(errorMsg)); + } + } + } + + /** + * 设置超时处理 + * @param callback 结果回调 + */ + private void setupTimeout(PeopleNumCallback callback) { + cancelTimeout(); // 先取消之前的超时任务 + + timeoutRunnable = () -> { + String errorMsg = "485人数查询超时"; + LogManager.logError(TAG, errorMsg); + if (callback != null) { + callback.onError(errorMsg); + } + }; + + timeoutHandler.postDelayed(timeoutRunnable, TIMEOUT_485_MILLIS); + } + + /** + * 取消超时处理 + */ + private void cancelTimeout() { + if (timeoutHandler != null && timeoutRunnable != null) { + timeoutHandler.removeCallbacks(timeoutRunnable); + timeoutRunnable = null; + } + } + + /** + * 解析485人数数据 + * 对应uniapp中的get485PeopleNum方法 + * @param arr 接收到的字节数组 + * @return 人数,-1表示解析失败 + */ + private int get485PeopleNum(byte[] arr) { + LogManager.logDebug(TAG, "解析485人数数据: " + java.util.Arrays.toString(arr)); + + // 数据校验 + if (arr == null) { + LogManager.logError(TAG, "485数据为空"); + return -1; + } + + if (arr.length != 9) { + LogManager.logError(TAG, "485数据长度错误,期望9字节,实际: " + arr.length + ", 数据: " + java.util.Arrays.toString(arr)); + return -1; + } + + // 检查第3个字节是否为4(表示正确响应) + if (arr[2] != 4) { + LogManager.logError(TAG, "485响应格式错误,第3字节应为4,实际: " + arr[2] + ", 数据: " + java.util.Arrays.toString(arr)); + return -1; + } + + // 提取第5个字节作为人数数据,使用位掩码确保为无符号整数 + int peopleNum = arr[4] & 0xFF; + LogManager.logInfo(TAG, "解析485人数成功: " + peopleNum); + + return peopleNum; + } + + /** + * 异步获取摄像头人数(使用CompletableFuture) + * @return CompletableFuture 人数结果 + */ + public CompletableFuture sendHex485ForPeopleNumAsync() { + LogManager.logInfo(TAG, "异步获取摄像头人数"); + + CompletableFuture future = new CompletableFuture<>(); + + sendHex485ForPeopleNum(new PeopleNumCallback() { + @Override + public void onSuccess(int peopleNum) { + future.complete(peopleNum); + } + + @Override + public void onError(String errorMessage) { + future.completeExceptionally(new RuntimeException(errorMessage)); + } + }); + + return future; + } + + /** + * 检查是否需要打开485串口 + */ + public void checkIsNeedOpen485() { + LogManager.logInfo(TAG, "检查是否需要打开485串口"); + if (!serialPortManager.isOpen()) { + LogManager.logInfo(TAG, "串口未打开,需要打开"); + } else { + LogManager.logInfo(TAG, "串口已打开"); + } + } + + /** + * 发送自定义485 HEX命令 + * @param hexCommand HEX命令字符串 + */ + public void send485HexCommand(String hexCommand) { + LogManager.logInfo(TAG, "发送485 HEX命令: " + hexCommand); + try { + if (!serialPortManager.isOpen()) { + LogManager.logError(TAG, "串口未打开,无法发送命令"); + return; + } + + byte[] hexBytes = hexStringToByteArray(hexCommand); + serialPortManager.sendBytes(hexBytes); + LogManager.logInfo(TAG, "485命令发送完成"); + } catch (Exception e) { + LogManager.logError(TAG, "发送485命令异常: " + e.getMessage(), e); + } + } + + /** + * 获取485串口状态 + * @return 串口状态描述 + */ + public String get485Status() { + boolean isOpen = serialPortManager.isOpen(); + String status = "串口状态: " + (isOpen ? "已打开" : "已关闭") + + ", 路径: " + DEFAULT_SERIAL_PORT_PATH + + ", 波特率: " + DEFAULT_BAUD_RATE + + ", 485模式: " + (gateCamera485OxOn ? "已启用" : "未启用"); + LogManager.logInfo(TAG, status); + return status; + } + + /** + * 关闭485串口 + */ + public void close485SerialPort() { + LogManager.logInfo(TAG, "关闭485串口"); + try { + cancelTimeout(); + if (serialPortManager != null) { + serialPortManager.close(); + LogManager.logInfo(TAG, "485串口已关闭"); + } + } catch (Exception e) { + LogManager.logError(TAG, "关闭485串口异常: " + e.getMessage(), e); + } + } + + /** + * 释放资源 + */ + public void release() { + LogManager.logInfo(TAG, "释放Ox485资源"); + close485SerialPort(); + } + + /** + * HEX字符串转字节数组 + * @param hexString HEX字符串 + * @return 字节数组 + */ + private byte[] hexStringToByteArray(String hexString) { + int len = hexString.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + + Character.digit(hexString.charAt(i + 1), 16)); + } + return data; + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_debug.xml b/app/src/main/res/layout/activity_debug.xml index bf7970a..33e2db0 100644 --- a/app/src/main/res/layout/activity_debug.xml +++ b/app/src/main/res/layout/activity_debug.xml @@ -239,13 +239,14 @@ android:layout_marginEnd="4dp" android:textSize="12sp" /> -