完成页面对接tcp推送的设备状态数据

完成页面测试解析tcp推送的设备状态数据的UI展示
This commit is contained in:
2026-03-03 19:34:17 +08:00
parent 0a3a062c04
commit 34b4c2bcbc
12 changed files with 676 additions and 230 deletions

View File

@@ -0,0 +1,6 @@
abstract class AuthTcpDatasource {
Future<void> sendAuthPacket();
}

View File

@@ -0,0 +1,108 @@
// lib/features/auth/data/datasources/auth_tcp_datasource.dart
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:fpdart/src/either.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/errors/device_failure.dart';
import '../../../../../core/network/tcp/tcp_client.dart';
import '../../../../../core/storage/user_storage.dart';
import '../../../../devices/domain/usecases/get_user_device_usecase.dart';
import '../../../../devices/domain/usecases/switch_device_usecase.dart';
import '../auth_tcp_datasource.dart';
class AuthTcpDatasourceImpl implements AuthTcpDatasource {
final TcpClient _tcpClient;
final UserStorage _userStorage;
final GetUserDeviceUseCase getUserDeviceUseCase;
final SwitchDeviceUseCase switchDeviceUseCase;
AuthTcpDatasourceImpl(this._tcpClient, this._userStorage, {required this.getUserDeviceUseCase, required this.switchDeviceUseCase});
@override
Future<void> sendAuthPacket() async {
if (!_tcpClient.isConnected) {
throw Exception('TCP 未连接,无法发送认证包');
}
final user = await _userStorage.getUser();
if (user == null || user.token == null || user.username == null) {
throw Exception('用户未登录,无法认证');
}
final username = user.username!;
final token = user.token!;
final authString = '$username:app:$token';
debugPrint('🔑 [AuthTcp] 准备发送认证包:$authString');
final authBytes = utf8.encode(authString);
// 构造协议包:AB AA 03 [Payload] CRC(00 00) AA AB
// 注意:如果服务端校验 CRC,需在此处计算真实 CRC
final builder = BytesBuilder()
..addByte(0xAB)
..addByte(0xAA)
..addByte(0x03)
..add(authBytes)
..addByte(0x00)
..addByte(0x00)
..addByte(0xAA)
..addByte(0xAB);
_tcpClient.sendRawBytes(builder.takeBytes()); // 假设 TcpClient 增加了此方法,或用 socket.add
debugPrint('✅ [AuthTcp] 认证包已发送');
// 获取用户设备列表
try {
// 1. 获取 Either 结果
final eitherResult = await getUserDeviceUseCase.repository.getUserDevice(username);
// 2. 使用 fold 解包 Either
// left: 处理错误情况 (DeviceFailure)
// right: 处理成功情况 (List<DeviceEntity>)
await eitherResult.fold(
(failure) {
// 处理失败:打印日志或抛出异常
debugPrint('❌ [AuthTcp] 获取设备列表失败:$failure');
throw Exception('获取设备列表失败:$failure');
},
(devices) async {
// 处理成功:devices 现在是真正的 List<DeviceEntity>
if (devices.isEmpty) {
debugPrint('⚠️ [AuthTcp] 当前用户无可用设备,跳过切换步骤');
return;
}
// 取第一个设备
final DeviceEntity targetDevice = devices.first;
debugPrint('📱 [AuthTcp] 准备切换至默认设备:${targetDevice.deviceName}');
// 切换设备 (同样,如果 switchDeviceUseCase 也返回 Either,也需要 fold 处理)
final switchResult = await switchDeviceUseCase.deviceRepository.switchDevice("app",targetDevice.deviceName);
await switchResult.fold(
(failure) {
debugPrint('❌ [AuthTcp] 切换设备失败:$failure');
throw Exception('切换设备失败:$failure');
},
(success) {
debugPrint('✅ [AuthTcp] 设备切换成功,服务端应开始推送数据');
},
);
},
);
} catch (e) {
debugPrint('❌ [AuthTcp] 设备订阅流程异常:$e');
rethrow;
}
}
}

View File

@@ -8,6 +8,10 @@ import 'package:maibu_satabot_v2/core/network/net_message_dispatcher.dart';
import '../../../../core/app/app_user_cubit.dart';
import '../../../../core/network/tcp/tcp_client.dart';
import '../../../../core/storage/user_storage.dart';
import '../../../devices/domain/usecases/get_user_device_usecase.dart';
import '../../../devices/domain/usecases/switch_device_usecase.dart';
import '../../data/datasources/auth_tcp_datasource.dart';
import '../../data/datasources/impl/auth_tcp_datasource_impl.dart';
import 'auth_state.dart';
/// emit做的事情:
@@ -19,9 +23,11 @@ class AuthCubit extends Cubit<AuthState> {
final TcpClient tcp;
final AppUserCubit appCubit;
final NetMessageDispatcher dispatcher;
final AuthTcpDatasource _authTcpDatasource;
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
AuthCubit(this.storage, this.tcp, this.appCubit, this.dispatcher)
AuthCubit(this.storage, this.tcp, this.appCubit, this.dispatcher, this._authTcpDatasource)
: super(AuthInitial()) {
// Cubit 一启动就开始监听 TCP 的“自动逻辑”
_listenToAuthResponse();
@@ -33,6 +39,7 @@ class AuthCubit extends Cubit<AuthState> {
if (user != null) {
// 1. 建立 TCP 连接 (远程控制)
tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
await _authTcpDatasource.sendAuthPacket();//包括发送认证包和获取列表和切换函数
// 2. 同步全局 App 状态
appCubit.setAuth(user);
// 3. 进入已登录状态
@@ -46,7 +53,8 @@ class AuthCubit extends Cubit<AuthState> {
Future<void> loginSuccess(UserEntity user) async {
await storage.saveUser(user);
await tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
tcp.startHeartbeat(interval: const Duration(seconds: 4)); // 👈 启动心跳
await _authTcpDatasource.sendAuthPacket();//包括发送认证包和获取列表和切换函数
tcp.startHeartbeat(interval: const Duration(seconds: 4)); //启动心跳
appCubit.setAuth(user);
emit(AuthAuthenticated(user));
}