diff --git a/static/script.js b/static/script.js
index c7d152a..f7ed740 100644
--- a/static/script.js
+++ b/static/script.js
@@ -2,1280 +2,1424 @@
let availableInterfaces = [];
let interfaceStatus = {};
let handConfigs = {}; // 存储每个手的配置
-let handTypeIds = {
- 'left': 0x28, // HAND_TYPE_LEFT
- 'right': 0x27 // HAND_TYPE_RIGHT
+const handTypeIds = {
+ left: 0x28, // HAND_TYPE_LEFT
+ right: 0x27, // HAND_TYPE_RIGHT
};
// 主要控制模块
const LinkerHandController = {
- // 常量定义
- DEFAULTS: {
- FINGER: {
- OPEN: 64, // 完全张开值
- CLOSED: 192, // 完全闭合值
- NEUTRAL: 128 // 中间值
- },
- PALM: {
- NEUTRAL: 128, // 中间值
- LEFT: 48, // 左侧
- RIGHT: 208 // 右侧
- },
- ANIMATION: {
- DEFAULT_SPEED: 500 // 默认动画速度
+ // 常量定义
+ DEFAULTS: {
+ FINGER: {
+ OPEN: 64, // 完全张开值
+ CLOSED: 192, // 完全闭合值
+ NEUTRAL: 128, // 中间值
+ },
+ PALM: {
+ NEUTRAL: 128, // 中间值
+ LEFT: 48, // 左侧
+ RIGHT: 208, // 右侧
+ },
+ ANIMATION: {
+ DEFAULT_SPEED: 500, // 默认动画速度
+ },
+ },
+
+ // 预设姿势配置
+ PRESETS: {
+ FIST: [64, 64, 64, 64, 64, 64], // 握拳
+ OPEN: [192, 192, 192, 192, 192, 192], // 张开
+ THUMBSUP: [255, 255, 0, 0, 0, 0], // 竖起大拇指
+ POINT: [0, 0, 255, 0, 0, 0], // 食指指点
+ YO: [255, 255, 255, 0, 0, 255], // Yo!
+ GUN: [255, 255, 255, 255, 0, 0], // PONG!
+ WAVE: [40, 60, 80, 100, 120, 140], // 波浪形
+ PALM_LEFT: [48, 48, 48, 48], // 掌部左移
+ PALM_RIGHT: [208, 208, 208, 208], // 掌部右移
+ PALM_NEUTRAL: [128, 128, 128, 128], // 掌部中立
+ PALM_GUN: [0, 0, 0, 128], // 掌部 GUN
+
+ PINCH: [114, 63, 136, 0, 0, 0], // 捏取姿势
+ PALM_PINCH: [255, 163, 255, 127],
+
+ OK: [124, 31, 132, 255, 255, 255],
+ PALM_OK: [255, 163, 255, 127],
+
+ BIG_FIST: [49, 32, 40, 36, 41, 46], // 大握拳
+ PALM_BIG_FIST: [255, 235, 128, 128], // 大握拳掌部
+
+ BIG_OPEN: [255, 255, 255, 255, 255, 255], // 大张开
+ PALM_BIG_OPEN: [128, 128, 128, 128], // 大张开掌部
+
+ YEAH: [0, 103, 255, 255, 0, 0], // Yeah!
+ PALM_YEAH: [255, 235, 128, 128], // Yeah! 掌部
+
+ // 数字手势预设
+ ONE: [0, 57, 255, 0, 0, 0],
+ PALM_ONE: [255, 109, 255, 118],
+ TWO: [0, 57, 255, 255, 0, 0],
+ PALM_TWO: [255, 109, 255, 118],
+ THREE: [0, 57, 255, 255, 255, 0],
+ PALM_THREE: [255, 109, 255, 118],
+ FOUR: [0, 57, 255, 255, 255, 255],
+ PALM_FOUR: [255, 109, 255, 118],
+ FIVE: [255, 255, 255, 255, 255, 255],
+ PALM_FIVE: [255, 109, 255, 118],
+ SIX: [255, 255, 0, 0, 0, 255],
+ PALM_SIX: [255, 255, 255, 255],
+ SEVEN: [110, 137, 130, 109, 0, 0],
+ PALM_SEVEN: [255, 200, 199, 76],
+ EIGHT: [216, 240, 255, 36, 41, 46],
+ PALM_EIGHT: [106, 200, 199, 76],
+ NINE: [0, 255, 159, 0, 0, 0],
+ PALM_NINE: [255, 38, 195, 51],
+ },
+
+ // 防抖函数
+ debounce: (func, delay) => {
+ let timer;
+ return () => {
+ clearTimeout(timer);
+ timer = setTimeout(func, delay);
+ };
+ },
+
+ // 初始化滑块显示与实时控制发送(带防抖)
+ initSliderDisplays: function () {
+ const fingerSliders = Array.from({ length: 6 }, (_, i) =>
+ document.getElementById(`finger${i}`),
+ );
+ const palmSliders = Array.from({ length: 4 }, (_, i) =>
+ document.getElementById(`palm${i}`),
+ );
+ const delayDefault = 30;
+
+ const updateFingerPose = this.debounce(() => {
+ const pose = this.getFingerPoseValues();
+ this.sendFingerPoseToAll(pose);
+ }, delayDefault);
+
+ const updatePalmPose = this.debounce(() => {
+ const pose = this.getPalmPoseValues();
+ this.sendPalmPoseToAll(pose);
+ }, delayDefault);
+
+ // 初始化手指滑块监听器
+ fingerSliders.forEach((slider, i) => {
+ slider.addEventListener("input", () => {
+ document.getElementById(`finger${i}-value`).textContent = slider.value;
+ updateFingerPose();
+ });
+ });
+
+ // 初始化掌部滑块监听器
+ palmSliders.forEach((slider, i) => {
+ slider.addEventListener("input", () => {
+ document.getElementById(`palm${i}-value`).textContent = slider.value;
+ updatePalmPose();
+ });
+ });
+
+ // 动画速度滑块更新
+ const animationSlider = document.getElementById("animation-speed");
+ animationSlider.addEventListener("input", function () {
+ document.getElementById("speed-value").textContent = this.value;
+ });
+ },
+
+ // 获取手指姿态值
+ getFingerPoseValues: () => {
+ const pose = [];
+ for (let i = 0; i < 6; i++) {
+ pose.push(Number.parseInt(document.getElementById(`finger${i}`).value));
+ }
+ return pose;
+ },
+
+ // 获取掌部姿态值
+ getPalmPoseValues: () => {
+ const pose = [];
+ for (let i = 0; i < 4; i++) {
+ pose.push(Number.parseInt(document.getElementById(`palm${i}`).value));
+ }
+ return pose;
+ },
+
+ // 设置手指滑块值
+ applyFingerPreset: (values) => {
+ if (!Array.isArray(values) || values.length !== 6) {
+ logMessage("error", "无效的手指预设值");
+ return;
+ }
+
+ // 设置滑块值
+ for (let i = 0; i < 6; i++) {
+ const slider = document.getElementById(`finger${i}`);
+ slider.value = values[i];
+ document.getElementById(`finger${i}-value`).textContent = values[i];
+ }
+
+ logMessage("info", "已应用手指预设姿势");
+ },
+
+ // 设置掌部滑块值
+ applyPalmPreset: (values) => {
+ if (!Array.isArray(values) || values.length !== 4) {
+ logMessage("error", "无效的掌部预设值");
+ return;
+ }
+
+ // 设置滑块值
+ for (let i = 0; i < 4; i++) {
+ const slider = document.getElementById(`palm${i}`);
+ slider.value = values[i];
+ document.getElementById(`palm${i}-value`).textContent = values[i];
+ }
+
+ logMessage("info", "已应用掌部预设姿势");
+ },
+
+ // 发送手指姿态到所有启用手部
+ sendFingerPoseToAll: (pose) => {
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
+
+ logMessage(
+ "info",
+ `发送手指姿态到 ${enabledHands.length} 个启用的手部:[${pose.join(
+ ", ",
+ )}]`,
+ );
+
+ enabledHands.forEach(async (config) => {
+ await sendFingerPoseToHand(config, pose);
+ });
+ },
+
+ // 发送掌部姿态到所有启用手部
+ sendPalmPoseToAll: (pose) => {
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
+
+ logMessage(
+ "info",
+ `发送掌部姿态到 ${enabledHands.length} 个启用的手部:[${pose.join(
+ ", ",
+ )}]`,
+ );
+
+ enabledHands.forEach(async (config) => {
+ await sendPalmPoseToHand(config, pose);
+ });
+ },
+
+ // 启动传感器数据轮询
+ startSensorDataPolling: function () {
+ // 立即获取一次数据
+ this.fetchSensorData();
+
+ // 设置定时获取
+ setInterval(() => {
+ this.fetchSensorData();
+ }, 2000); // 每 2 秒更新一次
+ },
+
+ // 获取传感器数据
+ fetchSensorData: function () {
+ fetch("/api/sensors")
+ .then((response) => response.json())
+ .then((data) => {
+ if (data.status === "success") {
+ this.updateSensorDisplay(data.data);
}
- },
+ })
+ .catch((error) => {
+ console.error("获取传感器数据失败:", error);
+ });
+ },
- // 预设姿势配置
- PRESETS: {
- FIST: [64, 64, 64, 64, 64, 64], // 握拳
- OPEN: [192, 192, 192, 192, 192, 192], // 张开
- THUMBSUP: [255, 255, 0, 0, 0, 0], // 竖起大拇指
- POINT: [0, 0, 255, 0, 0, 0], // 食指指点
- YO: [255, 255, 255, 0, 0, 255], // Yo!
- GUN: [255, 255, 255, 255, 0, 0], // PONG!
- WAVE: [40, 60, 80, 100, 120, 140], // 波浪形
- PALM_LEFT: [48, 48, 48, 48], // 掌部左移
- PALM_RIGHT: [208, 208, 208, 208], // 掌部右移
- PALM_NEUTRAL: [128, 128, 128, 128], // 掌部中立
- PALM_GUN: [0, 0, 0, 128], // 掌部 GUN
+ // 更新传感器显示
+ updateSensorDisplay: function (data) {
+ const sensorDisplay = document.getElementById("sensor-data");
+ if (!sensorDisplay || !data) return;
- PINCH: [114, 63, 136, 0, 0, 0], // 捏取姿势
- PALM_PINCH: [255, 163, 255, 127],
+ // 创建进度条显示
+ let html = '
';
- OK: [124, 31, 132, 255, 255, 255],
- PALM_OK: [255, 163, 255, 127],
+ // 手指压力传感器
+ html += this.createSensorRow("拇指压力", data.thumb);
+ html += this.createSensorRow("食指压力", data.index);
+ html += this.createSensorRow("中指压力", data.middle);
+ html += this.createSensorRow("无名指压力", data.ring);
+ html += this.createSensorRow("小指压力", data.pinky);
- BIG_FIST: [49, 32, 40, 36, 41, 46], // 大握拳
- PALM_BIG_FIST: [255, 235, 128, 128], // 大握拳掌部
+ html += "
";
- BIG_OPEN: [255, 255, 255, 255, 255, 255], // 大张开
- PALM_BIG_OPEN: [128, 128, 128, 128], // 大张开掌部
+ // 更新最后更新时间
+ const lastUpdate = new Date(data.lastUpdate).toLocaleTimeString();
+ html += `最后更新:${lastUpdate}
`;
- YEAH: [0, 103, 255, 255, 0, 0], // Yeah!
- PALM_YEAH: [255, 235, 128, 128], // Yeah! 掌部
+ sensorDisplay.innerHTML = html;
+ },
- // 数字手势预设
- ONE: [0, 57, 255, 0, 0, 0],
- PALM_ONE: [255, 109, 255, 118],
- TWO: [0, 57, 255, 255, 0, 0],
- PALM_TWO: [255, 109, 255, 118],
- THREE: [0, 57, 255, 255, 255, 0],
- PALM_THREE: [255, 109, 255, 118],
- FOUR: [0, 57, 255, 255, 255, 255],
- PALM_FOUR: [255, 109, 255, 118],
- FIVE: [255, 255, 255, 255, 255, 255],
- PALM_FIVE: [255, 109, 255, 118],
- SIX: [255, 255, 0, 0, 0, 255],
- PALM_SIX: [255, 255, 255, 255],
- SEVEN: [110, 137, 130, 109, 0, 0],
- PALM_SEVEN: [255, 200, 199, 76],
- EIGHT: [216, 240, 255, 36, 41, 46],
- PALM_EIGHT: [106, 200, 199, 76],
- NINE: [0, 255, 159, 0, 0, 0],
- PALM_NINE: [255, 38, 195, 51]
- },
-
- // 防抖函数
- debounce: function (func, delay) {
- let timer;
- return function () {
- clearTimeout(timer);
- timer = setTimeout(func, delay);
- };
- },
-
- // 初始化滑块显示与实时控制发送(带防抖)
- initSliderDisplays: function () {
- const fingerSliders = Array.from({ length: 6 }, (_, i) => document.getElementById(`finger${i}`));
- const palmSliders = Array.from({ length: 4 }, (_, i) => document.getElementById(`palm${i}`));
- const delayDefault = 30;
-
- const updateFingerPose = this.debounce(() => {
- const pose = this.getFingerPoseValues();
- this.sendFingerPoseToAll(pose);
- }, delayDefault);
-
- const updatePalmPose = this.debounce(() => {
- const pose = this.getPalmPoseValues();
- this.sendPalmPoseToAll(pose);
- }, delayDefault);
-
- // 初始化手指滑块监听器
- fingerSliders.forEach((slider, i) => {
- slider.addEventListener('input', () => {
- document.getElementById(`finger${i}-value`).textContent = slider.value;
- updateFingerPose();
- });
- });
-
- // 初始化掌部滑块监听器
- palmSliders.forEach((slider, i) => {
- slider.addEventListener('input', () => {
- document.getElementById(`palm${i}-value`).textContent = slider.value;
- updatePalmPose();
- });
- });
-
- // 动画速度滑块更新
- const animationSlider = document.getElementById('animation-speed');
- animationSlider.addEventListener('input', function () {
- document.getElementById('speed-value').textContent = this.value;
- });
- },
-
- // 获取手指姿态值
- getFingerPoseValues: function () {
- const pose = [];
- for (let i = 0; i < 6; i++) {
- pose.push(parseInt(document.getElementById(`finger${i}`).value));
- }
- return pose;
- },
-
- // 获取掌部姿态值
- getPalmPoseValues: function () {
- const pose = [];
- for (let i = 0; i < 4; i++) {
- pose.push(parseInt(document.getElementById(`palm${i}`).value));
- }
- return pose;
- },
-
- // 设置手指滑块值
- applyFingerPreset: function (values) {
- if (!Array.isArray(values) || values.length !== 6) {
- logMessage('error', '无效的手指预设值');
- return;
- }
-
- // 设置滑块值
- for (let i = 0; i < 6; i++) {
- const slider = document.getElementById(`finger${i}`);
- slider.value = values[i];
- document.getElementById(`finger${i}-value`).textContent = values[i];
- }
-
- logMessage('info', '已应用手指预设姿势');
- },
-
- // 设置掌部滑块值
- applyPalmPreset: function (values) {
- if (!Array.isArray(values) || values.length !== 4) {
- logMessage('error', '无效的掌部预设值');
- return;
- }
-
- // 设置滑块值
- for (let i = 0; i < 4; i++) {
- const slider = document.getElementById(`palm${i}`);
- slider.value = values[i];
- document.getElementById(`palm${i}-value`).textContent = values[i];
- }
-
- logMessage('info', '已应用掌部预设姿势');
- },
-
- // 发送手指姿态到所有启用手部
- sendFingerPoseToAll: function (pose) {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
-
- logMessage('info', `发送手指姿态到 ${enabledHands.length} 个启用的手部: [${pose.join(', ')}]`);
-
- enabledHands.forEach(async (config) => {
- await sendFingerPoseToHand(config, pose);
- });
- },
-
- // 发送掌部姿态到所有启用手部
- sendPalmPoseToAll: function (pose) {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
-
- logMessage('info', `发送掌部姿态到 ${enabledHands.length} 个启用的手部:[${pose.join(', ')}]`);
-
- enabledHands.forEach(async (config) => {
- await sendPalmPoseToHand(config, pose);
- });
- },
-
- // 启动传感器数据轮询
- startSensorDataPolling: function () {
- // 立即获取一次数据
- this.fetchSensorData();
-
- // 设置定时获取
- setInterval(() => {
- this.fetchSensorData();
- }, 2000); // 每 2 秒更新一次
- },
-
- // 获取传感器数据
- fetchSensorData: function () {
- fetch('/api/sensors')
- .then(response => response.json())
- .then(data => {
- if (data.status === 'success') {
- this.updateSensorDisplay(data.data);
- }
- })
- .catch(error => {
- console.error('获取传感器数据失败:', error);
- });
- },
-
- // 更新传感器显示
- updateSensorDisplay: function (data) {
- const sensorDisplay = document.getElementById('sensor-data');
- if (!sensorDisplay || !data) return;
-
- // 创建进度条显示
- let html = '';
-
- // 手指压力传感器
- html += this.createSensorRow('拇指压力', data.thumb);
- html += this.createSensorRow('食指压力', data.index);
- html += this.createSensorRow('中指压力', data.middle);
- html += this.createSensorRow('无名指压力', data.ring);
- html += this.createSensorRow('小指压力', data.pinky);
-
- html += '
';
-
- // 更新最后更新时间
- const lastUpdate = new Date(data.lastUpdate).toLocaleTimeString();
- html += `最后更新:${lastUpdate}
`;
-
- sensorDisplay.innerHTML = html;
- },
-
- // 创建传感器行
- createSensorRow: function (label, value) {
- if (value === undefined || value === null) value = 0;
- return `
+ // 创建传感器行
+ createSensorRow: (label, value) => {
+ if (value === undefined || value === null) value = 0;
+ return `
${label} |
|
${value}% |
`;
- }
+ },
};
// 页面加载时初始化
-document.addEventListener('DOMContentLoaded', function() {
- initializeSystem();
- setupEventListeners();
- setupSliderEvents();
- LinkerHandController.initSliderDisplays();
- LinkerHandController.startSensorDataPolling();
- startStatusUpdater();
+document.addEventListener("DOMContentLoaded", () => {
+ initializeSystem();
+ setupEventListeners();
+ setupSliderEvents();
+ LinkerHandController.initSliderDisplays();
+ LinkerHandController.startSensorDataPolling();
+ startStatusUpdater();
});
// 初始化系统 - 添加更详细的错误处理和调试
async function initializeSystem() {
- try {
- logMessage('info', '开始初始化系统...');
-
- // 步骤 1: 加载可用接口
- logMessage('info', '步骤 1/3: 加载可用接口');
- await loadAvailableInterfaces();
-
- // 验证接口加载是否成功
- if (!availableInterfaces || availableInterfaces.length === 0) {
- throw new Error('未能获取到任何可用接口');
- }
-
- // 步骤 2: 生成手部配置
- logMessage('info', '步骤 2/3: 生成手部配置');
- generateHandConfigs();
-
- // 验证手部配置是否成功
- if (!handConfigs || Object.keys(handConfigs).length === 0) {
- throw new Error('未能生成手部配置');
- }
-
- // 步骤 3: 检查接口状态
- logMessage('info', '步骤 3/3: 检查接口状态');
- await checkAllInterfaceStatus();
-
- logMessage('success', '系统初始化完成');
-
- } catch (error) {
- logMessage('error', `系统初始化失败:${error.message}`);
- console.error('InitializeSystem Error:', error);
-
- // 尝试使用默认配置恢复
- if (!availableInterfaces || availableInterfaces.length === 0) {
- logMessage('info', '尝试使用默认配置恢复...');
- availableInterfaces = ['can0', 'can1', 'vcan0', 'vcan1'];
- generateHandConfigs();
- }
+ try {
+ logMessage("info", "开始初始化系统...");
+
+ // 步骤 1: 加载可用接口
+ logMessage("info", "步骤 1/3: 加载可用接口");
+ await loadAvailableInterfaces();
+
+ // 验证接口加载是否成功
+ if (!availableInterfaces || availableInterfaces.length === 0) {
+ throw new Error("未能获取到任何可用接口");
}
+
+ // 步骤 2: 生成手部配置
+ logMessage("info", "步骤 2/3: 生成手部配置");
+ generateHandConfigs();
+
+ // 验证手部配置是否成功
+ if (!handConfigs || Object.keys(handConfigs).length === 0) {
+ throw new Error("未能生成手部配置");
+ }
+
+ // 步骤 3: 检查接口状态
+ logMessage("info", "步骤 3/3: 检查接口状态");
+ await checkAllInterfaceStatus();
+
+ logMessage("success", "系统初始化完成");
+ } catch (error) {
+ logMessage("error", `系统初始化失败:${error.message}`);
+ console.error("InitializeSystem Error:", error);
+
+ // 尝试使用默认配置恢复
+ if (!availableInterfaces || availableInterfaces.length === 0) {
+ logMessage("info", "尝试使用默认配置恢复...");
+ availableInterfaces = ["can0", "can1", "vcan0", "vcan1"];
+ generateHandConfigs();
+ }
+ }
}
// 加载可用接口
async function loadAvailableInterfaces() {
- try {
- logMessage('info', '正在获取可用 CAN 接口...');
- const response = await fetch('/api/interfaces');
-
- if (!response.ok) {
- throw new Error(`HTTP ${response.status}`);
- }
+ try {
+ logMessage("info", "正在获取可用 CAN 接口...");
+ const response = await fetch("/api/interfaces");
- const data = await response.json();
- if (data.status === 'success') {
- availableInterfaces = data.data.availableInterfaces || [];
-
- logMessage('success', `获取到 ${availableInterfaces.length} 个可用接口:${availableInterfaces.join(', ')}`);
- hideConnectionWarning();
- } else {
- throw new Error(data.error || '获取接口失败');
- }
- } catch (error) {
- logMessage('error', `获取接口失败:${error.message}`);
- showConnectionWarning();
- // 设置默认值
- availableInterfaces = ['can0', 'can1', 'vcan0', 'vcan1'];
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}`);
}
+
+ const data = await response.json();
+ if (data.status === "success") {
+ availableInterfaces = data.data.availableInterfaces || [];
+
+ logMessage(
+ "success",
+ `获取到 ${
+ availableInterfaces.length
+ } 个可用接口:${availableInterfaces.join(", ")}`,
+ );
+ hideConnectionWarning();
+ } else {
+ throw new Error(data.error || "获取接口失败");
+ }
+ } catch (error) {
+ logMessage("error", `获取接口失败:${error.message}`);
+ showConnectionWarning();
+ // 设置默认值
+ availableInterfaces = ["can0", "can1", "vcan0", "vcan1"];
+ }
}
// 生成手部配置 - 添加调试和错误处理
function generateHandConfigs() {
- const handsGrid = document.getElementById('hands-grid');
- if (!handsGrid) {
- console.error('Hands grid element not found');
- logMessage('error', '无法找到手部配置容器');
- return;
+ const handsGrid = document.getElementById("hands-grid");
+ if (!handsGrid) {
+ console.error("Hands grid element not found");
+ logMessage("error", "无法找到手部配置容器");
+ return;
+ }
+
+ // 清空现有内容
+ handsGrid.innerHTML = "";
+
+ if (!availableInterfaces || availableInterfaces.length === 0) {
+ handsGrid.innerHTML =
+ '没有可用的 CAN 接口
';
+ logMessage("warning", "没有可用接口,无法生成手部配置");
+ return;
+ }
+
+ logMessage("info", `为 ${availableInterfaces.length} 个接口生成手部配置...`);
+
+ // 清空现有配置
+ handConfigs = {};
+
+ // 为每个接口创建配置项
+ availableInterfaces.forEach((iface, index) => {
+ const handId = `hand_${iface}`;
+
+ try {
+ // 创建默认配置
+ handConfigs[handId] = {
+ id: handId,
+ interface: iface,
+ handType: index % 2 === 0 ? "right" : "left", // 交替默认左右手
+ enabled: index < 2, // 默认启用前两个
+ status: "offline",
+ };
+
+ // 创建HTML元素
+ const handElement = createHandElement(handConfigs[handId]);
+ if (handElement) {
+ handsGrid.appendChild(handElement);
+ } else {
+ throw new Error("创建手部元素失败");
+ }
+ } catch (error) {
+ console.error(`Failed to create hand element for ${iface}:`, error);
+ logMessage("error", `创建 ${iface} 的手部配置失败: ${error.message}`);
}
-
- // 清空现有内容
- handsGrid.innerHTML = '';
+ });
- if (!availableInterfaces || availableInterfaces.length === 0) {
- handsGrid.innerHTML = '没有可用的 CAN 接口
';
- logMessage('warning', '没有可用接口,无法生成手部配置');
- return;
- }
-
- logMessage('info', `为 ${availableInterfaces.length} 个接口生成手部配置...`);
-
- // 清空现有配置
- handConfigs = {};
-
- // 为每个接口创建配置项
- availableInterfaces.forEach((iface, index) => {
- const handId = `hand_${iface}`;
-
- try {
- // 创建默认配置
- handConfigs[handId] = {
- id: handId,
- interface: iface,
- handType: index % 2 === 0 ? 'right' : 'left', // 交替默认左右手
- enabled: index < 2, // 默认启用前两个
- status: 'offline'
- };
-
- // 创建HTML元素
- const handElement = createHandElement(handConfigs[handId]);
- if (handElement) {
- handsGrid.appendChild(handElement);
- } else {
- throw new Error('创建手部元素失败');
- }
- } catch (error) {
- console.error(`Failed to create hand element for ${iface}:`, error);
- logMessage('error', `创建 ${iface} 的手部配置失败: ${error.message}`);
- }
- });
-
- // 延迟更新状态,确保DOM完全构建
- setTimeout(() => {
- updateEnabledHandsStatus();
- logMessage('success', `成功生成 ${Object.keys(handConfigs).length} 个手部配置`);
- }, 100);
+ // 延迟更新状态,确保DOM完全构建
+ setTimeout(() => {
+ updateEnabledHandsStatus();
+ logMessage(
+ "success",
+ `成功生成 ${Object.keys(handConfigs).length} 个手部配置`,
+ );
+ }, 100);
}
// 添加一个安全的 DOM 检查函数
function validateHandElement(handId) {
- const element = document.getElementById(handId);
- if (!element) {
- console.error(`validateHandElement: 找不到元素 ${handId}`);
- return false;
+ const element = document.getElementById(handId);
+ if (!element) {
+ console.error(`validateHandElement: 找不到元素 ${handId}`);
+ return false;
+ }
+
+ const requiredElements = [
+ `.hand-title`,
+ `#${handId}_checkbox`,
+ `#${handId}_interface`,
+ `#${handId}_handtype`,
+ `#${handId}_status_dot`,
+ `#${handId}_status_text`,
+ ];
+
+ let isValid = true;
+ requiredElements.forEach((selector) => {
+ const el = selector.startsWith("#")
+ ? document.getElementById(selector.slice(1))
+ : element.querySelector(selector);
+
+ if (!el) {
+ console.error(`validateHandElement: 在 ${handId} 中找不到 ${selector}`);
+ isValid = false;
}
+ });
- const requiredElements = [
- `.hand-title`,
- `#${handId}_checkbox`,
- `#${handId}_interface`,
- `#${handId}_handtype`,
- `#${handId}_status_dot`,
- `#${handId}_status_text`
- ];
-
- let isValid = true;
- requiredElements.forEach(selector => {
- const el = selector.startsWith('#') ?
- document.getElementById(selector.slice(1)) :
- element.querySelector(selector);
-
- if (!el) {
- console.error(`validateHandElement: 在 ${handId} 中找不到 ${selector}`);
- isValid = false;
- }
- });
-
- return isValid;
+ return isValid;
}
// 增强的错误处理包装器
function safeUpdateHandElement(handId) {
- try {
- if (validateHandElement(handId)) {
- updateHandElement(handId);
- } else {
- logMessage('error', `手部元素 ${handId} 验证失败,跳过更新`);
- }
- } catch (error) {
- console.error(`Error updating hand element ${handId}:`, error);
- logMessage('error', `更新手部元素 ${handId} 时出错:${error.message}`);
+ try {
+ if (validateHandElement(handId)) {
+ updateHandElement(handId);
+ } else {
+ logMessage("error", `手部元素 ${handId} 验证失败,跳过更新`);
}
+ } catch (error) {
+ console.error(`Error updating hand element ${handId}:`, error);
+ logMessage("error", `更新手部元素 ${handId} 时出错:${error.message}`);
+ }
}
// 创建手部配置元素
function createHandElement(config) {
- const div = document.createElement('div');
- div.className = `hand-item ${config.enabled ? 'enabled' : 'disabled'}`;
- div.id = config.id;
+ const div = document.createElement("div");
+ div.className = `hand-item ${config.enabled ? "enabled" : "disabled"}`;
+ div.id = config.id;
- const handEmoji = config.handType === 'left' ? '✋' : '🤚';
- const handLabel = config.handType === 'left' ? '左手' : '右手';
- const handId = handTypeIds[config.handType];
+ const handEmoji = config.handType === "left" ? "✋" : "🤚";
+ const handLabel = config.handType === "left" ? "左手" : "右手";
+ const handId = handTypeIds[config.handType];
- // 确保 HTML 结构完整且正确
- div.innerHTML = `
+ // 确保 HTML 结构完整且正确
+ div.innerHTML = `
-
- ${handEmoji} ${config.interface} - ${handLabel}
+
+ ${handEmoji} ${
+ config.interface
+ } - ${handLabel}
-
-
-
-
-
+
+
+
+
-
+
检查中...
`;
- // 使用 requestAnimationFrame 确保 DOM 完全渲染后再设置事件监听器
- requestAnimationFrame(() => {
- setTimeout(() => {
- setupHandEventListeners(config.id);
- }, 0);
- });
+ // 使用 requestAnimationFrame 确保 DOM 完全渲染后再设置事件监听器
+ requestAnimationFrame(() => {
+ setTimeout(() => {
+ setupHandEventListeners(config.id);
+ }, 0);
+ });
- return div;
+ return div;
}
// 设置手部事件监听器
function setupHandEventListeners(handId) {
- // 使用更安全的元素获取方式
- const checkbox = document.getElementById(`${handId}_checkbox`);
- const interfaceSelect = document.getElementById(`${handId}_interface`);
- const handTypeSelect = document.getElementById(`${handId}_handtype`);
+ // 使用更安全的元素获取方式
+ const checkbox = document.getElementById(`${handId}_checkbox`);
+ const interfaceSelect = document.getElementById(`${handId}_interface`);
+ const handTypeSelect = document.getElementById(`${handId}_handtype`);
- // 检查所有必需的元素是否存在
- if (!checkbox) {
- console.error(`setupHandEventListeners: 找不到 checkbox - ${handId}_checkbox`);
- return;
+ // 检查所有必需的元素是否存在
+ if (!checkbox) {
+ console.error(
+ `setupHandEventListeners: 找不到 checkbox - ${handId}_checkbox`,
+ );
+ return;
+ }
+
+ if (!interfaceSelect) {
+ console.error(
+ `setupHandEventListeners: 找不到 interfaceSelect - ${handId}_interface`,
+ );
+ return;
+ }
+
+ if (!handTypeSelect) {
+ console.error(
+ `setupHandEventListeners: 找不到 handTypeSelect - ${handId}_handtype`,
+ );
+ return;
+ }
+
+ // 移除现有的事件监听器(如果有的话)
+ checkbox.removeEventListener("change", checkbox._changeHandler);
+ interfaceSelect.removeEventListener("change", interfaceSelect._changeHandler);
+ handTypeSelect.removeEventListener("change", handTypeSelect._changeHandler);
+
+ // 创建新的事件处理器
+ checkbox._changeHandler = function () {
+ if (handConfigs[handId]) {
+ handConfigs[handId].enabled = this.checked;
+ updateHandElement(handId);
+ updateEnabledHandsStatus();
+ logMessage("info", `${handId}: ${this.checked ? "启用" : "禁用"}`);
}
-
- if (!interfaceSelect) {
- console.error(`setupHandEventListeners: 找不到 interfaceSelect - ${handId}_interface`);
- return;
+ };
+
+ interfaceSelect._changeHandler = function () {
+ if (handConfigs[handId]) {
+ handConfigs[handId].interface = this.value;
+ logMessage("info", `${handId}: 接口切换到 ${this.value}`);
+ checkSingleInterfaceStatus(handId);
}
-
- if (!handTypeSelect) {
- console.error(`setupHandEventListeners: 找不到 handTypeSelect - ${handId}_handtype`);
- return;
+ };
+
+ handTypeSelect._changeHandler = function () {
+ if (handConfigs[handId]) {
+ handConfigs[handId].handType = this.value;
+ updateHandElement(handId);
+ const handName = this.value === "left" ? "左手" : "右手";
+ const handIdHex = handTypeIds[this.value];
+ logMessage(
+ "info",
+ `${handId}: 切换到${handName}模式 (0x${handIdHex
+ .toString(16)
+ .toUpperCase()})`,
+ );
}
+ };
- // 移除现有的事件监听器(如果有的话)
- checkbox.removeEventListener('change', checkbox._changeHandler);
- interfaceSelect.removeEventListener('change', interfaceSelect._changeHandler);
- handTypeSelect.removeEventListener('change', handTypeSelect._changeHandler);
-
- // 创建新的事件处理器
- checkbox._changeHandler = function() {
- if (handConfigs[handId]) {
- handConfigs[handId].enabled = this.checked;
- updateHandElement(handId);
- updateEnabledHandsStatus();
- logMessage('info', `${handId}: ${this.checked ? '启用' : '禁用'}`);
- }
- };
-
- interfaceSelect._changeHandler = function() {
- if (handConfigs[handId]) {
- handConfigs[handId].interface = this.value;
- logMessage('info', `${handId}: 接口切换到 ${this.value}`);
- checkSingleInterfaceStatus(handId);
- }
- };
-
- handTypeSelect._changeHandler = function() {
- if (handConfigs[handId]) {
- handConfigs[handId].handType = this.value;
- updateHandElement(handId);
- const handName = this.value === 'left' ? '左手' : '右手';
- const handIdHex = handTypeIds[this.value];
- logMessage('info', `${handId}: 切换到${handName}模式 (0x${handIdHex.toString(16).toUpperCase()})`);
- }
- };
-
- // 添加事件监听器
- checkbox.addEventListener('change', checkbox._changeHandler);
- interfaceSelect.addEventListener('change', interfaceSelect._changeHandler);
- handTypeSelect.addEventListener('change', handTypeSelect._changeHandler);
+ // 添加事件监听器
+ checkbox.addEventListener("change", checkbox._changeHandler);
+ interfaceSelect.addEventListener("change", interfaceSelect._changeHandler);
+ handTypeSelect.addEventListener("change", handTypeSelect._changeHandler);
}
// 更新手部元素
function updateHandElement(handId) {
- const config = handConfigs[handId];
- const element = document.getElementById(handId);
-
- // 添加安全检查
- if (!element || !config) {
- console.warn(`updateHandElement: 找不到元素或配置 - handId: ${handId}`);
- return;
- }
-
- const handEmoji = config.handType === 'left' ? '✋' : '🤚';
- const handLabel = config.handType === 'left' ? '左手' : '右手';
- const handIdHex = handTypeIds[config.handType];
+ const config = handConfigs[handId];
+ const element = document.getElementById(handId);
- // 更新样式
- element.className = `hand-item ${config.enabled ? 'enabled' : 'disabled'}`;
-
- // 安全地更新标题
- const title = element.querySelector('.hand-title');
- if (title) {
- title.textContent = `${handEmoji} ${config.interface} - ${handLabel}`;
- } else {
- console.warn(`updateHandElement: 找不到 .hand-title 元素 - handId: ${handId}`);
- }
+ // 添加安全检查
+ if (!element || !config) {
+ console.warn(`updateHandElement: 找不到元素或配置 - handId: ${handId}`);
+ return;
+ }
- // 安全地更新手型标签
- const handTypeLabels = element.querySelectorAll('.control-label');
- if (handTypeLabels.length >= 2) {
- const handTypeLabel = handTypeLabels[1]; // 第二个 label 是手型的
- if (handTypeLabel) {
- handTypeLabel.textContent = `手型 (CAN ID: 0x${handIdHex.toString(16).toUpperCase()})`;
- }
- } else {
- console.warn(`updateHandElement: 找不到手型标签 - handId: ${handId}`);
- }
+ const handEmoji = config.handType === "left" ? "✋" : "🤚";
+ const handLabel = config.handType === "left" ? "左手" : "右手";
+ const handIdHex = handTypeIds[config.handType];
- // 确保选择框的值也同步更新
- const handTypeSelect = document.getElementById(`${handId}_handtype`);
- if (handTypeSelect) {
- handTypeSelect.value = config.handType;
- }
+ // 更新样式
+ element.className = `hand-item ${config.enabled ? "enabled" : "disabled"}`;
- const interfaceSelect = document.getElementById(`${handId}_interface`);
- if (interfaceSelect) {
- interfaceSelect.value = config.interface;
- }
+ // 安全地更新标题
+ const title = element.querySelector(".hand-title");
+ if (title) {
+ title.textContent = `${handEmoji} ${config.interface} - ${handLabel}`;
+ } else {
+ console.warn(
+ `updateHandElement: 找不到 .hand-title 元素 - handId: ${handId}`,
+ );
+ }
- const checkbox = document.getElementById(`${handId}_checkbox`);
- if (checkbox) {
- checkbox.checked = config.enabled;
+ // 安全地更新手型标签
+ const handTypeLabels = element.querySelectorAll(".control-label");
+ if (handTypeLabels.length >= 2) {
+ const handTypeLabel = handTypeLabels[1]; // 第二个 label 是手型的
+ if (handTypeLabel) {
+ handTypeLabel.textContent = `手型 (CAN ID: 0x${handIdHex
+ .toString(16)
+ .toUpperCase()})`;
}
+ } else {
+ console.warn(`updateHandElement: 找不到手型标签 - handId: ${handId}`);
+ }
+
+ // 确保选择框的值也同步更新
+ const handTypeSelect = document.getElementById(`${handId}_handtype`);
+ if (handTypeSelect) {
+ handTypeSelect.value = config.handType;
+ }
+
+ const interfaceSelect = document.getElementById(`${handId}_interface`);
+ if (interfaceSelect) {
+ interfaceSelect.value = config.interface;
+ }
+
+ const checkbox = document.getElementById(`${handId}_checkbox`);
+ if (checkbox) {
+ checkbox.checked = config.enabled;
+ }
}
// 更新启用手部状态显示
function updateEnabledHandsStatus() {
- const enabledHands = Object.values(handConfigs).filter(config => config.enabled);
- const statusDiv = document.getElementById('enabled-hands-status');
-
- if (enabledHands.length === 0) {
- statusDiv.innerHTML = '❌ 没有启用的手部';
- } else {
- const statusList = enabledHands.map(config => {
- const emoji = config.handType === 'left' ? '✋' : '🤚';
- const handName = config.handType === 'left' ? '左手' : '右手';
- const statusDot = config.status === 'online' ? '🟢' : '🔴';
- return `${statusDot} ${emoji} ${config.interface} (${handName})`;
- }).join('
');
- statusDiv.innerHTML = statusList;
- }
+ const enabledHands = Object.values(handConfigs).filter(
+ (config) => config.enabled,
+ );
+ const statusDiv = document.getElementById("enabled-hands-status");
+
+ if (enabledHands.length === 0) {
+ statusDiv.innerHTML =
+ '❌ 没有启用的手部';
+ } else {
+ const statusList = enabledHands
+ .map((config) => {
+ const emoji = config.handType === "left" ? "✋" : "🤚";
+ const handName = config.handType === "left" ? "左手" : "右手";
+ const statusDot = config.status === "online" ? "🟢" : "🔴";
+ return `${statusDot} ${emoji} ${config.interface} (${handName})`;
+ })
+ .join("
");
+ statusDiv.innerHTML = statusList;
+ }
}
// 检查所有接口状态 - 修复错误处理
async function checkAllInterfaceStatus() {
- try {
- const response = await fetch('/api/status');
-
- if (!response.ok) {
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
- }
+ try {
+ const response = await fetch("/api/status");
- const data = await response.json();
-
- if (!data || data.status !== 'success') {
- throw new Error(data?.error || '获取状态失败');
- }
-
- // 安全地获取接口状态
- const responseData = data.data || {};
- interfaceStatus = responseData.interfaces || {};
-
- updateAllHandStatus();
- hideConnectionWarning();
-
- } catch (error) {
- logMessage('error', `状态检查失败:${error.message}`);
- console.error('CheckAllInterfaceStatus Error:', error);
- showConnectionWarning();
- setAllHandStatusOffline();
+ if (!response.ok) {
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
+
+ const data = await response.json();
+
+ if (!data || data.status !== "success") {
+ throw new Error(data?.error || "获取状态失败");
+ }
+
+ // 安全地获取接口状态
+ const responseData = data.data || {};
+ interfaceStatus = responseData.interfaces || {};
+
+ updateAllHandStatus();
+ hideConnectionWarning();
+ } catch (error) {
+ logMessage("error", `状态检查失败:${error.message}`);
+ console.error("CheckAllInterfaceStatus Error:", error);
+ showConnectionWarning();
+ setAllHandStatusOffline();
+ }
}
// 检查单个接口状态
async function checkSingleInterfaceStatus(handId) {
- await checkAllInterfaceStatus();
+ await checkAllInterfaceStatus();
}
// 更新所有手部状态
function updateAllHandStatus() {
- Object.keys(handConfigs).forEach(handId => {
- const config = handConfigs[handId];
- const status = interfaceStatus[config.interface];
-
- if (status && status.active) {
- config.status = 'online';
- updateHandStatusDisplay(handId, 'online', '在线');
- } else {
- config.status = 'offline';
- updateHandStatusDisplay(handId, 'offline', '离线');
- }
- });
- updateEnabledHandsStatus();
+ Object.keys(handConfigs).forEach((handId) => {
+ const config = handConfigs[handId];
+ const status = interfaceStatus[config.interface];
+
+ if (status && status.active) {
+ config.status = "online";
+ updateHandStatusDisplay(handId, "online", "在线");
+ } else {
+ config.status = "offline";
+ updateHandStatusDisplay(handId, "offline", "离线");
+ }
+ });
+ updateEnabledHandsStatus();
}
// 设置所有手部状态为离线
function setAllHandStatusOffline() {
- Object.keys(handConfigs).forEach(handId => {
- handConfigs[handId].status = 'offline';
- updateHandStatusDisplay(handId, 'offline', '连接失败');
- });
- updateEnabledHandsStatus();
+ Object.keys(handConfigs).forEach((handId) => {
+ handConfigs[handId].status = "offline";
+ updateHandStatusDisplay(handId, "offline", "连接失败");
+ });
+ updateEnabledHandsStatus();
}
// 更新手部状态显示
function updateHandStatusDisplay(handId, status, text) {
- const statusDot = document.getElementById(`${handId}_status_dot`);
- const statusText = document.getElementById(`${handId}_status_text`);
-
- if (statusDot && statusText) {
- statusDot.className = `status-dot ${status}`;
- statusText.textContent = text;
- }
+ const statusDot = document.getElementById(`${handId}_status_dot`);
+ const statusText = document.getElementById(`${handId}_status_text`);
+
+ if (statusDot && statusText) {
+ statusDot.className = `status-dot ${status}`;
+ statusText.textContent = text;
+ }
}
// 显示连接警告
function showConnectionWarning() {
- document.getElementById('connection-warning').style.display = 'block';
+ document.getElementById("connection-warning").style.display = "block";
}
// 隐藏连接警告
function hideConnectionWarning() {
- document.getElementById('connection-warning').style.display = 'none';
+ document.getElementById("connection-warning").style.display = "none";
}
// 获取启用的手部配置
function getEnabledHands() {
- return Object.values(handConfigs).filter(config => config.enabled);
+ return Object.values(handConfigs).filter((config) => config.enabled);
}
// 设置事件监听器
function setupEventListeners() {
- const delayDefault = 30;
+ const delayDefault = 30;
- // 刷新所有接口按钮
- document.getElementById('refresh-all').addEventListener('click', function() {
- logMessage('info', '手动刷新所有接口...');
- initializeSystem();
- });
+ // 刷新所有接口按钮
+ document.getElementById("refresh-all").addEventListener("click", () => {
+ logMessage("info", "手动刷新所有接口...");
+ initializeSystem();
+ });
- // 全局控制按钮
- document.getElementById('send-all-finger-poses').addEventListener('click', sendAllFingerPoses);
- document.getElementById('send-all-palm-poses').addEventListener('click', sendAllPalmPoses);
- document.getElementById('reset-all-hands').addEventListener('click', resetAllHands);
- document.getElementById('stop-all-animations').addEventListener('click', stopAllAnimations);
+ // 全局控制按钮
+ document
+ .getElementById("send-all-finger-poses")
+ .addEventListener("click", sendAllFingerPoses);
+ document
+ .getElementById("send-all-palm-poses")
+ .addEventListener("click", sendAllPalmPoses);
+ document
+ .getElementById("reset-all-hands")
+ .addEventListener("click", resetAllHands);
+ document
+ .getElementById("stop-all-animations")
+ .addEventListener("click", stopAllAnimations);
- // 动画按钮
- document.getElementById('start-wave').addEventListener('click', () => startAnimationForAll('wave'));
- document.getElementById('start-sway').addEventListener('click', () => startAnimationForAll('sway'));
- document.getElementById('stop-animation').addEventListener('click', stopAllAnimations);
+ // 动画按钮
+ document
+ .getElementById("start-wave")
+ .addEventListener("click", () => startAnimationForAll("wave"));
+ document
+ .getElementById("start-sway")
+ .addEventListener("click", () => startAnimationForAll("sway"));
+ document
+ .getElementById("stop-animation")
+ .addEventListener("click", stopAllAnimations);
- // 预设姿势按钮 - 使用 LinkerHandController 的预设
- setupPresetButtons();
+ // 预设姿势按钮 - 使用 LinkerHandController 的预设
+ setupPresetButtons();
- // 数字手势按钮事件
- setupNumericPresets();
+ // 数字手势按钮事件
+ setupNumericPresets();
- // Refill core 按钮
- setupRefillCore();
+ // Refill core 按钮
+ setupRefillCore();
}
// 设置预设按钮
function setupPresetButtons() {
- const delayDefault = 30;
+ const delayDefault = 30;
- // 基础预设姿势
- const presets = {
- 'pose-fist': { finger: 'FIST', palm: null },
- 'pose-open': { finger: 'OPEN', palm: null },
- 'pose-pinch': { finger: 'PINCH', palm: 'PALM_PINCH' },
- 'pose-point': { finger: 'POINT', palm: null },
- 'pose-thumbs-up': { finger: 'THUMBSUP', palm: null },
- 'pose-yeah': { finger: 'YEAH', palm: 'PALM_YEAH' },
- 'pose-wave': { finger: 'WAVE', palm: null },
- 'pose-big-fist': { finger: 'BIG_FIST', palm: 'PALM_BIG_FIST' },
- 'pose-big-open': { finger: 'BIG_OPEN', palm: 'PALM_BIG_OPEN' },
- 'pose-yo': { finger: 'YO', palm: null },
- 'pose-gun': { finger: 'GUN', palm: 'PALM_GUN' },
- 'pose-ok': { finger: 'OK', palm: 'PALM_OK' }
- };
+ // 基础预设姿势
+ const presets = {
+ "pose-fist": { finger: "FIST", palm: null },
+ "pose-open": { finger: "OPEN", palm: null },
+ "pose-pinch": { finger: "PINCH", palm: "PALM_PINCH" },
+ "pose-point": { finger: "POINT", palm: null },
+ "pose-thumbs-up": { finger: "THUMBSUP", palm: null },
+ "pose-yeah": { finger: "YEAH", palm: "PALM_YEAH" },
+ "pose-wave": { finger: "WAVE", palm: null },
+ "pose-big-fist": { finger: "BIG_FIST", palm: "PALM_BIG_FIST" },
+ "pose-big-open": { finger: "BIG_OPEN", palm: "PALM_BIG_OPEN" },
+ "pose-yo": { finger: "YO", palm: null },
+ "pose-gun": { finger: "GUN", palm: "PALM_GUN" },
+ "pose-ok": { finger: "OK", palm: "PALM_OK" },
+ };
- Object.entries(presets).forEach(([id, preset]) => {
- const button = document.getElementById(id);
- if (button) {
- button.addEventListener('click', () => {
- if (preset.palm) {
- LinkerHandController.applyPalmPreset(LinkerHandController.PRESETS[preset.palm]);
- const palmPose = LinkerHandController.getPalmPoseValues();
- LinkerHandController.sendPalmPoseToAll(palmPose);
-
- setTimeout(() => {
- LinkerHandController.applyFingerPreset(LinkerHandController.PRESETS[preset.finger]);
- const fingerPose = LinkerHandController.getFingerPoseValues();
- LinkerHandController.sendFingerPoseToAll(fingerPose);
- }, delayDefault);
- } else {
- LinkerHandController.applyFingerPreset(LinkerHandController.PRESETS[preset.finger]);
- const fingerPose = LinkerHandController.getFingerPoseValues();
- LinkerHandController.sendFingerPoseToAll(fingerPose);
- }
- });
+ Object.entries(presets).forEach(([id, preset]) => {
+ const button = document.getElementById(id);
+ if (button) {
+ button.addEventListener("click", () => {
+ if (preset.palm) {
+ LinkerHandController.applyPalmPreset(
+ LinkerHandController.PRESETS[preset.palm],
+ );
+ const palmPose = LinkerHandController.getPalmPoseValues();
+ LinkerHandController.sendPalmPoseToAll(palmPose);
+
+ setTimeout(() => {
+ LinkerHandController.applyFingerPreset(
+ LinkerHandController.PRESETS[preset.finger],
+ );
+ const fingerPose = LinkerHandController.getFingerPoseValues();
+ LinkerHandController.sendFingerPoseToAll(fingerPose);
+ }, delayDefault);
+ } else {
+ LinkerHandController.applyFingerPreset(
+ LinkerHandController.PRESETS[preset.finger],
+ );
+ const fingerPose = LinkerHandController.getFingerPoseValues();
+ LinkerHandController.sendFingerPoseToAll(fingerPose);
}
- });
+ });
+ }
+ });
}
// 设置数字预设
function setupNumericPresets() {
- const delayDefault = 30;
+ const delayDefault = 30;
- // 数字 1-9 的预设
- for (let i = 1; i <= 9; i++) {
- const button = document.getElementById(`pose-${i}`);
- if (button) {
- button.addEventListener('click', () => {
- const palmPreset = LinkerHandController.PRESETS[`PALM_${getNumberName(i)}`];
- const fingerPreset = LinkerHandController.PRESETS[getNumberName(i)];
+ // 数字 1-9 的预设
+ for (let i = 1; i <= 9; i++) {
+ const button = document.getElementById(`pose-${i}`);
+ if (button) {
+ button.addEventListener("click", () => {
+ const palmPreset =
+ LinkerHandController.PRESETS[`PALM_${getNumberName(i)}`];
+ const fingerPreset = LinkerHandController.PRESETS[getNumberName(i)];
- if (palmPreset) {
- LinkerHandController.applyPalmPreset(palmPreset);
- const palmPose = LinkerHandController.getPalmPoseValues();
- LinkerHandController.sendPalmPoseToAll(palmPose);
- }
-
- setTimeout(() => {
- if (fingerPreset) {
- LinkerHandController.applyFingerPreset(fingerPreset);
- const fingerPose = LinkerHandController.getFingerPoseValues();
- LinkerHandController.sendFingerPoseToAll(fingerPose);
- }
- }, delayDefault);
- });
+ if (palmPreset) {
+ LinkerHandController.applyPalmPreset(palmPreset);
+ const palmPose = LinkerHandController.getPalmPoseValues();
+ LinkerHandController.sendPalmPoseToAll(palmPose);
}
+
+ setTimeout(() => {
+ if (fingerPreset) {
+ LinkerHandController.applyFingerPreset(fingerPreset);
+ const fingerPose = LinkerHandController.getFingerPoseValues();
+ LinkerHandController.sendFingerPoseToAll(fingerPose);
+ }
+ }, delayDefault);
+ });
}
+ }
}
// 获取数字名称
function getNumberName(num) {
- const names = ['', 'ONE', 'TWO', 'THREE', 'FOUR', 'FIVE', 'SIX', 'SEVEN', 'EIGHT', 'NINE'];
- return names[num] || '';
+ const names = [
+ "",
+ "ONE",
+ "TWO",
+ "THREE",
+ "FOUR",
+ "FIVE",
+ "SIX",
+ "SEVEN",
+ "EIGHT",
+ "NINE",
+ ];
+ return names[num] || "";
}
// 设置 Refill Core 功能
function setupRefillCore() {
- document.getElementById("refill-core").addEventListener("click", () => {
- event.preventDefault();
- event.stopPropagation();
+ document.getElementById("refill-core").addEventListener("click", () => {
+ event.preventDefault();
+ event.stopPropagation();
- console.log("refill-core");
+ console.log("refill-core");
- const rukaPoseList = [
- [[246, 188, 128, 128], [149, 30, 145, 36, 41, 46]], // 食指
- [[246, 155, 154, 66], [138, 80, 0, 154, 41, 46]], // 中指
- [[246, 155, 154, 40], [140, 80, 0, 15, 155, 46]], // 无名指
- [[246, 155, 154, 25], [140, 62, 0, 15, 29, 143]], // 小指
- ];
+ const rukaPoseList = [
+ [
+ [246, 188, 128, 128],
+ [149, 30, 145, 36, 41, 46],
+ ], // 食指
+ [
+ [246, 155, 154, 66],
+ [138, 80, 0, 154, 41, 46],
+ ], // 中指
+ [
+ [246, 155, 154, 40],
+ [140, 80, 0, 15, 155, 46],
+ ], // 无名指
+ [
+ [246, 155, 154, 25],
+ [140, 62, 0, 15, 29, 143],
+ ], // 小指
+ ];
- const delayTime = 350; // 设定延迟时间为 350ms
-
- // 创建完整的序列:从第一个到最后一个,再从最后一个回到第二个
- const forwardIndices = [...Array(rukaPoseList.length).keys()]; // [0,1,2,3]
- const backwardIndices = [...forwardIndices].reverse().slice(1); // [3,2,1]
- const sequenceIndices = [...forwardIndices, ...backwardIndices];
-
- // 遍历序列索引,为每个索引创建两个操作(palm 和 finger)
- sequenceIndices.forEach((index, step) => {
- const targetPose = rukaPoseList[index];
-
- // 应用 palm 预设
- setTimeout(() => {
- console.log(`Step ${step+1}a: Applying palm preset for pose ${index+1}`);
- LinkerHandController.applyPalmPreset(targetPose[0]);
- const palmPose = LinkerHandController.getPalmPoseValues();
- LinkerHandController.sendPalmPoseToAll(palmPose);
- }, delayTime * (step * 2)); // 每个完整步骤有两个操作,所以是 step*2
-
- // 应用 finger 预设
- setTimeout(() => {
- console.log(`Step ${step+1}b: Applying finger preset for pose ${index+1}`);
- LinkerHandController.applyFingerPreset(targetPose[1]);
- const fingerPose = LinkerHandController.getFingerPoseValues();
- LinkerHandController.sendFingerPoseToAll(fingerPose);
- }, delayTime * (step * 2 + 1)); // 偏移一个 delayTime
- });
+ const delayTime = 350; // 设定延迟时间为 350ms
+
+ // 创建完整的序列:从第一个到最后一个,再从最后一个回到第二个
+ const forwardIndices = [...Array(rukaPoseList.length).keys()]; // [0,1,2,3]
+ const backwardIndices = [...forwardIndices].reverse().slice(1); // [3,2,1]
+ const sequenceIndices = [...forwardIndices, ...backwardIndices];
+
+ // 遍历序列索引,为每个索引创建两个操作(palm 和 finger)
+ sequenceIndices.forEach((index, step) => {
+ const targetPose = rukaPoseList[index];
+
+ // 应用 palm 预设
+ setTimeout(() => {
+ console.log(
+ `Step ${step + 1}a: Applying palm preset for pose ${index + 1}`,
+ );
+ LinkerHandController.applyPalmPreset(targetPose[0]);
+ const palmPose = LinkerHandController.getPalmPoseValues();
+ LinkerHandController.sendPalmPoseToAll(palmPose);
+ }, delayTime * (step * 2)); // 每个完整步骤有两个操作,所以是 step*2
+
+ // 应用 finger 预设
+ setTimeout(() => {
+ console.log(
+ `Step ${step + 1}b: Applying finger preset for pose ${index + 1}`,
+ );
+ LinkerHandController.applyFingerPreset(targetPose[1]);
+ const fingerPose = LinkerHandController.getFingerPoseValues();
+ LinkerHandController.sendFingerPoseToAll(fingerPose);
+ }, delayTime * (step * 2 + 1)); // 偏移一个 delayTime
});
+ });
}
// 设置滑块事件
function setupSliderEvents() {
- // 手指滑块
- for (let i = 0; i < 6; i++) {
- const slider = document.getElementById(`finger${i}`);
- const valueDisplay = document.getElementById(`finger${i}-value`);
- slider.addEventListener('input', function() {
- valueDisplay.textContent = this.value;
- });
- }
-
- // 掌部滑块
- for (let i = 0; i < 4; i++) {
- const slider = document.getElementById(`palm${i}`);
- const valueDisplay = document.getElementById(`palm${i}-value`);
- slider.addEventListener('input', function() {
- valueDisplay.textContent = this.value;
- });
- }
-
- // 速度滑块
- const speedSlider = document.getElementById('animation-speed');
- const speedDisplay = document.getElementById('speed-value');
- speedSlider.addEventListener('input', function() {
- speedDisplay.textContent = this.value;
+ // 手指滑块
+ for (let i = 0; i < 6; i++) {
+ const slider = document.getElementById(`finger${i}`);
+ const valueDisplay = document.getElementById(`finger${i}-value`);
+ slider.addEventListener("input", function () {
+ valueDisplay.textContent = this.value;
});
+ }
+
+ // 掌部滑块
+ for (let i = 0; i < 4; i++) {
+ const slider = document.getElementById(`palm${i}`);
+ const valueDisplay = document.getElementById(`palm${i}-value`);
+ slider.addEventListener("input", function () {
+ valueDisplay.textContent = this.value;
+ });
+ }
+
+ // 速度滑块
+ const speedSlider = document.getElementById("animation-speed");
+ const speedDisplay = document.getElementById("speed-value");
+ speedSlider.addEventListener("input", function () {
+ speedDisplay.textContent = this.value;
+ });
}
// 发送所有启用手部的手指姿态
async function sendAllFingerPoses() {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
- const pose = [];
- for (let i = 0; i < 6; i++) {
- pose.push(parseInt(document.getElementById(`finger${i}`).value));
- }
+ const pose = [];
+ for (let i = 0; i < 6; i++) {
+ pose.push(Number.parseInt(document.getElementById(`finger${i}`).value));
+ }
- logMessage('info', `发送手指姿态到 ${enabledHands.length} 个启用的手部...`);
+ logMessage("info", `发送手指姿态到 ${enabledHands.length} 个启用的手部...`);
- for (const config of enabledHands) {
- await sendFingerPoseToHand(config, pose);
- }
+ for (const config of enabledHands) {
+ await sendFingerPoseToHand(config, pose);
+ }
}
// 发送所有启用手部的掌部姿态
async function sendAllPalmPoses() {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
- const pose = [];
- for (let i = 0; i < 4; i++) {
- pose.push(parseInt(document.getElementById(`palm${i}`).value));
- }
+ const pose = [];
+ for (let i = 0; i < 4; i++) {
+ pose.push(Number.parseInt(document.getElementById(`palm${i}`).value));
+ }
- logMessage('info', `发送掌部姿态到 ${enabledHands.length} 个启用的手部...`);
+ logMessage("info", `发送掌部姿态到 ${enabledHands.length} 个启用的手部...`);
- for (const config of enabledHands) {
- await sendPalmPoseToHand(config, pose);
- }
+ for (const config of enabledHands) {
+ await sendPalmPoseToHand(config, pose);
+ }
}
// 发送手指姿态到指定手部
async function sendFingerPoseToHand(config, pose) {
- try {
- const response = await fetch('/api/fingers', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- interface: config.interface,
- pose: pose,
- handType: config.handType,
- handId: handTypeIds[config.handType]
- })
- });
+ try {
+ const response = await fetch("/api/fingers", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ interface: config.interface,
+ pose: pose,
+ handType: config.handType,
+ handId: handTypeIds[config.handType],
+ }),
+ });
- const data = await response.json();
- if (data.status === 'success') {
- const handName = config.handType === 'left' ? '左手' : '右手';
- logMessage('success', `${config.interface} (${handName}): 手指姿态发送成功 [${pose.join(', ')}]`);
- } else {
- logMessage('error', `${config.interface}: ${data.error}`);
- }
- } catch (error) {
- logMessage('error', `${config.interface}: 发送失败 - ${error.message}`);
+ const data = await response.json();
+ if (data.status === "success") {
+ const handName = config.handType === "left" ? "左手" : "右手";
+ logMessage(
+ "success",
+ `${config.interface} (${handName}): 手指姿态发送成功 [${pose.join(
+ ", ",
+ )}]`,
+ );
+ } else {
+ logMessage("error", `${config.interface}: ${data.error}`);
}
+ } catch (error) {
+ logMessage("error", `${config.interface}: 发送失败 - ${error.message}`);
+ }
}
// 发送掌部姿态到指定手部
async function sendPalmPoseToHand(config, pose) {
- try {
- const response = await fetch('/api/palm', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- interface: config.interface,
- pose: pose,
- handType: config.handType,
- handId: handTypeIds[config.handType]
- })
- });
+ try {
+ const response = await fetch("/api/palm", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ interface: config.interface,
+ pose: pose,
+ handType: config.handType,
+ handId: handTypeIds[config.handType],
+ }),
+ });
- const data = await response.json();
- if (data.status === 'success') {
- const handName = config.handType === 'left' ? '左手' : '右手';
- logMessage('success', `${config.interface} (${handName}): 掌部姿态发送成功 [${pose.join(', ')}]`);
- } else {
- logMessage('error', `${config.interface}: ${data.error}`);
- }
- } catch (error) {
- logMessage('error', `${config.interface}: 发送失败 - ${error.message}`);
+ const data = await response.json();
+ if (data.status === "success") {
+ const handName = config.handType === "left" ? "左手" : "右手";
+ logMessage(
+ "success",
+ `${config.interface} (${handName}): 掌部姿态发送成功 [${pose.join(
+ ", ",
+ )}]`,
+ );
+ } else {
+ logMessage("error", `${config.interface}: ${data.error}`);
}
+ } catch (error) {
+ logMessage("error", `${config.interface}: 发送失败 - ${error.message}`);
+ }
}
// 为所有启用手部设置预设姿势
async function setPresetPoseForAll(preset) {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
- logMessage('info', `设置预设姿势 "${preset}" 到 ${enabledHands.length} 个启用的手部...`);
+ logMessage(
+ "info",
+ `设置预设姿势 "${preset}" 到 ${enabledHands.length} 个启用的手部...`,
+ );
- for (const config of enabledHands) {
- await setPresetPoseToHand(config, preset);
- }
+ for (const config of enabledHands) {
+ await setPresetPoseToHand(config, preset);
+ }
}
// 为指定手部设置预设姿势
async function setPresetPoseToHand(config, preset) {
- try {
- const response = await fetch(`/api/preset/${preset}?interface=${config.interface}&handType=${config.handType}`, {
- method: 'POST'
- });
+ try {
+ const response = await fetch(
+ `/api/preset/${preset}?interface=${config.interface}&handType=${config.handType}`,
+ {
+ method: "POST",
+ },
+ );
- const data = await response.json();
- if (data.status === 'success') {
- const handName = config.handType === 'left' ? '左手' : '右手';
- logMessage('success', `${config.interface} (${handName}): ${data.message}`);
- } else {
- logMessage('error', `${config.interface}: ${data.error}`);
- }
- } catch (error) {
- logMessage('error', `${config.interface}: 预设姿势失败 - ${error.message}`);
+ const data = await response.json();
+ if (data.status === "success") {
+ const handName = config.handType === "left" ? "左手" : "右手";
+ logMessage(
+ "success",
+ `${config.interface} (${handName}): ${data.message}`,
+ );
+ } else {
+ logMessage("error", `${config.interface}: ${data.error}`);
}
+ } catch (error) {
+ logMessage("error", `${config.interface}: 预设姿势失败 - ${error.message}`);
+ }
}
// 为所有启用手部启动动画
async function startAnimationForAll(type) {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
- const speed = parseInt(document.getElementById('animation-speed').value);
- logMessage('info', `启动 "${type}" 动画到 ${enabledHands.length} 个启用的手部...`);
+ const speed = Number.parseInt(
+ document.getElementById("animation-speed").value,
+ );
+ logMessage(
+ "info",
+ `启动 "${type}" 动画到 ${enabledHands.length} 个启用的手部...`,
+ );
- for (const config of enabledHands) {
- await startAnimationForHand(config, type, speed);
- }
+ for (const config of enabledHands) {
+ await startAnimationForHand(config, type, speed);
+ }
}
// 为指定手部启动动画
async function startAnimationForHand(config, type, speed) {
- try {
- const response = await fetch('/api/animation', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- interface: config.interface,
- type: type,
- speed: speed,
- handType: config.handType,
- handId: handTypeIds[config.handType]
- })
- });
+ try {
+ const response = await fetch("/api/animation", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ interface: config.interface,
+ type: type,
+ speed: speed,
+ handType: config.handType,
+ handId: handTypeIds[config.handType],
+ }),
+ });
- const data = await response.json();
- if (data.status === 'success') {
- const handName = config.handType === 'left' ? '左手' : '右手';
- logMessage('success', `${config.interface} (${handName}): ${data.message}`);
- } else {
- logMessage('error', `${config.interface}: ${data.error}`);
- }
- } catch (error) {
- logMessage('error', `${config.interface}: 动画启动失败 - ${error.message}`);
+ const data = await response.json();
+ if (data.status === "success") {
+ const handName = config.handType === "left" ? "左手" : "右手";
+ logMessage(
+ "success",
+ `${config.interface} (${handName}): ${data.message}`,
+ );
+ } else {
+ logMessage("error", `${config.interface}: ${data.error}`);
}
+ } catch (error) {
+ logMessage("error", `${config.interface}: 动画启动失败 - ${error.message}`);
+ }
}
// 停止所有启用手部的动画
async function stopAllAnimations() {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
- logMessage('info', `停止 ${enabledHands.length} 个启用手部的动画...`);
+ logMessage("info", `停止 ${enabledHands.length} 个启用手部的动画...`);
- for (const config of enabledHands) {
- await stopAnimationForHand(config);
- }
+ for (const config of enabledHands) {
+ await stopAnimationForHand(config);
+ }
}
// 停止指定手部的动画
async function stopAnimationForHand(config) {
- try {
- const response = await fetch('/api/animation', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- interface: config.interface,
- type: 'stop',
- handType: config.handType,
- handId: handTypeIds[config.handType]
- })
- });
+ try {
+ const response = await fetch("/api/animation", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ interface: config.interface,
+ type: "stop",
+ handType: config.handType,
+ handId: handTypeIds[config.handType],
+ }),
+ });
- const data = await response.json();
- if (data.status === 'success') {
- const handName = config.handType === 'left' ? '左手' : '右手';
- logMessage('success', `${config.interface} (${handName}): ${data.message}`);
- } else {
- logMessage('error', `${config.interface}: ${data.error}`);
- }
- } catch (error) {
- logMessage('error', `${config.interface}: 停止动画失败 - ${error.message}`);
+ const data = await response.json();
+ if (data.status === "success") {
+ const handName = config.handType === "left" ? "左手" : "右手";
+ logMessage(
+ "success",
+ `${config.interface} (${handName}): ${data.message}`,
+ );
+ } else {
+ logMessage("error", `${config.interface}: ${data.error}`);
}
+ } catch (error) {
+ logMessage("error", `${config.interface}: 停止动画失败 - ${error.message}`);
+ }
}
// 重置所有启用手部
async function resetAllHands() {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
- // 重置滑块值
- LinkerHandController.applyFingerPreset(LinkerHandController.PRESETS.OPEN);
- LinkerHandController.applyPalmPreset(LinkerHandController.PRESETS.PALM_NEUTRAL);
+ // 重置滑块值
+ LinkerHandController.applyFingerPreset(LinkerHandController.PRESETS.OPEN);
+ LinkerHandController.applyPalmPreset(
+ LinkerHandController.PRESETS.PALM_NEUTRAL,
+ );
- logMessage('info', `重置 ${enabledHands.length} 个启用的手部...`);
+ logMessage("info", `重置 ${enabledHands.length} 个启用的手部...`);
- // 停止所有动画
- await stopAllAnimations();
-
- // 发送重置姿态
- await sendAllFingerPoses();
- await sendAllPalmPoses();
+ // 停止所有动画
+ await stopAllAnimations();
- logMessage('info', '所有启用手部已重置完成');
+ // 发送重置姿态
+ await sendAllFingerPoses();
+ await sendAllPalmPoses();
+
+ logMessage("info", "所有启用手部已重置完成");
}
// 自动触发按钮序列(数字手势)
async function triggerButtonsSequentially(interval = 2000) {
- const enabledHands = getEnabledHands();
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
+ const enabledHands = getEnabledHands();
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
+
+ logMessage("info", `开始自动数字手势序列 (${enabledHands.length} 个手部)`);
+
+ const buttons = [
+ document.getElementById("pose-1"),
+ document.getElementById("pose-2"),
+ document.getElementById("pose-3"),
+ document.getElementById("pose-4"),
+ document.getElementById("pose-5"),
+ document.getElementById("pose-6"),
+ document.getElementById("pose-7"),
+ document.getElementById("pose-8"),
+ document.getElementById("pose-9"),
+ ];
+
+ for (const button of buttons) {
+ if (button) {
+ button.click();
+ await new Promise((resolve) => setTimeout(resolve, interval));
}
+ }
- logMessage('info', `开始自动数字手势序列 (${enabledHands.length} 个手部)`);
-
- const buttons = [
- document.getElementById('pose-1'),
- document.getElementById('pose-2'),
- document.getElementById('pose-3'),
- document.getElementById('pose-4'),
- document.getElementById('pose-5'),
- document.getElementById('pose-6'),
- document.getElementById('pose-7'),
- document.getElementById('pose-8'),
- document.getElementById('pose-9'),
- ];
+ // 然后执行所有预设手势
+ const presetButtons = document.querySelectorAll(
+ ".preset-grid button:not(.preset-num-pose)",
+ );
+ for (const button of presetButtons) {
+ button.click();
+ await new Promise((resolve) => setTimeout(resolve, interval));
+ }
- for (const button of buttons) {
- if (button) {
- button.click();
- await new Promise(resolve => setTimeout(resolve, interval));
- }
- }
-
- // 然后执行所有预设手势
- const presetButtons = document.querySelectorAll('.preset-grid button:not(.preset-num-pose)');
- for (const button of presetButtons) {
- button.click();
- await new Promise(resolve => setTimeout(resolve, interval));
- }
-
- logMessage('success', '数字手势序列完成');
+ logMessage("success", "数字手势序列完成");
}
// 日志消息
function logMessage(type, message) {
- const statusLog = document.getElementById('status-log');
- const timestamp = new Date().toLocaleTimeString();
-
- const logEntry = document.createElement('div');
- logEntry.className = 'log-entry';
-
- let statusClass = 'status-info';
- if (type === 'success') statusClass = 'status-success';
- else if (type === 'error') statusClass = 'status-error';
-
- logEntry.innerHTML = `
+ const statusLog = document.getElementById("status-log");
+ const timestamp = new Date().toLocaleTimeString();
+
+ const logEntry = document.createElement("div");
+ logEntry.className = "log-entry";
+
+ let statusClass = "status-info";
+ if (type === "success") statusClass = "status-success";
+ else if (type === "error") statusClass = "status-error";
+
+ logEntry.innerHTML = `
${timestamp}
${message}
`;
-
- statusLog.appendChild(logEntry);
- statusLog.scrollTop = statusLog.scrollHeight;
- // 保持最多 50 条日志
- const entries = statusLog.querySelectorAll('.log-entry');
- if (entries.length > 50) {
- statusLog.removeChild(entries[0]);
- }
+ statusLog.appendChild(logEntry);
+ statusLog.scrollTop = statusLog.scrollHeight;
+
+ // 保持最多 50 条日志
+ const entries = statusLog.querySelectorAll(".log-entry");
+ if (entries.length > 50) {
+ statusLog.removeChild(entries[0]);
+ }
}
// 启动状态更新器
function startStatusUpdater() {
- // 每 5 秒检查一次接口状态
- setInterval(async () => {
- await checkAllInterfaceStatus();
- }, 5000);
+ // 每 5 秒检查一次接口状态
+ setInterval(async () => {
+ await checkAllInterfaceStatus();
+ }, 5000);
- // 每 30 秒刷新一次接口列表
- setInterval(async () => {
- const oldInterfaces = [...availableInterfaces];
- await loadAvailableInterfaces();
-
- // 如果接口发生变化,重新生成配置
- if (JSON.stringify(oldInterfaces) !== JSON.stringify(availableInterfaces)) {
- generateHandConfigs();
- }
- }, 30000);
+ // 每 30 秒刷新一次接口列表
+ setInterval(async () => {
+ const oldInterfaces = [...availableInterfaces];
+ await loadAvailableInterfaces();
+
+ // 如果接口发生变化,重新生成配置
+ if (JSON.stringify(oldInterfaces) !== JSON.stringify(availableInterfaces)) {
+ generateHandConfigs();
+ }
+ }, 30000);
}
// 添加调试功能
async function debugSystemStatus() {
- logMessage('info', '🔍 开始系统调试...');
-
- // 检查 HTML 元素
- const elements = {
- 'hands-grid': document.getElementById('hands-grid'),
- 'status-log': document.getElementById('status-log'),
- 'enabled-hands-status': document.getElementById('enabled-hands-status'),
- 'sensor-data': document.getElementById('sensor-data')
- };
-
- Object.entries(elements).forEach(([name, element]) => {
- if (element) {
- logMessage('success', `✅ 元素 ${name} 存在`);
- } else {
- logMessage('error', `❌ 元素 ${name} 不存在`);
- }
- });
-
- // 检查全局变量
- logMessage('info', `可用接口:[${availableInterfaces.join(', ')}]`);
- logMessage('info', `手部配置数量:${Object.keys(handConfigs).length}`);
- logMessage('info', `启用手部数量:${getEnabledHands().length}`);
-
- // 测试 API 连通性
- try {
- logMessage('info', '测试 /api/health 连接...');
- const response = await fetch('/api/health');
- if (response.ok) {
- const data = await response.json();
- logMessage('success', '✅ 健康检查通过');
- console.log('Health Check Data:', data);
- } else {
- logMessage('error', `❌ 健康检查失败:HTTP ${response.status}`);
- }
- } catch (error) {
- logMessage('error', `❌ 健康检查异常:${error.message}`);
+ logMessage("info", "🔍 开始系统调试...");
+
+ // 检查 HTML 元素
+ const elements = {
+ "hands-grid": document.getElementById("hands-grid"),
+ "status-log": document.getElementById("status-log"),
+ "enabled-hands-status": document.getElementById("enabled-hands-status"),
+ "sensor-data": document.getElementById("sensor-data"),
+ };
+
+ Object.entries(elements).forEach(([name, element]) => {
+ if (element) {
+ logMessage("success", `✅ 元素 ${name} 存在`);
+ } else {
+ logMessage("error", `❌ 元素 ${name} 不存在`);
}
-
- // 测试接口 API
- try {
- logMessage('info', '测试 /api/interfaces 连接...');
- const response = await fetch('/api/interfaces');
- if (response.ok) {
- const data = await response.json();
- logMessage('success', '✅ 接口 API 通过');
- console.log('Interfaces API Data:', data);
- } else {
- logMessage('error', `❌ 接口 API 失败:HTTP ${response.status}`);
- }
- } catch (error) {
- logMessage('error', `❌ 接口 API 异常:${error.message}`);
+ });
+
+ // 检查全局变量
+ logMessage("info", `可用接口:[${availableInterfaces.join(", ")}]`);
+ logMessage("info", `手部配置数量:${Object.keys(handConfigs).length}`);
+ logMessage("info", `启用手部数量:${getEnabledHands().length}`);
+
+ // 测试 API 连通性
+ try {
+ logMessage("info", "测试 /api/health 连接...");
+ const response = await fetch("/api/health");
+ if (response.ok) {
+ const data = await response.json();
+ logMessage("success", "✅ 健康检查通过");
+ console.log("Health Check Data:", data);
+ } else {
+ logMessage("error", `❌ 健康检查失败:HTTP ${response.status}`);
}
+ } catch (error) {
+ logMessage("error", `❌ 健康检查异常:${error.message}`);
+ }
+
+ // 测试接口 API
+ try {
+ logMessage("info", "测试 /api/interfaces 连接...");
+ const response = await fetch("/api/interfaces");
+ if (response.ok) {
+ const data = await response.json();
+ logMessage("success", "✅ 接口 API 通过");
+ console.log("Interfaces API Data:", data);
+ } else {
+ logMessage("error", `❌ 接口 API 失败:HTTP ${response.status}`);
+ }
+ } catch (error) {
+ logMessage("error", `❌ 接口 API 异常:${error.message}`);
+ }
}
// 导出全局函数供 HTML 按钮使用
@@ -1283,357 +1427,381 @@ window.triggerButtonsSequentially = triggerButtonsSequentially;
window.debugSystemStatus = debugSystemStatus;
// 添加全局错误处理
-window.addEventListener('error', function(event) {
- logMessage('error', `全局错误:${event.error?.message || event.message}`);
- console.error('Global Error:', event.error);
+window.addEventListener("error", (event) => {
+ logMessage("error", `全局错误:${event.error?.message || event.message}`);
+ console.error("Global Error:", event.error);
});
-window.addEventListener('unhandledrejection', function(event) {
- logMessage('error', `未处理的 Promise 拒绝:${event.reason?.message || event.reason}`);
- console.error('Unhandled Promise Rejection:', event.reason);
+window.addEventListener("unhandledrejection", (event) => {
+ logMessage(
+ "error",
+ `未处理的 Promise 拒绝:${event.reason?.message || event.reason}`,
+ );
+ console.error("Unhandled Promise Rejection:", event.reason);
});
// 页面可见性变化时的处理
-document.addEventListener('visibilitychange', function() {
- if (!document.hidden) {
- // 页面变为可见时,刷新状态
- checkAllInterfaceStatus();
- }
+document.addEventListener("visibilitychange", () => {
+ if (!document.hidden) {
+ // 页面变为可见时,刷新状态
+ checkAllInterfaceStatus();
+ }
});
// 处理网络错误时的重连逻辑
-window.addEventListener('online', function() {
- logMessage('info', '网络连接已恢复,正在重新连接...');
- initializeSystem();
+window.addEventListener("online", () => {
+ logMessage("info", "网络连接已恢复,正在重新连接...");
+ initializeSystem();
});
-window.addEventListener('offline', function() {
- logMessage('error', '网络连接已断开');
- showConnectionWarning();
+window.addEventListener("offline", () => {
+ logMessage("error", "网络连接已断开");
+ showConnectionWarning();
});
// 键盘快捷键支持
-document.addEventListener('keydown', function(e) {
- // Ctrl+R 刷新接口
- if (e.ctrlKey && e.key === 'r') {
- e.preventDefault();
- logMessage('info', '快捷键刷新接口列表...');
- initializeSystem();
- }
-
- // Ctrl+Space 停止所有动画
- if (e.ctrlKey && e.code === 'Space') {
- e.preventDefault();
- stopAllAnimations();
- }
-
- // Ctrl+A 选择/取消选择所有手部
- if (e.ctrlKey && e.key === 'a') {
- e.preventDefault();
- toggleAllHands();
- }
-
- // 数字键 1-9 快速设置预设姿势
- if (e.key >= '1' && e.key <= '9' && !e.ctrlKey && !e.altKey) {
- const activeElement = document.activeElement;
- // 确保不在输入框中
- if (activeElement.tagName !== 'INPUT' && activeElement.tagName !== 'SELECT') {
- const button = document.getElementById(`pose-${e.key}`);
- if (button) button.click();
- }
+document.addEventListener("keydown", (e) => {
+ // Ctrl+R 刷新接口
+ if (e.ctrlKey && e.key === "r") {
+ e.preventDefault();
+ logMessage("info", "快捷键刷新接口列表...");
+ initializeSystem();
+ }
+
+ // Ctrl+Space 停止所有动画
+ if (e.ctrlKey && e.code === "Space") {
+ e.preventDefault();
+ stopAllAnimations();
+ }
+
+ // Ctrl+A 选择/取消选择所有手部
+ if (e.ctrlKey && e.key === "a") {
+ e.preventDefault();
+ toggleAllHands();
+ }
+
+ // 数字键 1-9 快速设置预设姿势
+ if (e.key >= "1" && e.key <= "9" && !e.ctrlKey && !e.altKey) {
+ const activeElement = document.activeElement;
+ // 确保不在输入框中
+ if (
+ activeElement.tagName !== "INPUT" &&
+ activeElement.tagName !== "SELECT"
+ ) {
+ const button = document.getElementById(`pose-${e.key}`);
+ if (button) button.click();
}
+ }
});
// 切换所有手部启用状态
function toggleAllHands() {
- const enabledCount = Object.values(handConfigs).filter(config => config.enabled).length;
- const shouldEnable = enabledCount === 0;
+ const enabledCount = Object.values(handConfigs).filter(
+ (config) => config.enabled,
+ ).length;
+ const shouldEnable = enabledCount === 0;
- Object.keys(handConfigs).forEach(handId => {
- handConfigs[handId].enabled = shouldEnable;
- const checkbox = document.getElementById(`${handId}_checkbox`);
- if (checkbox) {
- checkbox.checked = shouldEnable;
- }
- updateHandElement(handId);
- });
+ Object.keys(handConfigs).forEach((handId) => {
+ handConfigs[handId].enabled = shouldEnable;
+ const checkbox = document.getElementById(`${handId}_checkbox`);
+ if (checkbox) {
+ checkbox.checked = shouldEnable;
+ }
+ updateHandElement(handId);
+ });
- updateEnabledHandsStatus();
- logMessage('info', `${shouldEnable ? '启用' : '禁用'}所有手部`);
+ updateEnabledHandsStatus();
+ logMessage("info", `${shouldEnable ? "启用" : "禁用"}所有手部`);
}
// 工具提示功能
function addTooltips() {
- const tooltips = {
- 'refresh-all': '刷新所有可用接口列表',
- 'send-all-finger-poses': '向所有启用的手部发送当前手指关节位置',
- 'send-all-palm-poses': '向所有启用的手部发送当前掌部关节位置',
- 'reset-all-hands': '重置所有启用手部到默认位置',
- 'stop-all-animations': '停止所有启用手部的动画',
- 'start-wave': '启动所有启用手部的手指波浪动画',
- 'start-sway': '启动所有启用手部的掌部摆动动画',
- 'stop-animation': '停止所有启用手部的动画',
- 'refill-core': '执行 Refill Core 动作序列'
- };
+ const tooltips = {
+ "refresh-all": "刷新所有可用接口列表",
+ "send-all-finger-poses": "向所有启用的手部发送当前手指关节位置",
+ "send-all-palm-poses": "向所有启用的手部发送当前掌部关节位置",
+ "reset-all-hands": "重置所有启用手部到默认位置",
+ "stop-all-animations": "停止所有启用手部的动画",
+ "start-wave": "启动所有启用手部的手指波浪动画",
+ "start-sway": "启动所有启用手部的掌部摆动动画",
+ "stop-animation": "停止所有启用手部的动画",
+ "refill-core": "执行 Refill Core 动作序列",
+ };
- Object.entries(tooltips).forEach(([id, text]) => {
- const element = document.getElementById(id);
- if (element) {
- element.title = text;
- }
- });
+ Object.entries(tooltips).forEach(([id, text]) => {
+ const element = document.getElementById(id);
+ if (element) {
+ element.title = text;
+ }
+ });
}
// 页面加载完成后添加工具提示
-document.addEventListener('DOMContentLoaded', function() {
- addTooltips();
+document.addEventListener("DOMContentLoaded", () => {
+ addTooltips();
});
-
// ---eof
// 六手依次动画函数
-async function startSequentialHandAnimation(animationType = 'wave', interval = 500, cycles = 3) {
- const enabledHands = getEnabledHands();
-
- // 检查是否有足够的手部
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
-
- // 确保按接口名称排序(can0, can1, can2...)
- const sortedHands = enabledHands.sort((a, b) => {
- const getInterfaceNumber = (iface) => {
- const match = iface.match(/(\d+)$/);
- return match ? parseInt(match[1]) : 0;
- };
- return getInterfaceNumber(a.interface) - getInterfaceNumber(b.interface);
- });
-
- logMessage('info', `开始六手依次动画 - 类型:${animationType}, 间隔:${interval}ms, 循环:${cycles}次`);
- logMessage('info', `动画顺序:${sortedHands.map(h => h.interface).join(' → ')}`);
-
- // 定义动画预设
- const animationPresets = {
- wave: {
- name: '手指波浪',
- fingerPoses: [
- [255, 255, 255, 255, 255, 255], // 完全张开
- [128, 128, 128, 128, 128, 128], // 中间位置
- [64, 64, 64, 64, 64, 64], // 握拳
- [128, 128, 128, 128, 128, 128], // 回到中间
- ],
- palmPose: [128, 128, 128, 128] // 掌部保持中立
- },
-
- thumbsUp: {
- name: '竖拇指传递',
- fingerPoses: [
- [255, 255, 0, 0, 0, 0], // 竖拇指
- [128, 128, 128, 128, 128, 128], // 恢复中立
- ],
- palmPose: [128, 128, 128, 128]
- },
-
- point: {
- name: '食指指点传递',
- fingerPoses: [
- [0, 0, 255, 0, 0, 0], // 食指指点
- [128, 128, 128, 128, 128, 128], // 恢复中立
- ],
- palmPose: [128, 128, 128, 128]
- },
-
- fistOpen: {
- name: '握拳张开',
- fingerPoses: [
- [64, 64, 64, 64, 64, 64], // 握拳
- [255, 255, 255, 255, 255, 255], // 张开
- [128, 128, 128, 128, 128, 128], // 中立
- ],
- palmPose: [128, 128, 128, 128]
- },
-
- numbers: {
- name: '数字倒计时',
- fingerPoses: [
- [255, 255, 255, 255, 255, 255], // 5
- [0, 57, 255, 255, 255, 255], // 4
- [0, 57, 255, 255, 255, 0], // 3
- [0, 57, 255, 255, 0, 0], // 2
- [0, 57, 255, 0, 0, 0], // 1
- [64, 64, 64, 64, 64, 64], // 握拳 (0)
- ],
- palmPoses: [
- [255, 109, 255, 118], // 5 对应的掌部
- [255, 109, 255, 118], // 4 对应的掌部
- [255, 109, 255, 118], // 3 对应的掌部
- [255, 109, 255, 118], // 2 对应的掌部
- [255, 109, 255, 118], // 1 对应的掌部
- [128, 128, 128, 128], // 0 对应的掌部
- ]
- },
-
- mexican: {
- name: '墨西哥波浪',
- fingerPoses: [
- [64, 64, 64, 64, 64, 64], // 起始握拳
- [128, 64, 64, 64, 64, 64], // 拇指起
- [255, 128, 64, 64, 64, 64], // 拇指 + 食指起
- [255, 255, 128, 64, 64, 64], // 前三指起
- [255, 255, 255, 128, 64, 64], // 前四指起
- [255, 255, 255, 255, 128, 64], // 前五指起
- [255, 255, 255, 255, 255, 255], // 全部张开
- [128, 255, 255, 255, 255, 128], // 波浪形
- [64, 128, 255, 255, 128, 64], // 继续波浪
- [64, 64, 128, 255, 128, 64], // 波浪收尾
- [64, 64, 64, 128, 64, 64], // 几乎回到握拳
- [64, 64, 64, 64, 64, 64], // 完全握拳
- ],
- palmPose: [128, 128, 128, 128]
- }
+async function startSequentialHandAnimation(
+ animationType = "wave",
+ interval = 500,
+ cycles = 3,
+) {
+ const enabledHands = getEnabledHands();
+
+ // 检查是否有足够的手部
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
+
+ // 确保按接口名称排序(can0, can1, can2...)
+ const sortedHands = enabledHands.sort((a, b) => {
+ const getInterfaceNumber = (iface) => {
+ const match = iface.match(/(\d+)$/);
+ return match ? Number.parseInt(match[1]) : 0;
};
-
- const preset = animationPresets[animationType] || animationPresets.wave;
- const fingerPoses = preset.fingerPoses;
- const palmPoses = preset.palmPoses || Array(fingerPoses.length).fill(preset.palmPose);
-
- // 执行动画循环
- for (let cycle = 0; cycle < cycles; cycle++) {
- logMessage('info', `${preset.name} - 第 ${cycle + 1}/${cycles} 轮`);
-
- // 每个动作姿势
- for (let poseIndex = 0; poseIndex < fingerPoses.length; poseIndex++) {
- const currentFingerPose = fingerPoses[poseIndex];
- const currentPalmPose = palmPoses[poseIndex];
-
- // 依次激活每只手
- for (let handIndex = 0; handIndex < sortedHands.length; handIndex++) {
- const hand = sortedHands[handIndex];
- const handName = hand.handType === 'left' ? '左手' : '右手';
-
- // 先发送掌部姿态
- await sendPalmPoseToHand(hand, currentPalmPose);
-
- // 短暂延迟后发送手指姿态
- setTimeout(async () => {
- await sendFingerPoseToHand(hand, currentFingerPose);
- }, 50);
-
- logMessage('info', `${hand.interface}(${handName}) 执行姿势 ${poseIndex + 1}/${fingerPoses.length}`);
-
- // 等待间隔时间再激活下一只手
- await new Promise(resolve => setTimeout(resolve, interval));
- }
- }
-
- // 循环间隔(如果有多轮)
- if (cycle < cycles - 1) {
- logMessage('info', `等待下一轮动画...`);
- await new Promise(resolve => setTimeout(resolve, interval * 2));
- }
- }
-
- // 动画结束后,让所有手回到中立位置
- logMessage('info', '动画完成,恢复中立位置...');
- const neutralFingerPose = [128, 128, 128, 128, 128, 128];
- const neutralPalmPose = [128, 128, 128, 128];
-
- for (const hand of sortedHands) {
- await sendPalmPoseToHand(hand, neutralPalmPose);
+ return getInterfaceNumber(a.interface) - getInterfaceNumber(b.interface);
+ });
+
+ logMessage(
+ "info",
+ `开始六手依次动画 - 类型:${animationType}, 间隔:${interval}ms, 循环:${cycles}次`,
+ );
+ logMessage(
+ "info",
+ `动画顺序:${sortedHands.map((h) => h.interface).join(" → ")}`,
+ );
+
+ // 定义动画预设
+ const animationPresets = {
+ wave: {
+ name: "手指波浪",
+ fingerPoses: [
+ [255, 255, 255, 255, 255, 255], // 完全张开
+ [128, 128, 128, 128, 128, 128], // 中间位置
+ [64, 64, 64, 64, 64, 64], // 握拳
+ [128, 128, 128, 128, 128, 128], // 回到中间
+ ],
+ palmPose: [128, 128, 128, 128], // 掌部保持中立
+ },
+
+ thumbsUp: {
+ name: "竖拇指传递",
+ fingerPoses: [
+ [255, 255, 0, 0, 0, 0], // 竖拇指
+ [128, 128, 128, 128, 128, 128], // 恢复中立
+ ],
+ palmPose: [128, 128, 128, 128],
+ },
+
+ point: {
+ name: "食指指点传递",
+ fingerPoses: [
+ [0, 0, 255, 0, 0, 0], // 食指指点
+ [128, 128, 128, 128, 128, 128], // 恢复中立
+ ],
+ palmPose: [128, 128, 128, 128],
+ },
+
+ fistOpen: {
+ name: "握拳张开",
+ fingerPoses: [
+ [64, 64, 64, 64, 64, 64], // 握拳
+ [255, 255, 255, 255, 255, 255], // 张开
+ [128, 128, 128, 128, 128, 128], // 中立
+ ],
+ palmPose: [128, 128, 128, 128],
+ },
+
+ numbers: {
+ name: "数字倒计时",
+ fingerPoses: [
+ [255, 255, 255, 255, 255, 255], // 5
+ [0, 57, 255, 255, 255, 255], // 4
+ [0, 57, 255, 255, 255, 0], // 3
+ [0, 57, 255, 255, 0, 0], // 2
+ [0, 57, 255, 0, 0, 0], // 1
+ [64, 64, 64, 64, 64, 64], // 握拳 (0)
+ ],
+ palmPoses: [
+ [255, 109, 255, 118], // 5 对应的掌部
+ [255, 109, 255, 118], // 4 对应的掌部
+ [255, 109, 255, 118], // 3 对应的掌部
+ [255, 109, 255, 118], // 2 对应的掌部
+ [255, 109, 255, 118], // 1 对应的掌部
+ [128, 128, 128, 128], // 0 对应的掌部
+ ],
+ },
+
+ mexican: {
+ name: "墨西哥波浪",
+ fingerPoses: [
+ [64, 64, 64, 64, 64, 64], // 起始握拳
+ [128, 64, 64, 64, 64, 64], // 拇指起
+ [255, 128, 64, 64, 64, 64], // 拇指 + 食指起
+ [255, 255, 128, 64, 64, 64], // 前三指起
+ [255, 255, 255, 128, 64, 64], // 前四指起
+ [255, 255, 255, 255, 128, 64], // 前五指起
+ [255, 255, 255, 255, 255, 255], // 全部张开
+ [128, 255, 255, 255, 255, 128], // 波浪形
+ [64, 128, 255, 255, 128, 64], // 继续波浪
+ [64, 64, 128, 255, 128, 64], // 波浪收尾
+ [64, 64, 64, 128, 64, 64], // 几乎回到握拳
+ [64, 64, 64, 64, 64, 64], // 完全握拳
+ ],
+ palmPose: [128, 128, 128, 128],
+ },
+ };
+
+ const preset = animationPresets[animationType] || animationPresets.wave;
+ const fingerPoses = preset.fingerPoses;
+ const palmPoses =
+ preset.palmPoses || Array(fingerPoses.length).fill(preset.palmPose);
+
+ // 执行动画循环
+ for (let cycle = 0; cycle < cycles; cycle++) {
+ logMessage("info", `${preset.name} - 第 ${cycle + 1}/${cycles} 轮`);
+
+ // 每个动作姿势
+ for (let poseIndex = 0; poseIndex < fingerPoses.length; poseIndex++) {
+ const currentFingerPose = fingerPoses[poseIndex];
+ const currentPalmPose = palmPoses[poseIndex];
+
+ // 依次激活每只手
+ for (let handIndex = 0; handIndex < sortedHands.length; handIndex++) {
+ const hand = sortedHands[handIndex];
+ const handName = hand.handType === "left" ? "左手" : "右手";
+
+ // 先发送掌部姿态
+ await sendPalmPoseToHand(hand, currentPalmPose);
+
+ // 短暂延迟后发送手指姿态
setTimeout(async () => {
- await sendFingerPoseToHand(hand, neutralFingerPose);
+ await sendFingerPoseToHand(hand, currentFingerPose);
}, 50);
- await new Promise(resolve => setTimeout(resolve, 100));
+
+ logMessage(
+ "info",
+ `${hand.interface}(${handName}) 执行姿势 ${poseIndex + 1}/${
+ fingerPoses.length
+ }`,
+ );
+
+ // 等待间隔时间再激活下一只手
+ await new Promise((resolve) => setTimeout(resolve, interval));
+ }
}
-
- logMessage('success', `六手依次动画 "${preset.name}" 完成!`);
+
+ // 循环间隔(如果有多轮)
+ if (cycle < cycles - 1) {
+ logMessage("info", `等待下一轮动画...`);
+ await new Promise((resolve) => setTimeout(resolve, interval * 2));
+ }
+ }
+
+ // 动画结束后,让所有手回到中立位置
+ logMessage("info", "动画完成,恢复中立位置...");
+ const neutralFingerPose = [128, 128, 128, 128, 128, 128];
+ const neutralPalmPose = [128, 128, 128, 128];
+
+ for (const hand of sortedHands) {
+ await sendPalmPoseToHand(hand, neutralPalmPose);
+ setTimeout(async () => {
+ await sendFingerPoseToHand(hand, neutralFingerPose);
+ }, 50);
+ await new Promise((resolve) => setTimeout(resolve, 100));
+ }
+
+ logMessage("success", `六手依次动画 "${preset.name}" 完成!`);
}
// 扩展的动画控制函数
async function startCustomSequentialAnimation(config) {
- const {
- animationType = 'wave',
- interval = 500,
- cycles = 3,
- direction = 'forward', // 'forward', 'backward', 'bounce'
- simultaneousHands = 1, // 同时激活的手数
- staggerDelay = 100 // 同时激活手之间的错开延迟
- } = config;
-
- const enabledHands = getEnabledHands();
-
- if (enabledHands.length === 0) {
- logMessage('error', '没有启用的手部');
- return;
- }
-
- // 根据方向排序手部
- let sortedHands = enabledHands.sort((a, b) => {
- const getInterfaceNumber = (iface) => {
- const match = iface.match(/(\d+)$/);
- return match ? parseInt(match[1]) : 0;
- };
- return getInterfaceNumber(a.interface) - getInterfaceNumber(b.interface);
- });
-
- if (direction === 'backward') {
- sortedHands = sortedHands.reverse();
- }
-
- logMessage('info', `开始自定义六手动画 - 方向:${direction}, 同时手数:${simultaneousHands}`);
-
- // 执行动画逻辑...
- // 这里可以根据 simultaneousHands 参数同时控制多只手
- // 实现类似的动画逻辑,但支持更多自定义选项
+ const {
+ animationType = "wave",
+ interval = 500,
+ cycles = 3,
+ direction = "forward", // 'forward', 'backward', 'bounce'
+ simultaneousHands = 1, // 同时激活的手数
+ staggerDelay = 100, // 同时激活手之间的错开延迟
+ } = config;
+
+ const enabledHands = getEnabledHands();
+
+ if (enabledHands.length === 0) {
+ logMessage("error", "没有启用的手部");
+ return;
+ }
+
+ // 根据方向排序手部
+ let sortedHands = enabledHands.sort((a, b) => {
+ const getInterfaceNumber = (iface) => {
+ const match = iface.match(/(\d+)$/);
+ return match ? Number.parseInt(match[1]) : 0;
+ };
+ return getInterfaceNumber(a.interface) - getInterfaceNumber(b.interface);
+ });
+
+ if (direction === "backward") {
+ sortedHands = sortedHands.reverse();
+ }
+
+ logMessage(
+ "info",
+ `开始自定义六手动画 - 方向:${direction}, 同时手数:${simultaneousHands}`,
+ );
+
+ // 执行动画逻辑...
+ // 这里可以根据 simultaneousHands 参数同时控制多只手
+ // 实现类似的动画逻辑,但支持更多自定义选项
}
// 预定义的快捷动画函数
async function startWaveAnimation() {
- await startSequentialHandAnimation('wave', 300, 2);
+ await startSequentialHandAnimation("wave", 300, 2);
}
async function startThumbsUpRelay() {
- await startSequentialHandAnimation('thumbsUp', 400, 3);
+ await startSequentialHandAnimation("thumbsUp", 400, 3);
}
async function startPointingRelay() {
- await startSequentialHandAnimation('point', 350, 2);
+ await startSequentialHandAnimation("point", 350, 2);
}
async function startNumberCountdown() {
- await startSequentialHandAnimation('numbers', 800, 1);
+ await startSequentialHandAnimation("numbers", 800, 1);
}
async function startMexicanWave() {
- await startSequentialHandAnimation('mexican', 200, 3);
+ await startSequentialHandAnimation("mexican", 200, 3);
}
async function startFistOpenWave() {
- await startSequentialHandAnimation('fistOpen', 400, 2);
+ await startSequentialHandAnimation("fistOpen", 400, 2);
}
// 高级组合动画:先正向再反向
async function startBidirectionalWave() {
- logMessage('info', '开始双向波浪动画...');
-
- // 正向波浪
- await startSequentialHandAnimation('wave', 300, 1);
- await new Promise(resolve => setTimeout(resolve, 500));
-
- // 反向波浪(通过反转手部顺序实现)
- const originalGetEnabledHands = window.getEnabledHands;
- window.getEnabledHands = function() {
- return originalGetEnabledHands().reverse();
- };
-
- await startSequentialHandAnimation('wave', 300, 1);
-
- // 恢复原始函数
- window.getEnabledHands = originalGetEnabledHands;
-
- logMessage('success', '双向波浪动画完成!');
+ logMessage("info", "开始双向波浪动画...");
+
+ // 正向波浪
+ await startSequentialHandAnimation("wave", 300, 1);
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ // 反向波浪(通过反转手部顺序实现)
+ const originalGetEnabledHands = window.getEnabledHands;
+ window.getEnabledHands = () => originalGetEnabledHands().reverse();
+
+ await startSequentialHandAnimation("wave", 300, 1);
+
+ // 恢复原始函数
+ window.getEnabledHands = originalGetEnabledHands;
+
+ logMessage("success", "双向波浪动画完成!");
}
// 导出函数到全局作用域
@@ -1646,4 +1814,3 @@ window.startNumberCountdown = startNumberCountdown;
window.startMexicanWave = startMexicanWave;
window.startFistOpenWave = startFistOpenWave;
window.startBidirectionalWave = startBidirectionalWave;
-