Files
flutterApp/lib/features/remote_control/data/datasources/remote_tcp_datasource.dart
Songzex 7c01229b9e 1:修复远程遥控控制状态显示不正确的问题
2:更换端口映射为测试服的8081-59003,9001-59004
2026-08-26 14:23:20 +08:00

235 lines
8.2 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import '../../../../core/logging/i_logger_service.dart';
import '../../../../core/network/protocol_decoder.dart';
import '../../../../core/network/tcp/tcp_client.dart';
import '../../../../core/protocol/machine_protocol_constants.dart';
import '../../../../core/storage/user_storage.dart';
/// 远程遥控 TCP 数据源
/// 负责通过 TCP 协议发送控制指令和权限请求
class RemoteTcpDatasource {
final TcpClient _tcpClient;
final UserStorage _userStorage;
final ILoggerService _logger = GetIt.I<ILoggerService>();
RemoteTcpDatasource(this._tcpClient, this._userStorage);
/// 发送 switch_control 权限请求(对应 Android 的 requestSwitchControl)
///
/// 这是获取设备控制权的第一步:
/// 1. 先发送 TCP 0x12 指令携带 switch_control 请求
/// 2. 等待服务端响应
/// 3. 再调用 HTTP API 确认控制权
Future<void> sendSwitchControlRequest(String deviceName) async {
if (!_tcpClient.isConnected) {
debugPrint('❌sendSwitchControlRequest [TCP] 无法发送权限请求:Socket 未连接');
throw Exception('sendSwitchControlRequest TCP 未连接');
}
try {
// 获取用户信息
final user = await _userStorage.getUser();
if (user == null || user.token == null) {
debugPrint(
'❌ sendSwitchControlRequest[TCP] 认证失败:用户未登录或 Token 为空,无法发送认证包',
);
throw Exception('用户未登录或 Token 无效');
}
final username = user.username;
final token = user.token;
final currentDeviceId = deviceName;
// 构造 JSON 请求体(参考 Android 代码)
final request = {
'request': 'switch_control',
'deviceId': currentDeviceId,
'userId': username,
'platform': 'app',
'token': token,
};
final jsonStr = jsonEncode(request);
final jsonBytes = utf8.encode(jsonStr);
debugPrint('sendSwitchControlRequest[TCP] 发送权限请求指令:$jsonStr');
_logger.logWithLevel(
'🔑 [TCP][获取权限] 发送权限请求指令:$jsonStr',
shouldLog: true,
);
// 使用 0x12 指令发送(cmdGetAuth)
// 注意:根据你的协议文档,这里可能是 0x04 或 0x12
// Android 代码中使用的是 0x12
// _tcpClient.sendnew(
// // MachineProtocolConstants.cmdGetAuth, // 0x04 或 0x12,根据实际协议调整
// jsonBytes,
// );
// 构造完整的协议包
final packet = buildSwitchControlPacket(jsonBytes);
debugPrint(
'sendSwitchControlRequest[TCP] 完整数据包:${packet.map((b) => '0x${b.toRadixString(16)}').join(' ')}',
);
// 发送数据包
_tcpClient.sendnew(packet);
debugPrint(
'✅ sendSwitchControlRequest[TCP] 已发送 switch_control 权限请求 (0x${MachineProtocolConstants.cmdGetAuth.toRadixString(16)})',
);
_logger.logWithLevel(
'✅ [TCP][获取权限] 已发送 switch_control 权限请求 (0x${MachineProtocolConstants.cmdGetAuth.toRadixString(16)})',
shouldLog: true,
);
} catch (e) {
debugPrint('❌ sendSwitchControlRequest[TCP] 发送权限请求失败:$e');
_logger.logWithLevel(
'❌ [TCP][获取权限] 发送权限请求失败:$e',
level: 'ERROR',
shouldLog: true,
);
rethrow;
}
}
/// 监听 TCP 回包流
/// 用于接收权限响应、状态更新等
Stream<RawPacket> get responseStream => _tcpClient.packetStream;
/// 检查 TCP 连接状态
bool get isConnected => _tcpClient.isConnected;
/// 构建 switch_control 协议包
///
/// 包结构:
/// - 帧头:0xAB, 0xAA (2 字节)
/// - 命令字:0x12 (1 字节)
/// - 数据载荷:JSON UTF-8 字节 (N 字节)
/// - 帧尾:0x00, 0x00, 0xAA, 0xAB (4 字节)
List<int> buildSwitchControlPacket(List<int> jsonBytes) {
final builder = BytesBuilder()
..addByte(MachineProtocolConstants.header1) // 0xAB
..addByte(MachineProtocolConstants.header2) // 0xAA
..addByte(0x12) // 命令字:switch_control
..add(jsonBytes) // JSON 数据
..addByte(0x00) // CRC 占位 1
..addByte(0x00) // CRC 占位 2
..addByte(MachineProtocolConstants.endFlag1) // 0xAA
..addByte(MachineProtocolConstants.endFlag2); // 0xAB
return builder.takeBytes();
}
// TODO: 同意授权和拒绝授权
void sendSwitchControlResponse(bool agreed, String deviceId) {
debugPrint(
'📤 [进入了TCP层==== 被调用 - agreed=$agreed, deviceId=$deviceId',
);
// 🔥 添加详细的连接状态日志
debugPrint(
'📤 [进入了TCP层==== 当前 TCP 连接状态: isConnected=${_tcpClient.isConnected}',
);
if (!_tcpClient.isConnected) {
debugPrint('❌ [TCP DataSource] 无法发送权限响应: Socket 未连接');
debugPrint('❌ [TCP DataSource] isConnected=${_tcpClient.isConnected}');
throw Exception('sendSwitchControlResponse TCP 未连接');
}
try {
debugPrint('📡 [TCP DataSource] TCP 连接正常,准备发送数据...');
// 构造响应 JSON(参考 Android 代码)
final response = agreed
? {
'respond': {
'switchResult': true,
'deviceId': deviceId,
'holder': 'you',
},
}
: {
'respond': {
'switchResult': false,
'deviceId': deviceId,
'reason': 'holder_denied',
},
};
debugPrint(
'📝 [TCP DataSource] ${agreed ? "同意" : "拒绝"}授权: deviceId=$deviceId',
);
final jsonStr = jsonEncode(response);
final jsonBytes = utf8.encode(jsonStr);
debugPrint('📦 [TCP DataSource] JSON 数据: $jsonStr');
debugPrint('📦 [TCP DataSource] JSON 字节长度: ${jsonBytes.length} 字节');
// 构造完整的协议包
final packet = buildSwitchControlPacket(jsonBytes);
_logger.logWithLevel('TCP DataSource] JSON 数据: $jsonStr' ,shouldLog: true);
debugPrint(
'📦 [TCP DataSource] 完整数据包(${packet.length}字节): ${packet.map((b) => '0x${b.toRadixString(16).padLeft(2, "0")}').join(' ')}',
);
// 🔥 发送前再次检查连接状态
if (!_tcpClient.isConnected) {
debugPrint('❌ [TCP DataSource] 发送前检查: Socket 已断开连接!');
throw Exception('TCP 连接在发送前断开');
}
// 发送数据包
debugPrint('🚀 [TCP DataSource] 正在调用 SendNew 发送数据...');
// 🔥 添加详细日志:记录发送的完整数据包(十六进制)
final hexString = packet
.map((byte) => '0x${byte.toRadixString(16).padLeft(2, '0')}')
.join(' ');
debugPrint('📦 [TCP DataSource] 完整数据包(${packet.length}字节): $hexString');
// 🔥 使用ILoggerService记录日志
_logger.logWithLevel(
'📦 [TCP DataSource] 完整数据包(${packet.length}字节): $hexString',
shouldLog: true,
);
// 🔥 记录JSON内容和字节数
_logger.logWithLevel(
'📝 [TCP DataSource] 发送内容: $jsonStr',
shouldLog: true,
);
_logger.logWithLevel(
'📊 [TCP DataSource] JSON字节数: ${jsonBytes.length}, 完整包字节数: ${packet.length}',
shouldLog: true,
);
_tcpClient.sendnew(packet);
debugPrint('✅ [TCP DataSource] TCP 响应已成功发送');
debugPrint('✅ [TCP DataSource] 发送字节数: ${packet.length}');
// 🔥 发送成功后的日志
_logger.logWithLevel(
'✅ [TCP DataSource] TCP 响应已成功发送 - agreed=$agreed, deviceId=$deviceId, 字节数=${packet.length}',
shouldLog: true,
);
} catch (e) {
debugPrint('❌ [TCP DataSource] 响应权限请求失败: $e');
debugPrint('❌ [TCP DataSource] 错误类型: ${e.runtimeType}');
debugPrint('❌ [TCP DataSource] 错误堆栈: ${StackTrace.current}');
rethrow;
}
}
}