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.
916 lines
34 KiB
916 lines
34 KiB
package com.ouxuan.oxface.device;
|
|
|
|
import android.content.Context;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.util.Log;
|
|
|
|
import com.ouxuan.oxface.utils.LogManager;
|
|
import com.tencent.iot.hub.device.android.core.gateway.TXGatewayConnection;
|
|
import com.tencent.iot.hub.device.java.core.common.Status;
|
|
import com.tencent.iot.hub.device.java.core.log.TXMqttLogCallBack;
|
|
import com.tencent.iot.hub.device.java.core.mqtt.TXMqttActionCallBack;
|
|
import com.tencent.iot.hub.device.java.core.dynreg.TXMqttDynreg;
|
|
import com.tencent.iot.hub.device.java.core.dynreg.TXMqttDynregCallback;
|
|
|
|
import org.eclipse.paho.client.mqttv3.DisconnectedBufferOptions;
|
|
import org.eclipse.paho.client.mqttv3.IMqttToken;
|
|
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
|
import org.eclipse.paho.client.mqttv3.MqttMessage;
|
|
import org.json.JSONObject;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Date;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.ExecutorService;
|
|
import java.util.concurrent.Executors;
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
|
|
/**
|
|
* 腾讯云MQTT管理器 - 基于TXGatewayConnection实现
|
|
* 负责MQTT连接管理、自动重连、消息订阅和发布
|
|
*
|
|
* 功能特性:
|
|
* 1. 初始化后自动连接
|
|
* 2. 网络断线自动重连机制
|
|
* 3. 消息订阅和处理
|
|
* 4. 连接状态监控和上报
|
|
* 5. 门闸控制和设备重启命令处理
|
|
*
|
|
* @author AI Assistant
|
|
* @version 4.0
|
|
* @date 2024/09/16
|
|
*/
|
|
public class MqttManager {
|
|
|
|
private static final String TAG = "MqttManager";
|
|
|
|
// 单例实例
|
|
private static volatile MqttManager instance;
|
|
|
|
// 腾讯云IoT参数配置
|
|
private static final String PRODUCT_ID = "WZX68L5I75";
|
|
private static final String PRODUCT_KEY = "qr3rximCZnT6ZU0NsAAiTC7O"; // 动态注册用的ProductKey
|
|
|
|
// MQTT连接配置 - 根据腾讯云IoT官方文档配置
|
|
// MQTTS(8883): ${productid}.iotcloud.tencentdevices.com
|
|
private static final String BROKER_URL = "ssl://" + PRODUCT_ID + ".iotcloud.tencentdevices.com:8883"; // 腾讯云IoT Hub SSL地址
|
|
private static final int KEEP_ALIVE_INTERVAL = 240; // 增加心跳间隔
|
|
private static final int CONNECTION_TIMEOUT = 30; // 增加连接超时时间
|
|
private static final int QOS = 1;
|
|
|
|
// 重连配置
|
|
private static final int MAX_RECONNECT_ATTEMPTS = 5;
|
|
private static final long RECONNECT_DELAY = 5000;
|
|
private static final long HEALTH_CHECK_INTERVAL = 30000;
|
|
|
|
// 上下文和状态管理
|
|
private Context context;
|
|
private TXGatewayConnection mqttConnection;
|
|
private String deviceName;
|
|
private String subscribeTopic;
|
|
private String path2Store;
|
|
private String dynamicDevicePSK; // 动态注册获取的设备PSK
|
|
private boolean isDynamicRegisterCompleted = false; // 动态注册是否完成
|
|
|
|
// 连接状态管理
|
|
private boolean isConnected = false;
|
|
private boolean isConnecting = false;
|
|
private int reconnectAttempts = 0;
|
|
private Handler mainHandler;
|
|
private ExecutorService executorService;
|
|
private ScheduledExecutorService scheduledExecutor;
|
|
|
|
// 请求ID生成器
|
|
private static AtomicInteger requestID = new AtomicInteger(0);
|
|
|
|
// 监听器接口
|
|
private ConnectionStatusListener connectionStatusListener;
|
|
private MessageReceivedListener messageReceivedListener;
|
|
|
|
// 外部依赖
|
|
private GateABController gateABController;
|
|
|
|
/**
|
|
* MQTT连接状态监听器
|
|
*/
|
|
public interface ConnectionStatusListener {
|
|
void onConnected();
|
|
void onConnectionFailed(String reason);
|
|
void onConnectionLost(String reason);
|
|
void onReconnecting(int attempt);
|
|
}
|
|
|
|
/**
|
|
* MQTT消息接收监听器
|
|
*/
|
|
public interface MessageReceivedListener {
|
|
void onGateCommandReceived(String gateCommand);
|
|
void onRebootCommandReceived();
|
|
void onLogLevelQueryReceived();
|
|
void onOtherMessageReceived(String topic, String message);
|
|
}
|
|
|
|
/**
|
|
* MQTT请求上下文
|
|
*/
|
|
private static class MQTTRequest {
|
|
public String requestType;
|
|
public int requestId;
|
|
|
|
public MQTTRequest(String requestType, int requestId) {
|
|
this.requestType = requestType;
|
|
this.requestId = requestId;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "MQTTRequest{requestType='" + requestType + "', requestId=" + requestId + "}";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取单例实例
|
|
*/
|
|
public static MqttManager getInstance() {
|
|
if (instance == null) {
|
|
synchronized (MqttManager.class) {
|
|
if (instance == null) {
|
|
instance = new MqttManager();
|
|
}
|
|
}
|
|
}
|
|
return instance;
|
|
}
|
|
|
|
/**
|
|
* 私有构造函数
|
|
*/
|
|
private MqttManager() {
|
|
mainHandler = new Handler(Looper.getMainLooper());
|
|
executorService = Executors.newCachedThreadPool();
|
|
scheduledExecutor = Executors.newScheduledThreadPool(2);
|
|
}
|
|
|
|
/**
|
|
* 初始化MQTT管理器
|
|
*/
|
|
public void initialize(Context context) {
|
|
this.context = context.getApplicationContext();
|
|
this.gateABController = GateABController.getInstance();
|
|
this.path2Store = context.getCacheDir().getAbsolutePath();
|
|
|
|
// 生成设备名称
|
|
generateDeviceName();
|
|
|
|
// 构建MQTT配置
|
|
buildMqttConfig();
|
|
|
|
LogManager.logInfo(TAG, "MQTT管理器初始化完成");
|
|
LogManager.logInfo(TAG, "设备名称: " + deviceName);
|
|
LogManager.logInfo(TAG, "订阅主题: " + subscribeTopic);
|
|
|
|
// 启动健康检查
|
|
startHealthCheck();
|
|
|
|
// 自动连接
|
|
connectAsync();
|
|
}
|
|
|
|
/**
|
|
* 生成设备名称
|
|
*/
|
|
private void generateDeviceName() {
|
|
String androidId = DeviceUtils.getAndroidID(context);
|
|
deviceName = "PadV6" + androidId;
|
|
LogManager.logInfo(TAG, "生成设备名称: " + deviceName);
|
|
}
|
|
|
|
/**
|
|
* 构建MQTT配置
|
|
*/
|
|
private void buildMqttConfig() {
|
|
subscribeTopic = PRODUCT_ID + "/" + deviceName + "/control";
|
|
LogManager.logInfo(TAG, "MQTT配置构建完成");
|
|
}
|
|
|
|
/**
|
|
* 开始动态注册
|
|
*/
|
|
private void startDynamicRegister() {
|
|
LogManager.logInfo(TAG, "开始动态注册 MQTT 设备...");
|
|
LogManager.logInfo(TAG, "ProductID: " + PRODUCT_ID);
|
|
LogManager.logInfo(TAG, "ProductKey: " + PRODUCT_KEY);
|
|
LogManager.logInfo(TAG, "DeviceName: " + deviceName);
|
|
|
|
try {
|
|
TXMqttDynreg dynreg = new TXMqttDynreg(PRODUCT_ID, PRODUCT_KEY, deviceName, new DynamicRegisterCallback());
|
|
dynreg.doDynamicRegister(); // 调起动态注册
|
|
LogManager.logInfo(TAG, "MQTT动态注册请求已发送");
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "动态注册失败", e);
|
|
scheduleReconnect();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 动态注册回调处理器
|
|
*/
|
|
private class DynamicRegisterCallback extends TXMqttDynregCallback {
|
|
|
|
@Override
|
|
public void onGetDevicePSK(String devicePsk) {
|
|
LogManager.logInfo(TAG, "动态注册成功,获取到设备PSK: " + devicePsk.substring(0, Math.min(8, devicePsk.length())) + "...");
|
|
|
|
// 保存动态获取的PSK
|
|
dynamicDevicePSK = devicePsk;
|
|
isDynamicRegisterCompleted = true;
|
|
|
|
// 使用获取到的PSK连接MQTT
|
|
try {
|
|
connectMqtt();
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "动态注册成功后连接MQTT失败", e);
|
|
scheduleReconnect();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onGetDeviceCert(String deviceCert, String devicePriv) {
|
|
LogManager.logInfo(TAG, "动态注册成功,获取到设备证书认证信息");
|
|
// 本项目使用PSK认证,不使用证书认证
|
|
}
|
|
|
|
@Override
|
|
public void onFailedDynreg(Throwable cause, String errMsg) {
|
|
LogManager.logError(TAG, "动态注册失败: " + errMsg, cause);
|
|
|
|
// 通知连接失败
|
|
if (connectionStatusListener != null) {
|
|
mainHandler.post(() -> connectionStatusListener.onConnectionFailed("动态注册失败: " + errMsg));
|
|
}
|
|
|
|
// 调度重连(包括重新动态注册)
|
|
scheduleReconnect();
|
|
}
|
|
|
|
@Override
|
|
public void onFailedDynreg(Throwable cause) {
|
|
String errorMsg = cause != null ? cause.getMessage() : "未知错误";
|
|
LogManager.logError(TAG, "动态注册失败: " + errorMsg, cause);
|
|
|
|
// 通知连接失败
|
|
if (connectionStatusListener != null) {
|
|
mainHandler.post(() -> connectionStatusListener.onConnectionFailed("动态注册失败: " + errorMsg));
|
|
}
|
|
|
|
// 调度重连(包括重新动态注册)
|
|
scheduleReconnect();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 开始动态注册和连接流程
|
|
*/
|
|
public void connectAsync() {
|
|
if (isConnecting || isConnected) {
|
|
LogManager.logInfo(TAG, "MQTT正在连接或已连接,跳过连接请求");
|
|
return;
|
|
}
|
|
|
|
executorService.execute(() -> {
|
|
try {
|
|
if (!isDynamicRegisterCompleted) {
|
|
// 先进行动态注册
|
|
startDynamicRegister();
|
|
} else {
|
|
// 已有PSK,直接连接MQTT
|
|
connectMqtt();
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "异步连接MQTT失败", e);
|
|
scheduleReconnect();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 连接MQTT服务器(使用动态注册获取的PSK)
|
|
*/
|
|
private void connectMqtt() {
|
|
LogManager.logInfo(TAG, "开始连接MQTT服务器...");
|
|
LogManager.logInfo(TAG, "连接地址: " + BROKER_URL);
|
|
LogManager.logInfo(TAG, "产品ID: " + PRODUCT_ID);
|
|
LogManager.logInfo(TAG, "设备名称: " + deviceName);
|
|
|
|
if (dynamicDevicePSK == null || dynamicDevicePSK.isEmpty()) {
|
|
LogManager.logError(TAG, "动态注册获取的PSK为空,无法连接MQTT");
|
|
scheduleReconnect();
|
|
return;
|
|
}
|
|
|
|
isConnecting = true;
|
|
|
|
try {
|
|
// 创建TXGatewayConnection实例,使用动态注册获取的PSK
|
|
mqttConnection = new TXGatewayConnection(
|
|
context,
|
|
BROKER_URL,
|
|
PRODUCT_ID,
|
|
deviceName,
|
|
dynamicDevicePSK, // 使用动态注册获取的PSK
|
|
null, // devCert
|
|
null, // devPriv
|
|
true, // 启用MQTT日志以便调试
|
|
new MqttLogCallback(), // 添加日志回调
|
|
new MqttActionCallback()
|
|
);
|
|
|
|
// 配置连接选项
|
|
MqttConnectOptions options = new MqttConnectOptions();
|
|
options.setConnectionTimeout(CONNECTION_TIMEOUT);
|
|
options.setKeepAliveInterval(KEEP_ALIVE_INTERVAL);
|
|
options.setAutomaticReconnect(false); // 手动控制重连
|
|
options.setCleanSession(true); // 改为true,避免会话问题
|
|
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1); // 明确指定MQTT版本
|
|
|
|
LogManager.logInfo(TAG, "使用动态注册PSK认证连接腾讯云IoT平台");
|
|
LogManager.logInfo(TAG, "连接超时: " + CONNECTION_TIMEOUT + "秒");
|
|
LogManager.logInfo(TAG, "心跳间隔: " + KEEP_ALIVE_INTERVAL + "秒");
|
|
|
|
// 连接MQTT
|
|
MQTTRequest mqttRequest = new MQTTRequest("connect", requestID.getAndIncrement());
|
|
mqttConnection.connect(options, mqttRequest);
|
|
|
|
LogManager.logInfo(TAG, "MQTT连接请求已发送");
|
|
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "创建MQTT连接失败", e);
|
|
isConnecting = false;
|
|
scheduleReconnect();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 订阅主题
|
|
*/
|
|
private void subscribeToTopic() {
|
|
if (mqttConnection != null && isConnected) {
|
|
try {
|
|
MQTTRequest mqttRequest = new MQTTRequest("subscribe", requestID.getAndIncrement());
|
|
mqttConnection.subscribe(subscribeTopic, QOS, mqttRequest);
|
|
LogManager.logInfo(TAG, "开始订阅主题: " + subscribeTopic);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "订阅主题异常", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 配置断线缓冲
|
|
*/
|
|
private void configureDisconnectedBuffer() {
|
|
try {
|
|
DisconnectedBufferOptions bufferOptions = new DisconnectedBufferOptions();
|
|
bufferOptions.setBufferEnabled(true);
|
|
bufferOptions.setBufferSize(1024);
|
|
bufferOptions.setDeleteOldestMessages(true);
|
|
bufferOptions.setPersistBuffer(true);
|
|
mqttConnection.setBufferOpts(bufferOptions);
|
|
LogManager.logInfo(TAG, "断线缓冲配置完成");
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "配置断线缓冲失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 调度重连(包括重新动态注册)
|
|
*/
|
|
private void scheduleReconnect() {
|
|
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
|
|
LogManager.logError(TAG, "已达到最大重连次数,停止重连");
|
|
LogManager.logError(TAG, "请检查网络连接和腾讯云IoT设备配置");
|
|
return;
|
|
}
|
|
|
|
reconnectAttempts++;
|
|
// 渐进式延迟:5s, 10s, 20s, 40s, 80s
|
|
long delay = RECONNECT_DELAY * (1L << (reconnectAttempts - 1));
|
|
|
|
LogManager.logInfo(TAG, "调度第" + reconnectAttempts + "次重连(包括动态注册),延迟" + delay + "ms");
|
|
LogManager.logInfo(TAG, "重连原因: 代理程序不可用或动态注册失败,可能是网络问题或认证失败");
|
|
|
|
if (connectionStatusListener != null) {
|
|
mainHandler.post(() -> connectionStatusListener.onReconnecting(reconnectAttempts));
|
|
}
|
|
|
|
scheduledExecutor.schedule(() -> {
|
|
if (!isConnected) {
|
|
LogManager.logInfo(TAG, "执行第" + reconnectAttempts + "次重连(包括重新动态注册)");
|
|
// 清理旧连接和动态注册状态
|
|
if (mqttConnection != null) {
|
|
try {
|
|
mqttConnection = null;
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "清理旧连接失败", e);
|
|
}
|
|
}
|
|
// 重置动态注册状态,重新执行动态注册流程
|
|
isDynamicRegisterCompleted = false;
|
|
dynamicDevicePSK = null;
|
|
connectAsync();
|
|
}
|
|
}, delay, TimeUnit.MILLISECONDS);
|
|
}
|
|
|
|
/**
|
|
* 启动健康检查
|
|
*/
|
|
private void startHealthCheck() {
|
|
scheduledExecutor.scheduleWithFixedDelay(() -> {
|
|
try {
|
|
checkConnectionHealth();
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "健康检查异常", e);
|
|
}
|
|
}, HEALTH_CHECK_INTERVAL, HEALTH_CHECK_INTERVAL, TimeUnit.MILLISECONDS);
|
|
|
|
LogManager.logInfo(TAG, "MQTT健康检查已启动");
|
|
}
|
|
|
|
/**
|
|
* 检查连接健康状态
|
|
*/
|
|
private void checkConnectionHealth() {
|
|
if (mqttConnection != null) {
|
|
// 检查连接状态
|
|
if (!isConnected && !isConnecting) {
|
|
LogManager.logInfo(TAG, "健康检查:连接已断开,尝试重连");
|
|
connectAsync();
|
|
}
|
|
} else if (!isConnecting) {
|
|
LogManager.logInfo(TAG, "健康检查:客户端为空,尝试重连");
|
|
connectAsync();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MQTT日志回调处理器
|
|
*/
|
|
private class MqttLogCallback extends TXMqttLogCallBack {
|
|
@Override
|
|
public String setSecretKey() {
|
|
return null; // 不需要加密
|
|
}
|
|
|
|
@Override
|
|
public void printDebug(String message) {
|
|
LogManager.logDebug("TXMQTT", message);
|
|
}
|
|
|
|
@Override
|
|
public boolean saveLogOffline(String log) {
|
|
// 简化实现,返回false表示不支持离线日志保存
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public String readOfflineLog() {
|
|
// 简化实现,返回null表示没有离线日志
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean delOfflineLog() {
|
|
// 简化实现,返回true表示删除成功
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* MQTT动作回调处理器
|
|
*/
|
|
private class MqttActionCallback extends TXMqttActionCallBack {
|
|
|
|
@Override
|
|
public void onConnectCompleted(Status status, boolean reconnect, Object userContext, String msg, Throwable cause) {
|
|
String userContextInfo = "";
|
|
if (userContext instanceof MQTTRequest) {
|
|
userContextInfo = userContext.toString();
|
|
}
|
|
String logInfo = String.format("onConnectCompleted, status[%s], reconnect[%b], userContext[%s], msg[%s]",
|
|
status.name(), reconnect, userContextInfo, msg);
|
|
LogManager.logInfo(TAG, "连接回调:" + logInfo);
|
|
|
|
if (cause != null) {
|
|
LogManager.logError(TAG, "连接异常信息", cause);
|
|
}
|
|
|
|
if (status.equals(Status.OK)) {
|
|
LogManager.logInfo(TAG, "MQTT连接成功");
|
|
isConnected = true;
|
|
isConnecting = false;
|
|
reconnectAttempts = 0;
|
|
|
|
// 订阅主题
|
|
subscribeToTopic();
|
|
|
|
// 配置断线缓冲
|
|
configureDisconnectedBuffer();
|
|
|
|
// 通知连接成功
|
|
if (connectionStatusListener != null) {
|
|
mainHandler.post(() -> connectionStatusListener.onConnected());
|
|
}
|
|
} else {
|
|
String errorMsg = "连接失败: " + msg;
|
|
if (msg != null && msg.contains("代理程序不可用")) {
|
|
errorMsg += "\n请检查:\n1. 网络连接是否正常\n2. 腾讯云IoT设备信息是否正确\n3. PSK密钥是否有效";
|
|
}
|
|
LogManager.logError(TAG, "MQTT" + errorMsg);
|
|
isConnecting = false;
|
|
|
|
// 通知连接失败
|
|
if (connectionStatusListener != null) {
|
|
final String finalErrorMsg = errorMsg;
|
|
mainHandler.post(() -> connectionStatusListener.onConnectionFailed(finalErrorMsg));
|
|
}
|
|
|
|
// 调度重连
|
|
scheduleReconnect();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onConnectionLost(Throwable cause) {
|
|
LogManager.logError(TAG, "MQTT连接丢失", cause);
|
|
isConnected = false;
|
|
isConnecting = false;
|
|
|
|
String reason = cause != null ? cause.getMessage() : "未知原因";
|
|
|
|
// 检查是否是由于腾讯云SDK内部的NullPointerException导致的断连
|
|
if (reason.contains("java.lang.NullPointerException") &&
|
|
reason.contains("String.contains")) {
|
|
LogManager.logError(TAG, "检测到由于腾讯云IoT Hub SDK内部问题导致的断连");
|
|
LogManager.logError(TAG, "这可能是SDK版本兼容性问题,将适当延迟重连");
|
|
|
|
// 对于这种情况,我们增加一些延迟重连的逻辑
|
|
scheduledExecutor.schedule(() -> {
|
|
if (!isConnected && !isConnecting) {
|
|
LogManager.logInfo(TAG, "尝试从腾讯云SDK内部错误中恢复连接");
|
|
scheduleReconnect();
|
|
}
|
|
}, 2000, TimeUnit.MILLISECONDS); // 2秒延迟
|
|
|
|
} else {
|
|
// 正常的连接丢失,立即重连
|
|
scheduleReconnect();
|
|
}
|
|
|
|
if (connectionStatusListener != null) {
|
|
mainHandler.post(() -> connectionStatusListener.onConnectionLost(reason));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisconnectCompleted(Status status, Object userContext, String msg, Throwable cause) {
|
|
LogManager.logInfo(TAG, "MQTT连接已断开: " + msg);
|
|
isConnected = false;
|
|
isConnecting = false;
|
|
}
|
|
|
|
@Override
|
|
public void onPublishCompleted(Status status, IMqttToken token, Object userContext, String errMsg, Throwable cause) {
|
|
if (status == Status.OK) {
|
|
LogManager.logDebug(TAG, "消息发布成功");
|
|
} else {
|
|
String errorMsg = errMsg != null ? errMsg : "未知错误";
|
|
LogManager.logError(TAG, "消息发布失败: " + errorMsg);
|
|
|
|
// 记录异常信息以便调试
|
|
if (cause != null) {
|
|
LogManager.logError(TAG, "发布异常详情", cause);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onSubscribeCompleted(Status status, IMqttToken asyncActionToken, Object userContext, String errMsg, Throwable cause) {
|
|
// 记录详细的调试信息
|
|
String tokenInfo = "null";
|
|
String userContextInfo = "null";
|
|
|
|
try {
|
|
if (asyncActionToken != null && asyncActionToken.getTopics() != null) {
|
|
tokenInfo = java.util.Arrays.toString(asyncActionToken.getTopics());
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "获取token信息失败", e);
|
|
}
|
|
|
|
try {
|
|
if (userContext instanceof MQTTRequest) {
|
|
userContextInfo = userContext.toString();
|
|
} else if (userContext != null) {
|
|
userContextInfo = userContext.toString();
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "获取userContext信息失败", e);
|
|
}
|
|
|
|
String debugInfo = String.format("onSubscribeCompleted: status[%s], topics[%s], userContext[%s], errMsg[%s]",
|
|
status != null ? status.name() : "null", tokenInfo, userContextInfo, errMsg != null ? errMsg : "null");
|
|
LogManager.logInfo(TAG, "订阅回调详情: " + debugInfo);
|
|
|
|
if (status == Status.OK) {
|
|
LogManager.logInfo(TAG, "主题订阅成功: " + subscribeTopic);
|
|
} else {
|
|
String errorMsg = errMsg != null ? errMsg : "未知错误";
|
|
LogManager.logError(TAG, "主题订阅失败: " + errorMsg);
|
|
|
|
// 检查是否是腾讯云SDK内部的NullPointerException
|
|
if (errorMsg != null && errorMsg.contains("java.lang.NullPointerException")) {
|
|
LogManager.logError(TAG, "检测到腾讯云IoT Hub SDK内部的NullPointerException,这可能是SDK的问题");
|
|
LogManager.logError(TAG, "尝试忽略这个错误并继续保持连接");
|
|
|
|
// 对于腾讯云SDK内部的gateway result主题订阅失败,我们可以忽略
|
|
if (tokenInfo.contains("$gateway/operation/result")) {
|
|
LogManager.logWarning(TAG, "忽略gateway result主题订阅失败,这不影响主要功能");
|
|
return; // 不触发重连
|
|
}
|
|
}
|
|
|
|
// 检查是否是由于代理程序不可用导致的错误
|
|
if (errorMsg != null && errorMsg.contains("代理程序不可用")) {
|
|
LogManager.logError(TAG, "MQTT代理程序不可用,请检查网络连接和腾讯云IoT设备配置");
|
|
}
|
|
|
|
// 记录异常信息以便调试
|
|
if (cause != null) {
|
|
LogManager.logError(TAG, "订阅异常详情", cause);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onUnSubscribeCompleted(Status status, IMqttToken asyncActionToken, Object userContext, String errMsg, Throwable cause) {
|
|
String message = errMsg != null ? errMsg : "取消订阅完成";
|
|
LogManager.logInfo(TAG, "取消订阅完成: " + message);
|
|
|
|
// 记录异常信息以便调试
|
|
if (cause != null) {
|
|
LogManager.logError(TAG, "取消订阅异常详情", cause);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onMessageReceived(String topic, MqttMessage message) {
|
|
String messageContent = new String(message.getPayload());
|
|
String timestamp = new SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()).format(new Date());
|
|
|
|
LogManager.logInfo(TAG, timestamp + " " + TAG + " D 接收到MQTT消息,主题: " + topic + ", 内容: " + messageContent);
|
|
|
|
// 处理消息
|
|
processMessage(topic, messageContent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理接收到的消息
|
|
*/
|
|
private void processMessage(String topic, String messageContent) {
|
|
try {
|
|
if (messageContent.contains("\"gate\"")) {
|
|
handleGateCommand(messageContent);
|
|
} else if (messageContent.contains("reboot-pad")) {
|
|
handleRebootCommand();
|
|
} else if (messageContent.contains("get_log_level")) {
|
|
handleLogLevelQuery();
|
|
} else {
|
|
handleOtherMessage(topic, messageContent);
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "处理MQTT消息异常", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理门闸控制命令
|
|
*/
|
|
private void handleGateCommand(String messageContent) {
|
|
LogManager.logInfo(TAG, "处理门闸控制命令: " + messageContent);
|
|
|
|
try {
|
|
String gateCommand = extractGateCommand(messageContent);
|
|
if (gateCommand != null) {
|
|
LogManager.logInfo(TAG, "解析门闸命令: " + gateCommand);
|
|
|
|
if (gateABController != null) {
|
|
gateABController.openGateAB(new GateABController.GateControlCallback() {
|
|
@Override
|
|
public void onSuccess(String message) {
|
|
LogManager.logInfo(TAG, "MQTT门闸控制成功: " + message);
|
|
}
|
|
|
|
@Override
|
|
public void onError(String errorMessage) {
|
|
LogManager.logError(TAG, "MQTT门闸控制失败: " + errorMessage);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (messageReceivedListener != null) {
|
|
mainHandler.post(() -> messageReceivedListener.onGateCommandReceived(gateCommand));
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "处理门闸控制命令异常", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 提取门闸命令
|
|
*/
|
|
private String extractGateCommand(String messageContent) {
|
|
try {
|
|
int gateIndex = messageContent.indexOf("\"gate\"");
|
|
if (gateIndex != -1) {
|
|
int startIndex = messageContent.indexOf(":", gateIndex) + 1;
|
|
int endIndex = messageContent.indexOf("}", startIndex);
|
|
if (startIndex > 0 && endIndex > startIndex) {
|
|
return messageContent.substring(startIndex, endIndex).trim();
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "提取门闸命令异常", e);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* 处理设备重启命令
|
|
*/
|
|
private void handleRebootCommand() {
|
|
LogManager.logInfo(TAG, "接收到设备重启命令");
|
|
|
|
try {
|
|
if (messageReceivedListener != null) {
|
|
mainHandler.post(() -> messageReceivedListener.onRebootCommandReceived());
|
|
}
|
|
LogManager.logWarning(TAG, "设备重启功能待实现");
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "处理设备重启命令异常", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理日志级别查询
|
|
*/
|
|
private void handleLogLevelQuery() {
|
|
LogManager.logInfo(TAG, "接收到日志级别查询命令");
|
|
|
|
try {
|
|
if (messageReceivedListener != null) {
|
|
mainHandler.post(() -> messageReceivedListener.onLogLevelQueryReceived());
|
|
}
|
|
uploadDeviceInfo();
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "处理日志级别查询异常", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 处理其他消息
|
|
*/
|
|
private void handleOtherMessage(String topic, String messageContent) {
|
|
LogManager.logInfo(TAG, "接收到其他消息,主题: " + topic + ", 内容: " + messageContent);
|
|
|
|
if (messageReceivedListener != null) {
|
|
mainHandler.post(() -> messageReceivedListener.onOtherMessageReceived(topic, messageContent));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 上报设备信息
|
|
*/
|
|
private void uploadDeviceInfo() {
|
|
try {
|
|
JSONObject deviceInfo = new JSONObject();
|
|
deviceInfo.put("deviceId", DeviceUtils.getAndroidID(context));
|
|
deviceInfo.put("deviceModel", DeviceUtils.getDeviceModel());
|
|
deviceInfo.put("deviceBrand", DeviceUtils.getDeviceBrand());
|
|
deviceInfo.put("androidVersion", DeviceUtils.getAndroidVersion());
|
|
deviceInfo.put("timestamp", System.currentTimeMillis());
|
|
|
|
String dataTopic = PRODUCT_ID + "/" + deviceName + "/data";
|
|
publishMessage(dataTopic, deviceInfo.toString());
|
|
|
|
LogManager.logInfo(TAG, "设备信息上报完成: " + deviceInfo.toString());
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "设备信息上报失败", e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 发布消息
|
|
*/
|
|
public void publishMessage(String topic, String messageContent) {
|
|
if (mqttConnection != null && isConnected) {
|
|
try {
|
|
MqttMessage message = new MqttMessage();
|
|
message.setQos(QOS);
|
|
message.setPayload(messageContent.getBytes());
|
|
|
|
MQTTRequest mqttRequest = new MQTTRequest("publish", requestID.getAndIncrement());
|
|
mqttConnection.publish(topic, message, mqttRequest);
|
|
|
|
LogManager.logInfo(TAG, "消息发布成功,主题: " + topic);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "发布消息异常", e);
|
|
}
|
|
} else {
|
|
LogManager.logWarning(TAG, "MQTT未连接,无法发布消息");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取连接状态
|
|
*/
|
|
public boolean isConnected() {
|
|
return isConnected && mqttConnection != null;
|
|
}
|
|
|
|
/**
|
|
* 获取连接状态详情
|
|
*/
|
|
public String getConnectionStatusDetail() {
|
|
if (isConnected()) {
|
|
return "MQTT已连接";
|
|
} else if (isConnecting) {
|
|
return "MQTT连接中...";
|
|
} else if (reconnectAttempts > 0) {
|
|
return "MQTT重连中(" + reconnectAttempts + "/" + MAX_RECONNECT_ATTEMPTS + ")";
|
|
} else {
|
|
return "MQTT未连接";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 设置连接状态监听器
|
|
*/
|
|
public void setConnectionStatusListener(ConnectionStatusListener listener) {
|
|
this.connectionStatusListener = listener;
|
|
}
|
|
|
|
/**
|
|
* 设置消息接收监听器
|
|
*/
|
|
public void setMessageReceivedListener(MessageReceivedListener listener) {
|
|
this.messageReceivedListener = listener;
|
|
}
|
|
|
|
/**
|
|
* 断开连接
|
|
*/
|
|
public void disconnect() {
|
|
LogManager.logInfo(TAG, "开始断开MQTT连接");
|
|
|
|
isConnected = false;
|
|
isConnecting = false;
|
|
|
|
if (mqttConnection != null) {
|
|
try {
|
|
MQTTRequest mqttRequest = new MQTTRequest("disconnect", requestID.getAndIncrement());
|
|
mqttConnection.disConnect(mqttRequest);
|
|
} catch (Exception e) {
|
|
LogManager.logError(TAG, "断开MQTT连接异常", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 释放资源
|
|
*/
|
|
public void release() {
|
|
LogManager.logInfo(TAG, "释放MQTT管理器资源");
|
|
|
|
disconnect();
|
|
|
|
if (executorService != null && !executorService.isShutdown()) {
|
|
executorService.shutdown();
|
|
}
|
|
|
|
if (scheduledExecutor != null && !scheduledExecutor.isShutdown()) {
|
|
scheduledExecutor.shutdown();
|
|
}
|
|
|
|
mqttConnection = null;
|
|
connectionStatusListener = null;
|
|
messageReceivedListener = null;
|
|
|
|
LogManager.logInfo(TAG, "MQTT管理器资源释放完成");
|
|
}
|
|
}
|