refactor: split hands to can, animation, sensor, hands

This commit is contained in:
Eli Yip 2025-05-27 10:26:24 +08:00
parent 092c4ac85e
commit e057ee1da0
No known key found for this signature in database
GPG Key ID: C98B69D4CF7D7EC5
5 changed files with 425 additions and 390 deletions

268
hands/animation.go Normal file
View File

@ -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)
}
}

95
hands/can.go Normal file
View File

@ -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
}

View File

@ -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
}

53
hands/sensor.go Normal file
View File

@ -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)
}
}()
}

View File

@ -23,7 +23,7 @@ func initService() {
log.Printf(" - 默认接口: %s", config.Config.DefaultInterface)
// 初始化手型配置映射
hands.InitHands()
hands.Init()
log.Println("✅ 控制服务初始化完成")
}