package com.ouxuan.oxface.device; import android.content.Context; import android.os.Handler; import android.os.Looper; import android.util.Log; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** * 门禁UDP通信控制类 * 用于控制AB门的开关状态查询和操作 * * 功能特性: * - 支持设备信息查询和更新 * - 支持门禁状态轮询 * - 支持A门、B门开门控制 * - 支持门关闭时间设置 * * @author AI Assistant * @version 1.0 */ public class OxUDP { private static final String TAG = "OxUDP"; // UDP配置参数 private static final String UDP_IP = "192.168.1.123"; // MJ AB中控-所有设备通用 private static final int UDP_SEND_PORT = 60000; // 发送端口 private static final int POLLING_INTERVAL = 5000; // 轮询间隔(毫秒) private static final int UDP_TIMEOUT = 3000; // UDP超时时间(毫秒) // UDP命令定义 private String udp_device_cmd = "17 94 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; private String udp_state_cmd = "17 20 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; private String udp_open_a_cmd = "17 40 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; private String udp_open_b_cmd = "17 40 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; private String udp_gate_a_close_time_cmd = "17 80 00 00 3E 4F 18 0F 01 03 04 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"; // 状态变量 private String udp_device_res = ""; // 设备返回的数据 private int udp_gate_a_close_time = 2; // A门关闭时间(秒) private boolean isInitialized = false; private boolean isPolling = false; // 线程管理 private ExecutorService executorService; private ScheduledExecutorService scheduledExecutor; private ScheduledFuture pollingTask; private Handler mainHandler; // 单例实例 private static OxUDP instance; private Context context; // 回调接口 private UDPStateListener stateListener; private UDPDeviceListener deviceListener; /** * UDP状态监听接口 */ public interface UDPStateListener { /** * 门禁状态更新 * @param gateAState A门状态 (true: 开启, false: 关闭) * @param gateBState B门状态 (true: 开启, false: 关闭) * @param rawData 原始返回数据 */ void onGateStateUpdate(boolean gateAState, boolean gateBState, String rawData); /** * 门开启成功回调 * @param gateType 门类型 ("A" 或 "B") * @param success 是否成功 */ void onGateOpenResult(String gateType, boolean success); /** * UDP通信错误 * @param error 错误信息 */ void onUDPError(String error); } /** * UDP设备监听接口 */ public interface UDPDeviceListener { /** * 设备信息更新 * @param deviceInfo 设备信息 */ void onDeviceInfoUpdate(String deviceInfo); /** * 设备连接状态变化 * @param connected 是否连接 */ void onDeviceConnectionChange(boolean connected); } private OxUDP() { executorService = Executors.newCachedThreadPool(); scheduledExecutor = Executors.newSingleThreadScheduledExecutor(); mainHandler = new Handler(Looper.getMainLooper()); } /** * 获取单例实例 */ public static synchronized OxUDP getInstance() { if (instance == null) { instance = new OxUDP(); } return instance; } /** * 初始化UDP连接 * @param context 应用上下文 */ public void initUDP(Context context) { this.context = context.getApplicationContext(); Log.i(TAG, "开始初始化UDP连接..."); executorService.execute(() -> { try { // 更新设备信息 updateDeviceUDP(); isInitialized = true; Log.i(TAG, "UDP初始化成功"); notifyDeviceConnectionChange(true); } catch (Exception e) { Log.e(TAG, "UDP初始化失败", e); notifyUDPError("UDP初始化失败: " + e.getMessage()); isInitialized = false; notifyDeviceConnectionChange(false); } }); } /** * 更新当前连接UDP的设备信息到命令中 */ private void updateDeviceUDP() { Log.d(TAG, "开始更新设备UDP信息..."); try { // 发送设备查询命令 String response = sendUDPCommand(udp_device_cmd); if (response != null && !response.isEmpty()) { Log.d(TAG, "updateDeviceUDP 响应: " + response); // 更新设备返回的数据 udp_device_res = response; // 更新各种命令的设备信息 udp_state_cmd = updateCmdDeviceInfo(response, udp_state_cmd); udp_open_a_cmd = updateCmdDeviceInfo(response, udp_open_a_cmd); udp_open_b_cmd = updateCmdDeviceInfo(response, udp_open_b_cmd); udp_gate_a_close_time_cmd = updateCmdDeviceInfo(response, udp_gate_a_close_time_cmd); Log.i(TAG, "设备信息更新成功"); notifyDeviceInfoUpdate(response); } else { Log.w(TAG, "设备返回数据为空"); notifyUDPError("设备返回数据为空"); } } catch (Exception e) { Log.e(TAG, "更新设备UDP信息失败", e); notifyUDPError("更新设备信息失败: " + e.getMessage()); } } /** * 更新命令中的设备信息 * @param deviceRes 设备返回的数据 * @param cmd 要更新的命令 * @return 更新后的命令 */ private String updateCmdDeviceInfo(String deviceRes, String cmd) { try { String[] resDeviceArr = getCmdArr(deviceRes); String[] cmdArr = getCmdArr(cmd); // 替换cmd中的4-7位(索引3-6) for (int i = 4; i <= 7; i++) { if (i < resDeviceArr.length && i < cmdArr.length) { cmdArr[i] = resDeviceArr[i]; } } // 重新组合命令 StringBuilder result = new StringBuilder(); for (int i = 0; i < cmdArr.length; i++) { if (i > 0) result.append(" "); result.append(cmdArr[i]); } String updatedCmd = result.toString(); Log.d(TAG, "updateCmdDeviceInfo: " + updatedCmd); return updatedCmd; } catch (Exception e) { Log.e(TAG, "更新命令设备信息失败", e); return cmd; // 返回原命令 } } /** * 将命令字符串转换为数组 * @param cmd 命令字符串(如 "17 94 00 00 ...") * @return 命令数组 */ private String[] getCmdArr(String cmd) { return cmd.trim().split("\\s+"); } /** * 将命令字符串转换为字节数组 * @param cmd 命令字符串(如 "17 94 00 00 ...") * @return 字节数组 */ private byte[] getCmdBytes(String cmd) { String[] hexStrings = getCmdArr(cmd); byte[] bytes = new byte[hexStrings.length]; for (int i = 0; i < hexStrings.length; i++) { try { bytes[i] = (byte) Integer.parseInt(hexStrings[i], 16); } catch (NumberFormatException e) { Log.e(TAG, "解析十六进制字符串失败: " + hexStrings[i], e); bytes[i] = 0; } } return bytes; } /** * 将字节数组转换为十六进制字符串 * @param bytes 字节数组 * @return 十六进制字符串(如 "17 94 00 00 ...") */ private String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { if (i > 0) sb.append(" "); sb.append(String.format("%02X", bytes[i] & 0xFF)); } return sb.toString(); } /** * 发送UDP命令并获取响应 * @param command 要发送的命令 * @return 响应数据 */ private String sendUDPCommand(String command) { DatagramSocket socket = null; try { // 创建UDP套接字 socket = new DatagramSocket(); socket.setSoTimeout(UDP_TIMEOUT); // 准备发送数据 byte[] sendData = getCmdBytes(command); InetAddress serverAddress = InetAddress.getByName(UDP_IP); DatagramPacket sendPacket = new DatagramPacket( sendData, sendData.length, serverAddress, UDP_SEND_PORT); // Log.d(TAG, "发送UDP命令到 " + UDP_IP + ":" + UDP_SEND_PORT + " - " + command); // 发送数据 socket.send(sendPacket); // 接收响应 byte[] receiveData = new byte[1024]; DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length); socket.receive(receivePacket); // 解析响应 byte[] responseBytes = new byte[receivePacket.getLength()]; System.arraycopy(receiveData, 0, responseBytes, 0, receivePacket.getLength()); String response = bytesToHexString(responseBytes); // Log.d(TAG, "收到UDP响应: " + response); return response; } catch (Exception e) { Log.e(TAG, "发送UDP命令失败: " + command, e); return null; } finally { if (socket != null && !socket.isClosed()) { socket.close(); } } } /** * 开始门禁状态轮询 */ public void startPolling() { if (!isInitialized) { Log.w(TAG, "UDP未初始化,无法开始轮询"); return; } if (isPolling) { Log.w(TAG, "轮询已在进行中"); return; } Log.i(TAG, "开始门禁状态轮询,间隔: " + POLLING_INTERVAL + "ms"); isPolling = true; pollingTask = scheduledExecutor.scheduleWithFixedDelay(() -> { try { queryGateState(); } catch (Exception e) { Log.e(TAG, "轮询过程中发生错误", e); } }, 0, POLLING_INTERVAL, TimeUnit.MILLISECONDS); } /** * 停止门禁状态轮询 */ public void stopPolling() { if (!isPolling) { return; } Log.i(TAG, "停止门禁状态轮询"); isPolling = false; if (pollingTask != null && !pollingTask.isCancelled()) { pollingTask.cancel(true); pollingTask = null; } } /** * 查询门禁状态 */ public void queryGateState() { try { String response = sendUDPCommand(udp_state_cmd); if (response != null && !response.isEmpty()) { // 解析门禁状态 parseGateState(response); } else { Log.w(TAG, "查询门禁状态失败,响应为空"); } } catch (Exception e) { Log.e(TAG, "查询门禁状态失败", e); notifyUDPError("查询门禁状态失败: " + e.getMessage()); } } /** * 解析门禁状态 * @param response 响应数据 */ private void parseGateState(String response) { try { String[] responseArr = getCmdArr(response); // 检查响应数据长度,确保包含门禁状态信息 if (responseArr.length < 30) { Log.w(TAG, "响应数据长度不足,无法解析门禁状态。长度: " + responseArr.length); notifyUDPError("响应数据长度不足: " + responseArr.length); return; } // 根据协议解析门禁状态 // 第28位表示A门状态(数组索引28),第29位表示B门状态(数组索引29) boolean gateAState = false; boolean gateBState = false; // 解析A门状态(第28位,数组索引28) if (responseArr.length > 28) { String aStateValue = responseArr[28]; gateAState = "01".equals(aStateValue); // 01表示开启,00表示关闭 Log.d(TAG, "A门状态值: " + aStateValue + " -> " + (gateAState ? "开启" : "关闭")); } // 解析B门状态(第29位,数组索引29) if (responseArr.length > 29) { String bStateValue = responseArr[29]; gateBState = "01".equals(bStateValue); // 01表示开启,00表示关闭 Log.d(TAG, "B门状态值: " + bStateValue + " -> " + (gateBState ? "开启" : "关闭")); } // 记录完整的响应数据用于调试 // Log.d(TAG, "完整UDP响应: " + response); // Log.d(TAG, "响应数组长度: " + responseArr.length); if (responseArr.length > 28) { // Log.d(TAG, "第28位(A门): " + responseArr[28]); } if (responseArr.length > 29) { // Log.d(TAG, "第29位(B门): " + responseArr[29]); } // Log.d(TAG, "门禁状态 - A门: " + (gateAState ? "开启" : "关闭") + // ", B门: " + (gateBState ? "开启" : "关闭")); notifyGateStateUpdate(gateAState, gateBState, response); } catch (Exception e) { Log.e(TAG, "解析门禁状态失败", e); notifyUDPError("解析门禁状态失败: " + e.getMessage()); } } /** * 打开A门(进门) */ public void openGateA() { Log.i(TAG, "执行开启A门命令"); executorService.execute(() -> { try { String response = sendUDPCommand(udp_open_a_cmd); boolean success = response != null && !response.isEmpty(); Log.i(TAG, "开启A门结果: " + (success ? "成功" : "失败")); notifyGateOpenResult("A", success); } catch (Exception e) { Log.e(TAG, "开启A门失败", e); notifyGateOpenResult("A", false); notifyUDPError("开启A门失败: " + e.getMessage()); } }); } /** * 打开B门(出门) */ public void openGateB() { Log.i(TAG, "执行开启B门命令"); executorService.execute(() -> { try { String response = sendUDPCommand(udp_open_b_cmd); boolean success = response != null && !response.isEmpty(); Log.i(TAG, "开启B门结果: " + (success ? "成功" : "失败")); notifyGateOpenResult("B", success); } catch (Exception e) { Log.e(TAG, "开启B门失败", e); notifyGateOpenResult("B", false); notifyUDPError("开启B门失败: " + e.getMessage()); } }); } /** * 设置A门关闭时间 * @param seconds 关闭时间(秒) */ public void setGateACloseTime(int seconds) { this.udp_gate_a_close_time = seconds; Log.i(TAG, "设置A门关闭时间: " + seconds + "秒"); executorService.execute(() -> { try { // 更新命令中的时间参数(需要根据实际协议调整) // 这里假设第10位是时间参数 String[] cmdArr = getCmdArr(udp_gate_a_close_time_cmd); if (cmdArr.length > 10) { cmdArr[10] = String.format("%02X", seconds); StringBuilder sb = new StringBuilder(); for (int i = 0; i < cmdArr.length; i++) { if (i > 0) sb.append(" "); sb.append(cmdArr[i]); } udp_gate_a_close_time_cmd = sb.toString(); } String response = sendUDPCommand(udp_gate_a_close_time_cmd); boolean success = response != null && !response.isEmpty(); Log.i(TAG, "设置A门关闭时间结果: " + (success ? "成功" : "失败")); } catch (Exception e) { Log.e(TAG, "设置A门关闭时间失败", e); notifyUDPError("设置A门关闭时间失败: " + e.getMessage()); } }); } /** * 设置状态监听器 */ public void setStateListener(UDPStateListener listener) { this.stateListener = listener; } /** * 设置设备监听器 */ public void setDeviceListener(UDPDeviceListener listener) { this.deviceListener = listener; } /** * 获取初始化状态 */ public boolean isInitialized() { return isInitialized; } /** * 获取轮询状态 */ public boolean isPolling() { return isPolling; } /** * 获取设备返回数据 */ public String getDeviceResponse() { return udp_device_res; } /** * 获取A门关闭时间 */ public int getGateACloseTime() { return udp_gate_a_close_time; } /** * 停止UDP服务 */ public void stop() { Log.i(TAG, "停止UDP服务"); stopPolling(); isInitialized = false; notifyDeviceConnectionChange(false); } /** * 释放资源 */ public void release() { Log.i(TAG, "释放UDP资源"); stop(); if (executorService != null && !executorService.isShutdown()) { executorService.shutdown(); } if (scheduledExecutor != null && !scheduledExecutor.isShutdown()) { scheduledExecutor.shutdown(); } stateListener = null; deviceListener = null; } // 通知方法 private void notifyGateStateUpdate(boolean gateAState, boolean gateBState, String rawData) { if (stateListener != null) { mainHandler.post(() -> stateListener.onGateStateUpdate(gateAState, gateBState, rawData)); } } private void notifyGateOpenResult(String gateType, boolean success) { if (stateListener != null) { mainHandler.post(() -> stateListener.onGateOpenResult(gateType, success)); } } private void notifyUDPError(String error) { if (stateListener != null) { mainHandler.post(() -> stateListener.onUDPError(error)); } } private void notifyDeviceInfoUpdate(String deviceInfo) { if (deviceListener != null) { mainHandler.post(() -> deviceListener.onDeviceInfoUpdate(deviceInfo)); } } private void notifyDeviceConnectionChange(boolean connected) { if (deviceListener != null) { mainHandler.post(() -> deviceListener.onDeviceConnectionChange(connected)); } } }