Files
planet/frontend/public/earth/js/motion-agent-provider.js
2026-05-10 22:06:01 +08:00

109 lines
2.6 KiB
JavaScript

const DEFAULT_RECONNECT_MS = 1800;
export const DEFAULT_AGENT_URL = "ws://127.0.0.1:8765/ws/gestures";
export function createMotionAgentProvider(options = {}) {
const {
url = DEFAULT_AGENT_URL,
reconnectMs = DEFAULT_RECONNECT_MS,
WebSocketCtor = typeof WebSocket !== "undefined" ? WebSocket : null,
onMessage = () => {},
onState = () => {},
onStatus = () => {},
} = options;
let socket = null;
let reconnectTimer = null;
let disposed = false;
let connected = false;
function emitState(detail = {}) {
onState({
provider: "motion_agent",
connected,
url,
...detail,
});
}
function clearReconnect() {
if (!reconnectTimer) return;
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
function scheduleReconnect() {
if (disposed || reconnectTimer) return;
reconnectTimer = setTimeout(() => {
reconnectTimer = null;
connect();
}, reconnectMs);
}
function closeSocket() {
if (!socket) return;
const current = socket;
socket = null;
current.onopen = null;
current.onmessage = null;
current.onerror = null;
current.onclose = null;
try {
current.close();
} catch (_error) {
// Browser WebSocket close can throw during teardown in older engines.
}
}
function connect() {
if (disposed || socket) return;
if (!WebSocketCtor) {
emitState({ error: "websocket_unavailable", message: "当前浏览器不支持 WebSocket" });
return;
}
try {
socket = new WebSocketCtor(url);
} catch (_error) {
emitState({ error: "socket_create_failed", message: "无法创建 Motion Agent 连接" });
scheduleReconnect();
return;
}
socket.onopen = () => {
connected = true;
emitState({ connected: true });
onStatus("动捕 Agent 已连接", "info");
};
socket.onmessage = (rawMessage) => onMessage(rawMessage?.data ?? rawMessage);
socket.onerror = () => {
emitState({ connected: false, error: "socket_error", message: "Motion Agent 连接异常" });
};
socket.onclose = () => {
connected = false;
socket = null;
emitState({ connected: false, message: "Motion Agent 未连接" });
scheduleReconnect();
};
}
return {
provider: "motion_agent",
start() {
if (disposed) return false;
connect();
return true;
},
stop() {
disposed = true;
clearReconnect();
closeSocket();
connected = false;
emitState({ connected: false });
},
isConnected() {
return connected;
},
};
}