109 lines
2.6 KiB
JavaScript
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;
|
|
},
|
|
};
|
|
}
|