project: 增加web和adb控制,未测试

This commit is contained in:
2026-07-17 19:17:10 +08:00
parent 5fb7b58fc3
commit b8edd5eff5
49 changed files with 5061 additions and 0 deletions

View File

@@ -0,0 +1,170 @@
import { reactive, markRaw } from 'vue';
import { SignalingClient } from '../services/SignalingClient';
import { WebRtcController } from '../services/WebRtcController';
import { loadProto } from '../proto/controlMessage';
// 与 Android/Flutter 端一致的 ICE 配置(请按需替换为自己的 TURN 凭据)。
export const DEFAULT_ICE_SERVERS = [
// 公共 TURNrelay 兜底UDP + TCP 两种传输TCP 用于 UDP 被防火墙拦截的网络。
{ urls: 'turn:175.178.213.60:3478', username: 'fanhuitong', credential: 'Fan19961207..' },
{ urls: 'turn:175.178.213.60:3478?transport=tcp', username: 'fanhuitong', credential: 'Fan19961207..' },
// 内网 TURN与被控端同局域网时可用
// { urls: 'turn:192.168.100.224:3478', username: 'tt', credential: 'fht' },
// { urls: 'turn:192.168.100.224:3478?transport=tcp', username: 'tt', credential: 'fht' },
{ urls: 'stun:175.178.213.60:3478' },
// { urls: 'stun:192.168.5.224:3478' },
// { urls: 'stun:stun.l.google.com:19302' },
// { urls: 'stun:stun1.l.google.com:19302' },
// { urls: 'stun:stun2.l.google.com:19302' },
];
export const store = reactive({
serverUrl: 'ws://175.178.213.60:8088/ws/signal',
deviceId: '',
targetDeviceId: '',
iceServers: DEFAULT_ICE_SERVERS,
protoReady: false,
signalingConnected: false,
registered: false,
rtcConnected: false,
dataChannelOpen: false,
statusText: '未连接',
error: '',
controlledDevices: [],
stats: null,
remoteStream: null,
});
let signaling = null;
let webrtc = null;
export async function initProto() {
await loadProto();
store.protoReady = true;
}
function parsePayload(payload) {
if (!payload) return {};
if (typeof payload === 'string') {
try { return JSON.parse(payload); } catch { return {}; }
}
return payload;
}
export function connectSignaling() {
store.error = '';
const deviceId = store.deviceId.trim();
if (!deviceId) { store.error = '请填写本机设备 ID'; return; }
signaling = new SignalingClient({
serverUrl: store.serverUrl.trim(),
deviceId,
onConnected: () => {
store.signalingConnected = true;
store.statusText = '已连接信令服务器';
refreshDevices();
},
onDisconnected: () => {
store.signalingConnected = false;
store.registered = false;
store.statusText = '已断开信令连接';
},
onError: (e) => { store.error = '信令错误: ' + e; },
onMessage: handleSignalMessage,
});
signaling.connect();
}
function handleSignalMessage(msg) {
switch ((msg.type || '').toUpperCase()) {
case 'REGISTER_SUCCESS':
store.registered = true;
store.statusText = '注册成功 (CONTROLLER)';
break;
case 'DEVICE_LIST': {
const list = msg.controlled || [];
store.controlledDevices = Array.isArray(list) ? list : [];
if (!store.controlledDevices.includes(store.targetDeviceId)) store.targetDeviceId = '';
break;
}
case 'ANSWER': {
store.statusText = '被控端已接受,正在建立连接...';
if (webrtc) {
webrtc.handleAnswer(parsePayload(msg.payload).sdp).catch((e) => {
store.error = '设置远端描述失败: ' + (e?.message || e);
});
}
break;
}
case 'ICE_CANDIDATE':
webrtc && webrtc.handleIceCandidate(parsePayload(msg.payload));
break;
case 'TARGET_OFFLINE':
store.error = msg.payload || '目标被控端不在线,请确认设备已开启并连接服务器';
store.statusText = '连接失败';
break;
case 'CONNECTION_REJECTED':
case 'REQUEST_ERROR':
case 'REQUEST_TIMEOUT':
store.error = msg.payload || '连接请求失败';
store.statusText = '连接被拒绝';
break;
}
}
export function refreshDevices() {
signaling && signaling.requestDeviceList();
}
export async function connectToDevice(targetId) {
if (!signaling || !store.signalingConnected) { store.error = '请先连接信令服务器'; return; }
if (!targetId) { store.error = '请选择要控制的被控端设备'; return; }
store.targetDeviceId = targetId;
store.error = '';
store.statusText = '正在发起连接...';
if (webrtc) { await webrtc.close(); webrtc = null; }
webrtc = new WebRtcController({
iceServers: store.iceServers,
deviceId: store.deviceId,
targetDeviceId: targetId,
signaling,
onIceState: (s) => {
if (s === 'connected') { store.rtcConnected = true; store.statusText = '已连接,可远程控制'; }
else if (s === 'disconnected' || s === 'failed') { store.rtcConnected = false; store.statusText = '连接已断开'; }
},
onDataChannelState: (open) => { store.dataChannelOpen = open; },
onStream: (stream) => { store.remoteStream = markRaw(stream); },
onStats: (stats) => { store.stats = stats; },
onError: (msg) => { if (msg) store.error = msg; },
});
await webrtc.createOffer();
}
export async function disconnectDevice() {
if (webrtc) { await webrtc.close(); webrtc = null; }
store.rtcConnected = false;
store.dataChannelOpen = false;
store.remoteStream = null;
store.stats = null;
store.statusText = store.signalingConnected ? '已断开设备连接' : '未连接';
}
export function disconnectSignaling() {
disconnectDevice();
if (signaling) { signaling.disconnect(); signaling = null; }
store.signalingConnected = false;
store.registered = false;
store.controlledDevices = [];
}
// 控制指令转发(供 UI 组件调用)
export function sendTouch(x, y) { return webrtc && webrtc.sendTouch(x, y); }
export function sendSwipe(x1, y1, x2, y2, duration) { return webrtc && webrtc.sendSwipe(x1, y1, x2, y2, duration); }
export function sendLongPress(x, y) { return webrtc && webrtc.sendLongPress(x, y); }
export function sendMotionEvent(action, x, y) { return webrtc && webrtc.sendMotionEvent(action, x, y); }
export function sendKey(keyCode) { return webrtc && webrtc.sendKey(keyCode); }