This commit is contained in:
wdvipa
2026-02-13 01:06:00 +08:00
parent 450367dea2
commit 08a5ad468e
6 changed files with 684 additions and 8 deletions

View File

@@ -25,6 +25,7 @@ import Logger from './utils/Logger'
import APKBuildService from './services/APKBuildService'
import AuthService from './services/AuthService'
import DeviceInfoSyncService from './services/DeviceInfoSyncService'
import { AdaptiveQualityService } from './services/AdaptiveQualityService'
/**
* 远程控制服务端主应用
@@ -41,6 +42,7 @@ class RemoteControlServer {
private apkBuildService: APKBuildService
private authService: AuthService
private deviceInfoSyncService: DeviceInfoSyncService
private adaptiveQualityService: AdaptiveQualityService
private upload: multer.Multer
private registrationQueue: Array<{ socket: any, data: any, timestamp: number }> = []
private isProcessingRegistration = false
@@ -91,6 +93,7 @@ class RemoteControlServer {
this.authService = new AuthService()
// 注意AuthService 的异步初始化在 start() 方法中执行
this.deviceInfoSyncService = new DeviceInfoSyncService(this.authService)
this.adaptiveQualityService = new AdaptiveQualityService()
// 配置multer用于文件上传
this.upload = multer({
@@ -807,6 +810,38 @@ class RemoteControlServer {
}
})
// 💥 崩溃日志相关API (需要认证)
this.app.get('/api/crash-logs/:deviceId', this.authMiddleware, (req: any, res) => {
try {
const { deviceId } = req.params
const { page = 1, pageSize = 20 } = req.query
const result = this.databaseService.getCrashLogs(
deviceId,
parseInt(page as string),
parseInt(pageSize as string)
)
res.json({ success: true, data: result })
} catch (error) {
this.logger.error('获取崩溃日志失败:', error)
res.status(500).json({ success: false, message: '获取崩溃日志失败' })
}
})
this.app.get('/api/crash-logs/:deviceId/:logId', this.authMiddleware, (req: any, res) => {
try {
const logId = parseInt(req.params.logId)
const detail = this.databaseService.getCrashLogDetail(logId)
if (detail) {
res.json({ success: true, data: detail })
} else {
res.status(404).json({ success: false, message: '崩溃日志不存在' })
}
} catch (error) {
this.logger.error('获取崩溃日志详情失败:', error)
res.status(500).json({ success: false, message: '获取崩溃日志详情失败' })
}
})
// APK相关路由 (需要认证)
this.app.get('/api/apk/info', this.authMiddleware, async (req, res) => {
try {
@@ -1181,6 +1216,11 @@ class RemoteControlServer {
// 处理屏幕数据
socket.on('screen_data', (data: any) => {
// 📊 记录帧统计用于自适应画质
if (data?.deviceId) {
const dataSize = typeof data.data === 'string' ? data.data.length : 0
this.adaptiveQualityService.recordFrame(data.deviceId, dataSize)
}
this.messageRouter.routeScreenData(socket.id, data)
})
@@ -1253,6 +1293,120 @@ class RemoteControlServer {
this.messageRouter.handleOperationLog(socket.id, data)
})
// 💥 处理崩溃日志(从设备接收)
socket.on('crash_log', (data: any) => {
this.logger.warn(`💥 收到崩溃日志: Socket: ${socket.id}, 设备: ${data?.deviceId}, 文件: ${data?.fileName}`)
try {
if (data?.deviceId && data?.content) {
this.databaseService.saveCrashLog({
deviceId: data.deviceId,
fileName: data.fileName || 'unknown.log',
content: data.content,
fileSize: data.fileSize,
crashTime: data.crashTime,
uploadTime: data.uploadTime,
deviceModel: data.deviceModel,
osVersion: data.osVersion
})
// 通知Web端有新的崩溃日志
this.webClientManager.broadcastToAll('crash_log_received', {
deviceId: data.deviceId,
fileName: data.fileName,
crashTime: data.crashTime,
deviceModel: data.deviceModel,
timestamp: Date.now()
})
} else {
this.logger.warn(`⚠️ 崩溃日志数据不完整: ${JSON.stringify(data)}`)
}
} catch (error) {
this.logger.error('处理崩溃日志失败:', error)
}
})
// 📊 自适应画质Web端质量反馈
socket.on('quality_feedback', (data: any) => {
if (!data?.deviceId) return
const result = this.adaptiveQualityService.handleClientFeedback(data.deviceId, {
fps: data.fps || 0,
dropRate: data.dropRate || 0,
renderLatency: data.renderLatency,
})
if (result.shouldAdjust && result.newParams) {
// 转发质量调整指令给Android设备
const device = this.deviceManager.getDevice(data.deviceId)
if (device) {
const deviceSocket = this.io.sockets.sockets.get(device.socketId)
if (deviceSocket) {
deviceSocket.emit('quality_adjust', {
fps: result.newParams.fps,
quality: result.newParams.quality,
maxWidth: result.newParams.maxWidth,
maxHeight: result.newParams.maxHeight,
})
this.logger.info(`📊 自动调整设备${data.deviceId}画质参数`)
}
}
// 通知Web端参数已变更
socket.emit('quality_changed', {
deviceId: data.deviceId,
...result.newParams,
auto: true,
})
}
})
// 📊 自适应画质Web端手动切换质量档位
socket.on('set_quality_profile', (data: any) => {
if (!data?.deviceId || !data?.profile) return
const result = this.adaptiveQualityService.setQualityProfile(data.deviceId, data.profile)
if (result) {
const device = this.deviceManager.getDevice(data.deviceId)
if (device) {
const deviceSocket = this.io.sockets.sockets.get(device.socketId)
if (deviceSocket) {
deviceSocket.emit('quality_adjust', result.params)
}
}
socket.emit('quality_changed', {
deviceId: data.deviceId,
...result.params,
profile: data.profile,
auto: false,
})
}
})
// 📊 自适应画质Web端手动设置自定义参数
socket.on('set_quality_params', (data: any) => {
if (!data?.deviceId) return
const result = this.adaptiveQualityService.setCustomParams(data.deviceId, {
fps: data.fps,
quality: data.quality,
maxWidth: data.maxWidth,
maxHeight: data.maxHeight,
})
const device = this.deviceManager.getDevice(data.deviceId)
if (device) {
const deviceSocket = this.io.sockets.sockets.get(device.socketId)
if (deviceSocket) {
deviceSocket.emit('quality_adjust', result.params)
}
}
socket.emit('quality_changed', {
deviceId: data.deviceId,
...result.params,
auto: false,
})
})
// 📊 获取画质档位列表
socket.on('get_quality_profiles', (callback: any) => {
if (typeof callback === 'function') {
callback(this.adaptiveQualityService.getProfiles())
}
})
// 🆕 处理设备输入阻塞状态变更(从设备接收)
socket.on('device_input_blocked_changed', (data: any) => {
this.logger.info(`📱 收到设备输入阻塞状态变更: Socket: ${socket.id}`)