From e057ee1da08eb0d4a6264300075585f2bfe9a4c5 Mon Sep 17 00:00:00 2001 From: Eli Yip Date: Tue, 27 May 2025 10:26:24 +0800 Subject: [PATCH] refactor: split hands to can, animation, sensor, hands --- hands/animation.go | 268 ++++++++++++++++++++++++++++++ hands/can.go | 95 +++++++++++ hands/hands.go | 397 +-------------------------------------------- hands/sensor.go | 53 ++++++ main.go | 2 +- 5 files changed, 425 insertions(+), 390 deletions(-) create mode 100644 hands/animation.go create mode 100644 hands/can.go create mode 100644 hands/sensor.go diff --git a/hands/animation.go b/hands/animation.go new file mode 100644 index 0000000..43c270c --- /dev/null +++ b/hands/animation.go @@ -0,0 +1,268 @@ +package hands + +import ( + "hands/config" + "log" + "sync" + "time" +) + +var ( + AnimationActive map[string]bool // 每个接口的动画状态 + AnimationMutex sync.Mutex + StopAnimationMap map[string]chan struct{} // 每个接口的停止动画通道 +) + +func initAnimation() { + // 初始化动画状态映射 + AnimationActive = make(map[string]bool) + StopAnimationMap = make(map[string]chan struct{}) + for _, ifName := range config.Config.AvailableInterfaces { + AnimationActive[ifName] = false + StopAnimationMap[ifName] = make(chan struct{}, 1) + } +} + +// 执行波浪动画 - 支持手型参数 +func StartWaveAnimation(ifName string, speed int, handType string, handId uint32) { + if speed <= 0 { + speed = 500 // 默认速度 + } + + // 如果未指定接口,使用默认接口 + if ifName == "" { + ifName = config.Config.DefaultInterface + } + + // 验证接口 + if !IsValidInterface(ifName) { + log.Printf("❌ 无法启动波浪动画: 无效的接口 %s", ifName) + return + } + + AnimationMutex.Lock() + + // 如果已经有动画在运行,先停止它 + if AnimationActive[ifName] { + select { + case StopAnimationMap[ifName] <- struct{}{}: + // 发送成功 + default: + // 通道已满,无需发送 + } + + StopAnimationMap[ifName] = make(chan struct{}, 1) + } + + AnimationActive[ifName] = true + AnimationMutex.Unlock() + + currentStopChannel := StopAnimationMap[ifName] + + go func() { + defer func() { + AnimationMutex.Lock() + AnimationActive[ifName] = false + AnimationMutex.Unlock() + log.Printf("👋 %s 波浪动画已完成", ifName) + }() + + fingerOrder := []int{0, 1, 2, 3, 4, 5} + open := byte(64) // 0x40 + close := byte(192) // 0xC0 + + log.Printf("🚀 开始 %s 波浪动画", ifName) + + // 动画循环 + for { + select { + case <-currentStopChannel: + log.Printf("🛑 %s 波浪动画被用户停止", ifName) + return + default: + // 波浪张开 + for _, idx := range fingerOrder { + pose := make([]byte, 6) + for j := 0; j < 6; j++ { + if j == idx { + pose[j] = open + } else { + pose[j] = close + } + } + + if err := SendFingerPose(ifName, pose, handType, handId); err != nil { + log.Printf("%s 动画发送失败: %v", ifName, err) + return + } + + delay := time.Duration(speed) * time.Millisecond + + select { + case <-currentStopChannel: + log.Printf("🛑 %s 波浪动画被用户停止", ifName) + return + case <-time.After(delay): + // 继续执行 + } + } + + // 波浪握拳 + for _, idx := range fingerOrder { + pose := make([]byte, 6) + for j := 0; j < 6; j++ { + if j == idx { + pose[j] = close + } else { + pose[j] = open + } + } + + if err := SendFingerPose(ifName, pose, handType, handId); err != nil { + log.Printf("%s 动画发送失败: %v", ifName, err) + return + } + + delay := time.Duration(speed) * time.Millisecond + + select { + case <-currentStopChannel: + log.Printf("🛑 %s 波浪动画被用户停止", ifName) + return + case <-time.After(delay): + // 继续执行 + } + } + } + } + }() +} + +// 执行横向摆动动画 - 支持手型参数 +func StartSwayAnimation(ifName string, speed int, handType string, handId uint32) { + if speed <= 0 { + speed = 500 // 默认速度 + } + + // 如果未指定接口,使用默认接口 + if ifName == "" { + ifName = config.Config.DefaultInterface + } + + // 验证接口 + if !IsValidInterface(ifName) { + log.Printf("❌ 无法启动摆动动画: 无效的接口 %s", ifName) + return + } + + AnimationMutex.Lock() + + if AnimationActive[ifName] { + select { + case StopAnimationMap[ifName] <- struct{}{}: + // 发送成功 + default: + // 通道已满,无需发送 + } + + StopAnimationMap[ifName] = make(chan struct{}, 1) + } + + AnimationActive[ifName] = true + AnimationMutex.Unlock() + + currentStopChannel := StopAnimationMap[ifName] + + go func() { + defer func() { + AnimationMutex.Lock() + AnimationActive[ifName] = false + AnimationMutex.Unlock() + log.Printf("🔄 %s 横向摆动动画已完成", ifName) + }() + + leftPose := []byte{48, 48, 48, 48} // 0x30 + rightPose := []byte{208, 208, 208, 208} // 0xD0 + + log.Printf("🚀 开始 %s 横向摆动动画", ifName) + + // 动画循环 + for { + select { + case <-currentStopChannel: + log.Printf("🛑 %s 横向摆动动画被用户停止", ifName) + return + default: + // 向左移动 + if err := SendPalmPose(ifName, leftPose, handType, handId); err != nil { + log.Printf("%s 动画发送失败: %v", ifName, err) + return + } + + delay := time.Duration(speed) * time.Millisecond + + select { + case <-currentStopChannel: + log.Printf("🛑 %s 横向摆动动画被用户停止", ifName) + return + case <-time.After(delay): + // 继续执行 + } + + // 向右移动 + if err := SendPalmPose(ifName, rightPose, handType, handId); err != nil { + log.Printf("%s 动画发送失败: %v", ifName, err) + return + } + + select { + case <-currentStopChannel: + log.Printf("🛑 %s 横向摆动动画被用户停止", ifName) + return + case <-time.After(delay): + // 继续执行 + } + } + } + }() +} + +// 停止所有动画 +func StopAllAnimations(ifName string) { + // 如果未指定接口,停止所有接口的动画 + if ifName == "" { + for _, validIface := range config.Config.AvailableInterfaces { + StopAllAnimations(validIface) + } + return + } + + // 验证接口 + if !IsValidInterface(ifName) { + log.Printf("⚠️ 尝试停止无效接口的动画: %s", ifName) + return + } + + AnimationMutex.Lock() + defer AnimationMutex.Unlock() + + if AnimationActive[ifName] { + select { + case StopAnimationMap[ifName] <- struct{}{}: + log.Printf("✅ 已发送停止 %s 动画信号", ifName) + default: + StopAnimationMap[ifName] = make(chan struct{}, 1) + StopAnimationMap[ifName] <- struct{}{} + log.Printf("⚠️ %s 通道重置后发送了停止信号", ifName) + } + + AnimationActive[ifName] = false + + go func() { + time.Sleep(100 * time.Millisecond) + resetToDefaultPose(ifName) + }() + } else { + log.Printf("ℹ️ %s 当前没有运行中的动画", ifName) + } +} diff --git a/hands/can.go b/hands/can.go new file mode 100644 index 0000000..15e3baf --- /dev/null +++ b/hands/can.go @@ -0,0 +1,95 @@ +package hands + +import ( + "bytes" + "encoding/json" + "fmt" + "hands/config" + "hands/define" + "log" + "net/http" +) + +type CanMessage struct { + Interface string `json:"interface"` + ID uint32 `json:"id"` + Data []byte `json:"data"` +} + +// 检查 CAN 服务状态 +func CheckCanServiceStatus() map[string]bool { + resp, err := http.Get(config.Config.CanServiceURL + "/api/status") + if err != nil { + log.Printf("❌ CAN 服务状态检查失败: %v", err) + result := make(map[string]bool) + for _, ifName := range config.Config.AvailableInterfaces { + result[ifName] = false + } + return result + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + log.Printf("❌ CAN 服务返回非正常状态:%d", resp.StatusCode) + result := make(map[string]bool) + for _, ifName := range config.Config.AvailableInterfaces { + result[ifName] = false + } + return result + } + + var statusResp define.ApiResponse + if err := json.NewDecoder(resp.Body).Decode(&statusResp); err != nil { + log.Printf("❌ 解析 CAN 服务状态失败: %v", err) + result := make(map[string]bool) + for _, ifName := range config.Config.AvailableInterfaces { + result[ifName] = false + } + return result + } + + // 检查状态数据 + result := make(map[string]bool) + for _, ifName := range config.Config.AvailableInterfaces { + result[ifName] = false + } + + // 从响应中获取各接口状态 + if statusData, ok := statusResp.Data.(map[string]interface{}); ok { + if interfaces, ok := statusData["interfaces"].(map[string]interface{}); ok { + for ifName, ifStatus := range interfaces { + if status, ok := ifStatus.(map[string]interface{}); ok { + if active, ok := status["active"].(bool); ok { + result[ifName] = active + } + } + } + } + } + + return result +} + +// 发送请求到 CAN 服务 +func sendToCanService(msg CanMessage) error { + jsonData, err := json.Marshal(msg) + if err != nil { + return fmt.Errorf("JSON 编码错误: %v", err) + } + + resp, err := http.Post(config.Config.CanServiceURL+"/api/can", "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + return fmt.Errorf("CAN 服务请求失败: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + var errResp define.ApiResponse + if err := json.NewDecoder(resp.Body).Decode(&errResp); err != nil { + return fmt.Errorf("CAN 服务返回错误:HTTP %d", resp.StatusCode) + } + return fmt.Errorf("CAN 服务返回错误: %s", errResp.Error) + } + + return nil +} diff --git a/hands/hands.go b/hands/hands.go index 089d5a9..5c247ad 100644 --- a/hands/hands.go +++ b/hands/hands.go @@ -1,14 +1,12 @@ package hands import ( - "bytes" - "encoding/json" "fmt" "hands/config" "hands/define" "log" "math/rand/v2" - "net/http" + "slices" "strings" "sync" "time" @@ -35,38 +33,15 @@ type HandConfig struct { var ( HandConfigMutex sync.RWMutex HandConfigs map[string]*HandConfig // 每个接口的手型配置 - - SensorDataMap map[string]*SensorData // 每个接口的传感器数据 - SensorMutex sync.RWMutex - AnimationActive map[string]bool // 每个接口的动画状态 - AnimationMutex sync.Mutex - StopAnimationMap map[string]chan struct{} // 每个接口的停止动画通道 ) -func InitHands() { - // 初始化传感器数据映射 - SensorDataMap = make(map[string]*SensorData) - for _, ifName := range config.Config.AvailableInterfaces { - SensorDataMap[ifName] = &SensorData{ - Interface: ifName, - Thumb: 0, - Index: 0, - Middle: 0, - Ring: 0, - Pinky: 0, - PalmPosition: []byte{128, 128, 128, 128}, - LastUpdate: time.Now(), - } - } - - // 初始化动画状态映射 - AnimationActive = make(map[string]bool) - StopAnimationMap = make(map[string]chan struct{}) - for _, ifName := range config.Config.AvailableInterfaces { - AnimationActive[ifName] = false - StopAnimationMap[ifName] = make(chan struct{}, 1) - } +func Init() { + initSensorData() + initAnimation() + initHands() +} +func initHands() { HandConfigs = make(map[string]*HandConfig) } @@ -131,18 +106,7 @@ func ParseHandType(handType string, handId uint32, ifName string) uint32 { // 验证接口是否可用 func IsValidInterface(ifName string) bool { - for _, validIface := range config.Config.AvailableInterfaces { - if ifName == validIface { - return true - } - } - return false -} - -type CanMessage struct { - Interface string `json:"interface"` - ID uint32 `json:"id"` - Data []byte `json:"data"` + return slices.Contains(config.Config.AvailableInterfaces, ifName) } // 发送手指姿态指令 - 支持手型参数 @@ -206,30 +170,6 @@ func perturb(base byte, delta int) byte { return byte(v) } -// 发送请求到 CAN 服务 -func sendToCanService(msg CanMessage) error { - jsonData, err := json.Marshal(msg) - if err != nil { - return fmt.Errorf("JSON 编码错误: %v", err) - } - - resp, err := http.Post(config.Config.CanServiceURL+"/api/can", "application/json", bytes.NewBuffer(jsonData)) - if err != nil { - return fmt.Errorf("CAN 服务请求失败: %v", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - var errResp define.ApiResponse - if err := json.NewDecoder(resp.Body).Decode(&errResp); err != nil { - return fmt.Errorf("CAN 服务返回错误:HTTP %d", resp.StatusCode) - } - return fmt.Errorf("CAN 服务返回错误: %s", errResp.Error) - } - - return nil -} - // 发送掌部姿态指令 - 支持手型参数 func SendPalmPose(ifName string, pose []byte, handType string, handId uint32) error { if len(pose) != 4 { @@ -285,250 +225,6 @@ func SendPalmPose(ifName string, pose []byte, handType string, handId uint32) er return err } -// 执行波浪动画 - 支持手型参数 -func StartWaveAnimation(ifName string, speed int, handType string, handId uint32) { - if speed <= 0 { - speed = 500 // 默认速度 - } - - // 如果未指定接口,使用默认接口 - if ifName == "" { - ifName = config.Config.DefaultInterface - } - - // 验证接口 - if !IsValidInterface(ifName) { - log.Printf("❌ 无法启动波浪动画: 无效的接口 %s", ifName) - return - } - - AnimationMutex.Lock() - - // 如果已经有动画在运行,先停止它 - if AnimationActive[ifName] { - select { - case StopAnimationMap[ifName] <- struct{}{}: - // 发送成功 - default: - // 通道已满,无需发送 - } - - StopAnimationMap[ifName] = make(chan struct{}, 1) - } - - AnimationActive[ifName] = true - AnimationMutex.Unlock() - - currentStopChannel := StopAnimationMap[ifName] - - go func() { - defer func() { - AnimationMutex.Lock() - AnimationActive[ifName] = false - AnimationMutex.Unlock() - log.Printf("👋 %s 波浪动画已完成", ifName) - }() - - fingerOrder := []int{0, 1, 2, 3, 4, 5} - open := byte(64) // 0x40 - close := byte(192) // 0xC0 - - log.Printf("🚀 开始 %s 波浪动画", ifName) - - // 动画循环 - for { - select { - case <-currentStopChannel: - log.Printf("🛑 %s 波浪动画被用户停止", ifName) - return - default: - // 波浪张开 - for _, idx := range fingerOrder { - pose := make([]byte, 6) - for j := 0; j < 6; j++ { - if j == idx { - pose[j] = open - } else { - pose[j] = close - } - } - - if err := SendFingerPose(ifName, pose, handType, handId); err != nil { - log.Printf("%s 动画发送失败: %v", ifName, err) - return - } - - delay := time.Duration(speed) * time.Millisecond - - select { - case <-currentStopChannel: - log.Printf("🛑 %s 波浪动画被用户停止", ifName) - return - case <-time.After(delay): - // 继续执行 - } - } - - // 波浪握拳 - for _, idx := range fingerOrder { - pose := make([]byte, 6) - for j := 0; j < 6; j++ { - if j == idx { - pose[j] = close - } else { - pose[j] = open - } - } - - if err := SendFingerPose(ifName, pose, handType, handId); err != nil { - log.Printf("%s 动画发送失败: %v", ifName, err) - return - } - - delay := time.Duration(speed) * time.Millisecond - - select { - case <-currentStopChannel: - log.Printf("🛑 %s 波浪动画被用户停止", ifName) - return - case <-time.After(delay): - // 继续执行 - } - } - } - } - }() -} - -// 执行横向摆动动画 - 支持手型参数 -func StartSwayAnimation(ifName string, speed int, handType string, handId uint32) { - if speed <= 0 { - speed = 500 // 默认速度 - } - - // 如果未指定接口,使用默认接口 - if ifName == "" { - ifName = config.Config.DefaultInterface - } - - // 验证接口 - if !IsValidInterface(ifName) { - log.Printf("❌ 无法启动摆动动画: 无效的接口 %s", ifName) - return - } - - AnimationMutex.Lock() - - if AnimationActive[ifName] { - select { - case StopAnimationMap[ifName] <- struct{}{}: - // 发送成功 - default: - // 通道已满,无需发送 - } - - StopAnimationMap[ifName] = make(chan struct{}, 1) - } - - AnimationActive[ifName] = true - AnimationMutex.Unlock() - - currentStopChannel := StopAnimationMap[ifName] - - go func() { - defer func() { - AnimationMutex.Lock() - AnimationActive[ifName] = false - AnimationMutex.Unlock() - log.Printf("🔄 %s 横向摆动动画已完成", ifName) - }() - - leftPose := []byte{48, 48, 48, 48} // 0x30 - rightPose := []byte{208, 208, 208, 208} // 0xD0 - - log.Printf("🚀 开始 %s 横向摆动动画", ifName) - - // 动画循环 - for { - select { - case <-currentStopChannel: - log.Printf("🛑 %s 横向摆动动画被用户停止", ifName) - return - default: - // 向左移动 - if err := SendPalmPose(ifName, leftPose, handType, handId); err != nil { - log.Printf("%s 动画发送失败: %v", ifName, err) - return - } - - delay := time.Duration(speed) * time.Millisecond - - select { - case <-currentStopChannel: - log.Printf("🛑 %s 横向摆动动画被用户停止", ifName) - return - case <-time.After(delay): - // 继续执行 - } - - // 向右移动 - if err := SendPalmPose(ifName, rightPose, handType, handId); err != nil { - log.Printf("%s 动画发送失败: %v", ifName, err) - return - } - - select { - case <-currentStopChannel: - log.Printf("🛑 %s 横向摆动动画被用户停止", ifName) - return - case <-time.After(delay): - // 继续执行 - } - } - } - }() -} - -// 停止所有动画 -func StopAllAnimations(ifName string) { - // 如果未指定接口,停止所有接口的动画 - if ifName == "" { - for _, validIface := range config.Config.AvailableInterfaces { - StopAllAnimations(validIface) - } - return - } - - // 验证接口 - if !IsValidInterface(ifName) { - log.Printf("⚠️ 尝试停止无效接口的动画: %s", ifName) - return - } - - AnimationMutex.Lock() - defer AnimationMutex.Unlock() - - if AnimationActive[ifName] { - select { - case StopAnimationMap[ifName] <- struct{}{}: - log.Printf("✅ 已发送停止 %s 动画信号", ifName) - default: - StopAnimationMap[ifName] = make(chan struct{}, 1) - StopAnimationMap[ifName] <- struct{}{} - log.Printf("⚠️ %s 通道重置后发送了停止信号", ifName) - } - - AnimationActive[ifName] = false - - go func() { - time.Sleep(100 * time.Millisecond) - resetToDefaultPose(ifName) - }() - } else { - log.Printf("ℹ️ %s 当前没有运行中的动画", ifName) - } -} - // 重置到默认姿势 func resetToDefaultPose(ifName string) { // 如果未指定接口,重置所有接口 @@ -561,80 +257,3 @@ func resetToDefaultPose(ifName string) { log.Printf("✅ 已重置 %s 到默认姿势", ifName) } - -// 读取传感器数据 (模拟) -func ReadSensorData() { - go func() { - for { - SensorMutex.Lock() - // 为每个接口模拟压力数据 (0-100) - for _, ifName := range config.Config.AvailableInterfaces { - if sensorData, exists := SensorDataMap[ifName]; exists { - sensorData.Thumb = rand.IntN(101) - sensorData.Index = rand.IntN(101) - sensorData.Middle = rand.IntN(101) - sensorData.Ring = rand.IntN(101) - sensorData.Pinky = rand.IntN(101) - sensorData.LastUpdate = time.Now() - } - } - SensorMutex.Unlock() - - time.Sleep(500 * time.Millisecond) - } - }() -} - -// 检查 CAN 服务状态 -func CheckCanServiceStatus() map[string]bool { - resp, err := http.Get(config.Config.CanServiceURL + "/api/status") - if err != nil { - log.Printf("❌ CAN 服务状态检查失败: %v", err) - result := make(map[string]bool) - for _, ifName := range config.Config.AvailableInterfaces { - result[ifName] = false - } - return result - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - log.Printf("❌ CAN 服务返回非正常状态:%d", resp.StatusCode) - result := make(map[string]bool) - for _, ifName := range config.Config.AvailableInterfaces { - result[ifName] = false - } - return result - } - - var statusResp define.ApiResponse - if err := json.NewDecoder(resp.Body).Decode(&statusResp); err != nil { - log.Printf("❌ 解析 CAN 服务状态失败: %v", err) - result := make(map[string]bool) - for _, ifName := range config.Config.AvailableInterfaces { - result[ifName] = false - } - return result - } - - // 检查状态数据 - result := make(map[string]bool) - for _, ifName := range config.Config.AvailableInterfaces { - result[ifName] = false - } - - // 从响应中获取各接口状态 - if statusData, ok := statusResp.Data.(map[string]interface{}); ok { - if interfaces, ok := statusData["interfaces"].(map[string]interface{}); ok { - for ifName, ifStatus := range interfaces { - if status, ok := ifStatus.(map[string]interface{}); ok { - if active, ok := status["active"].(bool); ok { - result[ifName] = active - } - } - } - } - } - - return result -} diff --git a/hands/sensor.go b/hands/sensor.go new file mode 100644 index 0000000..515abe3 --- /dev/null +++ b/hands/sensor.go @@ -0,0 +1,53 @@ +package hands + +import ( + "hands/config" + "math/rand/v2" + "sync" + "time" +) + +var ( + SensorDataMap map[string]*SensorData // 每个接口的传感器数据 + SensorMutex sync.RWMutex +) + +func initSensorData() { + // 初始化传感器数据映射 + SensorDataMap = make(map[string]*SensorData) + for _, ifName := range config.Config.AvailableInterfaces { + SensorDataMap[ifName] = &SensorData{ + Interface: ifName, + Thumb: 0, + Index: 0, + Middle: 0, + Ring: 0, + Pinky: 0, + PalmPosition: []byte{128, 128, 128, 128}, + LastUpdate: time.Now(), + } + } +} + +// 读取传感器数据 (模拟) +func ReadSensorData() { + go func() { + for { + SensorMutex.Lock() + // 为每个接口模拟压力数据 (0-100) + for _, ifName := range config.Config.AvailableInterfaces { + if sensorData, exists := SensorDataMap[ifName]; exists { + sensorData.Thumb = rand.IntN(101) + sensorData.Index = rand.IntN(101) + sensorData.Middle = rand.IntN(101) + sensorData.Ring = rand.IntN(101) + sensorData.Pinky = rand.IntN(101) + sensorData.LastUpdate = time.Now() + } + } + SensorMutex.Unlock() + + time.Sleep(500 * time.Millisecond) + } + }() +} diff --git a/main.go b/main.go index 2367549..1f6c93e 100644 --- a/main.go +++ b/main.go @@ -23,7 +23,7 @@ func initService() { log.Printf(" - 默认接口: %s", config.Config.DefaultInterface) // 初始化手型配置映射 - hands.InitHands() + hands.Init() log.Println("✅ 控制服务初始化完成") }