oxFaceAndroid
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

618 lines
21 KiB

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));
}
}
}