Files
ble_debug_system/bin/server.dart

385 lines
12 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_web_socket/shelf_web_socket.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
// 用户连接信息
class UserConnection {
final String uuid;
final String deviceModel;
final WebSocketChannel channel;
final DateTime connectedAt;
String? currentDeviceId;
UserConnection({
required this.uuid,
required this.deviceModel,
required this.channel,
required this.connectedAt,
this.currentDeviceId,
});
}
// WebSocket 管理器
class WebSocketManager {
final Map<String, UserConnection> _devices = {};
final Map<String, WebSocketChannel> _webClients = {};
// 存储每个连接的注册信息
final Map<WebSocketChannel, String> _channelToClientId = {};
final Map<WebSocketChannel, String> _channelToDeviceUuid = {};
// 获取设备连接
UserConnection? getDevice(String uuid) {
return _devices[uuid];
}
// 获取所有设备列表
List<Map<String, dynamic>> getDeviceList() {
return _devices.entries.map((entry) {
return {
'uuid': entry.key,
'deviceModel': entry.value.deviceModel,
'connectedAt': entry.value.connectedAt.toIso8601String(),
'isConnected': true,
};
}).toList();
}
// 注册设备
void registerDevice(String uuid, String deviceModel, WebSocketChannel channel) {
_devices[uuid] = UserConnection(
uuid: uuid,
deviceModel: deviceModel,
channel: channel,
connectedAt: DateTime.now(),
);
_channelToDeviceUuid[channel] = uuid;
print('✅ 调试设备注册: $deviceModel ($uuid)');
_broadcastUserList();
}
// 注册网页客户端
void registerWebClient(String clientId, WebSocketChannel channel) {
_webClients[clientId] = channel;
_channelToClientId[channel] = clientId;
print('🌐 网页客户端注册: $clientId');
// 发送设备列表
_sendUserListToClient(channel);
}
// 处理网页消息
void handleWebMessage(WebSocketChannel channel, Map<String, dynamic> data) {
final clientId = _channelToClientId[channel];
if (clientId == null) {
print('未找到对应的网页客户端');
return;
}
print('💻 收到网页指令: ${data['type']} from client: $clientId');
final commandType = data['type'] as String;
final targetUuid = data['targetUuid'] as String?;
switch (commandType) {
case 'select_device':
if (targetUuid != null) {
final device = _devices[targetUuid];
if (device != null) {
print(' -> 网页选择控制设备: $targetUuid (${device.deviceModel})');
channel.sink.add(jsonEncode({
'type': 'device_selected',
'targetUuid': targetUuid,
'deviceModel': device.deviceModel,
'timestamp': DateTime.now().toIso8601String(),
}));
} else {
print(' -> 设备不存在: $targetUuid');
}
}
break;
case 'scan':
if (targetUuid != null) {
final device = _devices[targetUuid];
if (device != null) {
print(' -> 发送扫描命令到设备: $targetUuid');
device.channel.sink.add(jsonEncode({
'type': 'scan',
'timestamp': DateTime.now().toIso8601String(),
}));
} else {
print(' -> 设备不存在: $targetUuid');
}
}
break;
case 'stop_scan':
if (targetUuid != null) {
final device = _devices[targetUuid];
if (device != null) {
device.channel.sink.add(jsonEncode({
'type': 'stop_scan',
'timestamp': DateTime.now().toIso8601String(),
}));
print(' -> 发送停止扫描命令到设备: $targetUuid');
}
}
break;
case 'connect':
if (targetUuid != null) {
final device = _devices[targetUuid];
final deviceId = data['deviceId'] as String?;
if (device != null && deviceId != null) {
device.currentDeviceId = deviceId;
device.channel.sink.add(jsonEncode({
'type': 'connect',
'deviceId': deviceId,
'timestamp': DateTime.now().toIso8601String(),
}));
print(' -> 发送连接设备命令到: $targetUuid, 设备ID: $deviceId');
}
}
break;
case 'disconnect':
if (targetUuid != null) {
final device = _devices[targetUuid];
if (device != null) {
device.currentDeviceId = null;
device.channel.sink.add(jsonEncode({
'type': 'disconnect',
'timestamp': DateTime.now().toIso8601String(),
}));
print(' -> 发送断开连接命令到设备: $targetUuid');
}
}
break;
case 'send_data':
if (targetUuid != null) {
final device = _devices[targetUuid];
final sendData = data['data'];
if (device != null && sendData != null) {
device.channel.sink.add(jsonEncode({
'type': 'send_data',
'data': sendData,
'timestamp': DateTime.now().toIso8601String(),
}));
print(' -> 发送数据到设备: $targetUuid, 数据: $sendData');
}
}
break;
default:
print('未知命令: $commandType');
}
}
// 处理设备消息
void handleDeviceMessage(WebSocketChannel channel, Map<String, dynamic> data) {
final uuid = _channelToDeviceUuid[channel];
if (uuid == null) {
print('未找到对应的设备');
return;
}
print('📱 收到设备 $uuid 消息: ${data['type']}');
// 转发给所有网页客户端
for (var entry in _webClients.entries) {
entry.value.sink.add(jsonEncode({
'type': 'device_message',
'fromUser': uuid,
'data': data,
'timestamp': DateTime.now().toIso8601String(),
}));
}
}
// 移除设备
void removeDevice(WebSocketChannel channel) {
final uuid = _channelToDeviceUuid[channel];
if (uuid != null) {
_devices.remove(uuid);
_channelToDeviceUuid.remove(channel);
print('🗑️ 移除调试设备: $uuid');
_broadcastUserList();
}
}
// 移除网页客户端
void removeWebClient(WebSocketChannel channel) {
final clientId = _channelToClientId[channel];
if (clientId != null) {
_webClients.remove(clientId);
_channelToClientId.remove(channel);
print('网页客户端断开: $clientId');
}
}
void _sendUserListToClient(WebSocketChannel client) {
final deviceList = getDeviceList();
client.sink.add(jsonEncode({
'type': 'user_list',
'users': deviceList,
'timestamp': DateTime.now().toIso8601String(),
}));
print('📡 发送设备列表到网页客户端: ${deviceList.length} 个设备');
}
void _broadcastUserList() {
final deviceList = getDeviceList();
final message = jsonEncode({
'type': 'user_list',
'users': deviceList,
'timestamp': DateTime.now().toIso8601String(),
});
for (var client in _webClients.values) {
client.sink.add(message);
}
print('📡 广播设备列表: ${deviceList.length} 个设备');
}
}
// WebSocket 处理函数 - 所有消息都在这里统一处理
Future<Response> handleWebSocket(Request request, WebSocketManager manager) async {
final handler = webSocketHandler((WebSocketChannel channel, String? protocol) {
print('🔌 WebSocket 连接建立');
// 标识是否已经注册
bool isRegistered = false;
String? connectionType;
channel.stream.listen((message) {
try {
final data = jsonDecode(message);
final type = data['type'] as String?;
print('收到消息,类型: $type, 已注册: $isRegistered');
if (!isRegistered) {
// 处理注册消息
if (type == 'device_register') {
final uuid = data['uuid'] as String?;
final deviceModel = data['deviceModel'] as String?;
if (uuid != null && deviceModel != null) {
isRegistered = true;
connectionType = 'device';
manager.registerDevice(uuid, deviceModel, channel);
} else {
print('设备注册信息不完整');
channel.sink.close();
}
} else if (type == 'web_register') {
final clientId = data['clientId'] as String? ??
'web_${DateTime.now().millisecondsSinceEpoch}';
isRegistered = true;
connectionType = 'web';
manager.registerWebClient(clientId, channel);
} else {
print('等待注册消息,收到: $type');
channel.sink.close();
}
} else {
// 已注册,根据连接类型处理后续消息
if (connectionType == 'web') {
manager.handleWebMessage(channel, data);
} else if (connectionType == 'device') {
manager.handleDeviceMessage(channel, data);
} else {
print('未知连接类型: $connectionType');
}
}
} catch (e) {
print('解析消息失败: $e');
}
}, onDone: () {
// 连接关闭时的清理
if (connectionType == 'device') {
manager.removeDevice(channel);
} else if (connectionType == 'web') {
manager.removeWebClient(channel);
}
print('🔌 连接关闭');
}, onError: (error) {
print('连接错误: $error');
if (connectionType == 'device') {
manager.removeDevice(channel);
} else if (connectionType == 'web') {
manager.removeWebClient(channel);
}
});
});
return handler(request);
}
// CORS 中间件
Middleware createCorsMiddleware() {
return (Handler innerHandler) {
return (Request request) async {
if (request.method == 'OPTIONS') {
return Response.ok('', headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Requested-With',
'Access-Control-Allow-Credentials': 'true',
});
}
final response = await innerHandler(request);
return response.change(headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Requested-With',
'Access-Control-Allow-Credentials': 'true',
});
};
};
}
void main() async {
final manager = WebSocketManager();
Future<Response> router(Request request) async {
final path = request.url.path;
print('📨 请求: ${request.method} $path');
if (path == 'ws') {
return await handleWebSocket(request, manager);
}
if (path == 'users') {
return Response.ok(
jsonEncode(manager.getDeviceList()),
headers: {'Content-Type': 'application/json'},
);
}
return Response.notFound('Not Found');
}
final handler = const Pipeline()
.addMiddleware(logRequests())
.addMiddleware(createCorsMiddleware())
.addHandler(router);
//端口
final server = await io.serve(handler, '0.0.0.0', 8089);
print('\n🚀 ========================================');
print('✅ 服务器启动成功!');
print('🌐 HTTP 地址: http://${server.address.host}:${server.port}');
print('🔌 WebSocket 地址: ws://${server.address.host}:${server.port}/ws');
print('📋 用户列表 API: http://${server.address.host}:${server.port}/users');
print('========================================\n');
}