完成页面对接tcp推送的设备状态数据
完成页面测试解析tcp推送的设备状态数据的UI展示
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
abstract class AuthTcpDatasource {
|
||||
|
||||
Future<void> sendAuthPacket();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user