#连接库初步完成

This commit is contained in:
CC
2026-01-27 18:44:49 +08:00
parent 8c39f0daf8
commit 949743972c
82 changed files with 1187 additions and 650 deletions

View File

@@ -0,0 +1,14 @@
import 'dart:async';
import 'package:fpdart/fpdart.dart';
import '../../error/failure.dart';
enum ConnectionStatus { connected, disconnected, connecting }
abstract class IConnection {
Stream<List<int>> get rawStream;
Stream<ConnectionStatus> get statusStream;
Future<Either<Failure, Unit>> connect();
Future<void> disconnect();
Future<Either<Failure, Unit>> send(List<int> data);
}

View File

@@ -0,0 +1,3 @@
class BleClient{
}

View File

@@ -0,0 +1 @@
enum ControlMode { TCP, BLE, LOCAL, NONE, OTHER }

View File

@@ -1,8 +1,8 @@
import 'package:dio/dio.dart';
import 'package:maibu_satabot_v2/core/consts/http_api_consts.dart';
import '../app/app_user_cubit.dart';
import '../di/injection.dart';
import '../../../features/auth/presentation/bloc/app_user_cubit.dart';
import '../../di/injection.dart';
class DioClient {
static Dio create() {

View File

@@ -0,0 +1,17 @@
class ConnectionConfig {
final String address; // IP 地址或蓝牙 MAC/UUID
final int? port; // TCP 专用端口
ConnectionConfig({
required this.address,
this.port,
});
// 方便快捷创建 TCP 配置
factory ConnectionConfig.tcp(String ip, int port) =>
ConnectionConfig(address: ip, port: port);
// 方便快捷创建 BLE 配置
factory ConnectionConfig.ble(String mac) =>
ConnectionConfig(address: mac);
}

View File

@@ -1,26 +0,0 @@
import 'dart:convert';
import 'protocol_decoder.dart';
import 'tcp/tcp_client.dart';
// 注意:这里需要导入你具体的 UserModel
// import '../../features/auth/data/models/user_model.dart';
class NetMessageDispatcher {
final TcpClient tcpClient;
NetMessageDispatcher(this.tcpClient);
/// 过滤特定指令的流
Stream<RawPacket> onCommand(int commandCode) {
return tcpClient.packetStream.where((p) => p.command == commandCode);
}
/// 示例解析方法:将 0x02 指令解析为 String
Stream<String> onStringMessage() {
return onCommand(0x02).map((p) => utf8.decode(p.payload));
}
/// 示例解析方法:将 0x12 指令解析为 JSON 并转为模型
Stream<Map<String, dynamic>> onJsonMessage(int cmd) {
return onCommand(cmd).map((p) => jsonDecode(utf8.decode(p.payload)));
}
}

View File

@@ -1,59 +0,0 @@
import 'dart:typed_data';
class RawPacket {
final int command; // 第三位指令位
final Uint8List payload; // 数据位
RawPacket({required this.command, required this.payload});
}
class ProtocolDecoder {
static const head = [0xAB, 0xAA];
static const tail = [0xAA, 0xAB];
final List<int> _buffer = [];
List<RawPacket> decode(List<int> newData) {
_buffer.addAll(newData);
List<RawPacket> packets = [];
while (_buffer.length >= 5) {
// 最小包长度:头2+指令1+尾2 = 5,加1位数据=6,但是心跳包是AB AA FF AA AB
// 1. 找头
int headIdx = -1;
for (int i = 0; i < _buffer.length - 1; i++) {
if (_buffer[i] == head[0] && _buffer[i + 1] == head[1]) {
headIdx = i;
break;
}
}
if (headIdx == -1) {
_buffer.clear(); // 没头,全是垃圾数据
break;
}
// 2. 找尾
int tailIdx = -1;
for (int i = headIdx + 2; i < _buffer.length - 1; i++) {
if (_buffer[i] == tail[0] && _buffer[i + 1] == tail[1]) {
tailIdx = i;
break;
}
}
if (tailIdx != -1) {
// 3. 提取
int cmd = _buffer[headIdx + 2];
Uint8List payload = Uint8List.fromList(
_buffer.sublist(headIdx + 3, tailIdx),
);
packets.add(RawPacket(command: cmd, payload: payload));
// 4. 移除已处理部分
_buffer.removeRange(0, tailIdx + 2);
} else {
break; // 还没传完
}
}
return packets;
}
}

View File

@@ -1,66 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:maibu_satabot_v2/core/network/protocol_decoder.dart';
class TcpClient {
Socket? _socket;
final _decoder = ProtocolDecoder();
// 使用 StreamController 将原始数据流暴露给外部,以便多个 DataSource 监听
final _controller = StreamController<RawPacket>.broadcast();
Stream<RawPacket> get packetStream => _controller.stream;
// 通过参数配置,不硬编码
Future<void> connect({required String host, required int port}) async {
try {
_socket = await Socket.connect(
host,
port,
timeout: const Duration(seconds: 5),
);
_socket!.listen(
(data) {
var packets = _decoder.decode(data);
for (var packet in packets) {
_controller.add(packet);
}
},
onDone: disconnect,
onError: (e) => disconnect(),
);
} catch (e) {
rethrow; // 向上抛出连接异常
}
}
void send(Map<String, dynamic> data) {
if (_socket == null) throw Exception("Socket not connected");
_socket!.write(jsonEncode(data));
}
void sendRaw(int cmd, List<int> payload) {
if (_socket == null) return;
final builder = BytesBuilder()
..addByte(0xAB)
..addByte(0xAA)
..addByte(cmd)
..add(payload)
..add([0x00, 0x00]) // CRC
..addByte(0xAA)
..addByte(0xAB);
_socket!.add(builder.takeBytes());
}
void disconnect() {
_socket?.destroy();
_socket = null;
_controller.close();
debugPrint("TCP Disconnected");
}
}

View File

@@ -0,0 +1,53 @@
import 'dart:io';
import 'dart:async';
import 'package:fpdart/fpdart.dart';
import '../../error/failure.dart';
import '../base/i_connection.dart';
class TcpConnection implements IConnection {
final String host;
final int port;
Socket? _socket;
final _statusController = StreamController<ConnectionStatus>.broadcast();
TcpConnection({required this.host, required this.port});
@override
Stream<List<int>> get rawStream => _socket ?? const Stream.empty();
@override
Stream<ConnectionStatus> get statusStream => _statusController.stream;
@override
Future<Either<Failure, Unit>> connect() async {
try {
_statusController.add(ConnectionStatus.connecting);
_socket = await Socket.connect(host, port, timeout: const Duration(seconds: 5));
_statusController.add(ConnectionStatus.connected);
_socket!.done.then((_) => _statusController.add(ConnectionStatus.disconnected));
return right(unit);
} catch (e) {
_statusController.add(ConnectionStatus.disconnected);
return left(NetworkFailure(e.toString()));
}
}
@override
Future<Either<Failure, Unit>> send(List<int> data) async {
if (_socket == null) return left(NetworkFailure("Socket未连接"));
try {
_socket!.add(data);
await _socket!.flush(); // 立即刷出
return right(unit);
} catch (e) {
return left(NetworkFailure(e.toString()));
}
}
@override
Future<void> disconnect() async {
await _socket?.close();
_socket = null;
_statusController.add(ConnectionStatus.disconnected);
}
}