2025-05-27 09:11:30 +08:00

1246 lines
33 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"bytes"
"encoding/json"
"fmt"
"hands/cli"
"hands/define"
"log"
"math/rand/v2"
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
const HAND_TYPE_LEFT = 0x28
const HAND_TYPE_RIGHT = 0x27
// API 请求结构体 - 添加手型支持
type FingerPoseRequest struct {
Interface string `json:"interface,omitempty"`
Pose []byte `json:"pose" binding:"required,len=6"`
HandType string `json:"handType,omitempty"` // 新增:手型类型
HandId uint32 `json:"handId,omitempty"` // 新增CAN ID
}
type PalmPoseRequest struct {
Interface string `json:"interface,omitempty"`
Pose []byte `json:"pose" binding:"required,len=4"`
HandType string `json:"handType,omitempty"` // 新增:手型类型
HandId uint32 `json:"handId,omitempty"` // 新增CAN ID
}
type AnimationRequest struct {
Interface string `json:"interface,omitempty"`
Type string `json:"type" binding:"required,oneof=wave sway stop"`
Speed int `json:"speed" binding:"min=0,max=2000"`
HandType string `json:"handType,omitempty"` // 新增:手型类型
HandId uint32 `json:"handId,omitempty"` // 新增CAN ID
}
// 新增:手型设置请求
type HandTypeRequest struct {
Interface string `json:"interface" binding:"required"`
HandType string `json:"handType" binding:"required,oneof=left right"`
HandId uint32 `json:"handId" binding:"required"`
}
// CAN 服务请求结构体
type CanMessage struct {
Interface string `json:"interface"`
ID uint32 `json:"id"`
Data []byte `json:"data"`
}
// 传感器数据结构体
type SensorData struct {
Interface string `json:"interface"`
Thumb int `json:"thumb"`
Index int `json:"index"`
Middle int `json:"middle"`
Ring int `json:"ring"`
Pinky int `json:"pinky"`
PalmPosition []byte `json:"palmPosition"`
LastUpdate time.Time `json:"lastUpdate"`
}
// 手型配置结构体
type HandConfig struct {
HandType string `json:"handType"`
HandId uint32 `json:"handId"`
}
// 全局变量
var (
sensorDataMap map[string]*SensorData // 每个接口的传感器数据
sensorMutex sync.RWMutex
animationActive map[string]bool // 每个接口的动画状态
animationMutex sync.Mutex
stopAnimationMap map[string]chan struct{} // 每个接口的停止动画通道
handConfigs map[string]*HandConfig // 每个接口的手型配置
handConfigMutex sync.RWMutex
config *define.Config
serverStartTime time.Time
)
// 验证接口是否可用
func isValidInterface(ifName string) bool {
for _, validIface := range config.AvailableInterfaces {
if ifName == validIface {
return true
}
}
return false
}
// 获取或创建手型配置
func getHandConfig(ifName string) *HandConfig {
handConfigMutex.RLock()
if handConfig, exists := handConfigs[ifName]; exists {
handConfigMutex.RUnlock()
return handConfig
}
handConfigMutex.RUnlock()
// 创建默认配置
handConfigMutex.Lock()
defer handConfigMutex.Unlock()
// 再次检查(双重检查锁定)
if handConfig, exists := handConfigs[ifName]; exists {
return handConfig
}
// 创建默认配置(右手)
handConfigs[ifName] = &HandConfig{
HandType: "right",
HandId: HAND_TYPE_RIGHT,
}
log.Printf("🆕 为接口 %s 创建默认手型配置: 右手 (0x%X)", ifName, HAND_TYPE_RIGHT)
return handConfigs[ifName]
}
// 设置手型配置
func setHandConfig(ifName, handType string, handId uint32) {
handConfigMutex.Lock()
defer handConfigMutex.Unlock()
handConfigs[ifName] = &HandConfig{
HandType: handType,
HandId: handId,
}
log.Printf("🔧 接口 %s 手型配置已更新: %s (0x%X)", ifName, handType, handId)
}
// 解析手型参数
func parseHandType(handType string, handId uint32, ifName string) uint32 {
// 如果提供了有效的 handId直接使用
if handId != 0 {
return handId
}
// 根据 handType 字符串确定 ID
switch strings.ToLower(handType) {
case "left":
return HAND_TYPE_LEFT
case "right":
return HAND_TYPE_RIGHT
default:
// 使用接口的配置
handConfig := getHandConfig(ifName)
return handConfig.HandId
}
}
// 初始化服务
func initService() {
log.Printf("🔧 服务配置:")
log.Printf(" - CAN 服务 URL: %s", config.CanServiceURL)
log.Printf(" - Web 端口: %s", config.WebPort)
log.Printf(" - 可用接口: %v", config.AvailableInterfaces)
log.Printf(" - 默认接口: %s", config.DefaultInterface)
// 初始化传感器数据映射
sensorDataMap = make(map[string]*SensorData)
for _, ifName := range 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.AvailableInterfaces {
animationActive[ifName] = false
stopAnimationMap[ifName] = make(chan struct{}, 1)
}
// 初始化手型配置映射
handConfigs = make(map[string]*HandConfig)
log.Println("✅ 控制服务初始化完成")
}
// 发送请求到 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.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 sendFingerPose(ifName string, pose []byte, handType string, handId uint32) error {
if len(pose) != 6 {
return fmt.Errorf("无效的姿态数据长度,需要 6 个字节")
}
// 如果未指定接口,使用默认接口
if ifName == "" {
ifName = config.DefaultInterface
}
// 验证接口
if !isValidInterface(ifName) {
return fmt.Errorf("无效的接口 %s可用接口: %v", ifName, config.AvailableInterfaces)
}
// 解析手型 ID
canId := parseHandType(handType, handId, ifName)
// 添加随机扰动
perturbedPose := make([]byte, len(pose))
for i, v := range pose {
perturbedPose[i] = perturb(v, 5)
}
// 构造 CAN 消息
msg := CanMessage{
Interface: ifName,
ID: canId, // 使用动态的手型 ID
Data: append([]byte{0x01}, perturbedPose...),
}
err := sendToCanService(msg)
if err == nil {
handTypeName := "右手"
if canId == HAND_TYPE_LEFT {
handTypeName = "左手"
}
log.Printf("✅ %s (%s, 0x%X) 手指动作已发送: [%X %X %X %X %X %X]",
ifName, handTypeName, canId, perturbedPose[0], perturbedPose[1], perturbedPose[2],
perturbedPose[3], perturbedPose[4], perturbedPose[5])
} else {
log.Printf("❌ %s 手指控制发送失败: %v", ifName, err)
}
return err
}
// 发送掌部姿态指令 - 支持手型参数
func sendPalmPose(ifName string, pose []byte, handType string, handId uint32) error {
if len(pose) != 4 {
return fmt.Errorf("无效的姿态数据长度,需要 4 个字节")
}
// 如果未指定接口,使用默认接口
if ifName == "" {
ifName = config.DefaultInterface
}
// 验证接口
if !isValidInterface(ifName) {
return fmt.Errorf("无效的接口 %s可用接口: %v", ifName, config.AvailableInterfaces)
}
// 解析手型 ID
canId := parseHandType(handType, handId, ifName)
// 添加随机扰动
perturbedPose := make([]byte, len(pose))
for i, v := range pose {
perturbedPose[i] = perturb(v, 8)
}
// 构造 CAN 消息
msg := CanMessage{
Interface: ifName,
ID: canId, // 使用动态的手型 ID
Data: append([]byte{0x04}, perturbedPose...),
}
err := sendToCanService(msg)
if err == nil {
handTypeName := "右手"
if canId == HAND_TYPE_LEFT {
handTypeName = "左手"
}
log.Printf("✅ %s (%s, 0x%X) 掌部姿态已发送: [%X %X %X %X]",
ifName, handTypeName, canId, perturbedPose[0], perturbedPose[1], perturbedPose[2], perturbedPose[3])
// 更新传感器数据中的掌部位置
sensorMutex.Lock()
if sensorData, exists := sensorDataMap[ifName]; exists {
copy(sensorData.PalmPosition, perturbedPose)
sensorData.LastUpdate = time.Now()
}
sensorMutex.Unlock()
} else {
log.Printf("❌ %s 掌部控制发送失败: %v", ifName, err)
}
return err
}
// 在 base 基础上进行 ±delta 的扰动,范围限制在 [0, 255]
func perturb(base byte, delta int) byte {
offset := rand.IntN(2*delta+1) - delta
v := int(base) + offset
if v < 0 {
v = 0
}
if v > 255 {
v = 255
}
return byte(v)
}
// 执行波浪动画 - 支持手型参数
func startWaveAnimation(ifName string, speed int, handType string, handId uint32) {
if speed <= 0 {
speed = 500 // 默认速度
}
// 如果未指定接口,使用默认接口
if ifName == "" {
ifName = 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.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.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) {
// 如果未指定接口,重置所有接口
if ifName == "" {
for _, validIface := range config.AvailableInterfaces {
resetToDefaultPose(validIface)
}
return
}
// 验证接口
if !isValidInterface(ifName) {
log.Printf("⚠️ 尝试重置无效接口: %s", ifName)
return
}
defaultFingerPose := []byte{64, 64, 64, 64, 64, 64}
defaultPalmPose := []byte{128, 128, 128, 128}
// 获取当前接口的手型配置
handConfig := getHandConfig(ifName)
if err := sendFingerPose(ifName, defaultFingerPose, handConfig.HandType, handConfig.HandId); err != nil {
log.Printf("%s 重置手指姿势失败: %v", ifName, err)
}
if err := sendPalmPose(ifName, defaultPalmPose, handConfig.HandType, handConfig.HandId); err != nil {
log.Printf("%s 重置掌部姿势失败: %v", ifName, err)
}
log.Printf("✅ 已重置 %s 到默认姿势", ifName)
}
// 读取传感器数据 (模拟)
func readSensorData() {
go func() {
for {
sensorMutex.Lock()
// 为每个接口模拟压力数据 (0-100)
for _, ifName := range 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.CanServiceURL + "/api/status")
if err != nil {
log.Printf("❌ CAN 服务状态检查失败: %v", err)
result := make(map[string]bool)
for _, ifName := range 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.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.AvailableInterfaces {
result[ifName] = false
}
return result
}
// 检查状态数据
result := make(map[string]bool)
for _, ifName := range 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
}
// API 路由设置
func setupRoutes(r *gin.Engine) {
r.StaticFile("/", "./static/index.html")
r.Static("/static", "./static")
api := r.Group("/api")
{
// 手型设置 API - 新增
api.POST("/hand-type", func(c *gin.Context) {
var req HandTypeRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "无效的手型设置请求:" + err.Error(),
})
return
}
// 验证接口
if !isValidInterface(req.Interface) {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: fmt.Sprintf("无效的接口 %s可用接口: %v", req.Interface, config.AvailableInterfaces),
})
return
}
// 验证手型 ID
if req.HandType == "left" && req.HandId != HAND_TYPE_LEFT {
req.HandId = HAND_TYPE_LEFT
} else if req.HandType == "right" && req.HandId != HAND_TYPE_RIGHT {
req.HandId = HAND_TYPE_RIGHT
}
// 设置手型配置
setHandConfig(req.Interface, req.HandType, req.HandId)
handTypeName := "右手"
if req.HandType == "left" {
handTypeName = "左手"
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: fmt.Sprintf("接口 %s 手型已设置为%s (0x%X)", req.Interface, handTypeName, req.HandId),
Data: map[string]interface{}{
"interface": req.Interface,
"handType": req.HandType,
"handId": req.HandId,
},
})
})
// 手指姿态 API - 更新支持手型
api.POST("/fingers", func(c *gin.Context) {
var req FingerPoseRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "无效的手指姿态数据:" + err.Error(),
})
return
}
// 验证每个值是否在范围内
for _, v := range req.Pose {
if v < 0 || v > 255 {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "手指姿态值必须在 0-255 范围内",
})
return
}
}
// 如果未指定接口,使用默认接口
if req.Interface == "" {
req.Interface = config.DefaultInterface
}
// 验证接口
if !isValidInterface(req.Interface) {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: fmt.Sprintf("无效的接口 %s可用接口: %v", req.Interface, config.AvailableInterfaces),
})
return
}
stopAllAnimations(req.Interface)
if err := sendFingerPose(req.Interface, req.Pose, req.HandType, req.HandId); err != nil {
c.JSON(http.StatusInternalServerError, define.ApiResponse{
Status: "error",
Error: "发送手指姿态失败:" + err.Error(),
})
return
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: "手指姿态指令发送成功",
Data: map[string]interface{}{"interface": req.Interface, "pose": req.Pose},
})
})
// 掌部姿态 API - 更新支持手型
api.POST("/palm", func(c *gin.Context) {
var req PalmPoseRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "无效的掌部姿态数据:" + err.Error(),
})
return
}
// 验证每个值是否在范围内
for _, v := range req.Pose {
if v < 0 || v > 255 {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "掌部姿态值必须在 0-255 范围内",
})
return
}
}
// 如果未指定接口,使用默认接口
if req.Interface == "" {
req.Interface = config.DefaultInterface
}
// 验证接口
if !isValidInterface(req.Interface) {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: fmt.Sprintf("无效的接口 %s可用接口: %v", req.Interface, config.AvailableInterfaces),
})
return
}
stopAllAnimations(req.Interface)
if err := sendPalmPose(req.Interface, req.Pose, req.HandType, req.HandId); err != nil {
c.JSON(http.StatusInternalServerError, define.ApiResponse{
Status: "error",
Error: "发送掌部姿态失败:" + err.Error(),
})
return
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: "掌部姿态指令发送成功",
Data: map[string]interface{}{"interface": req.Interface, "pose": req.Pose},
})
})
// 预设姿势 API - 更新支持手型
api.POST("/preset/:pose", func(c *gin.Context) {
pose := c.Param("pose")
// 从查询参数获取接口名称和手型
ifName := c.Query("interface")
handType := c.Query("handType")
if ifName == "" {
ifName = config.DefaultInterface
}
// 验证接口
if !isValidInterface(ifName) {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: fmt.Sprintf("无效的接口 %s可用接口: %v", ifName, config.AvailableInterfaces),
})
return
}
stopAllAnimations(ifName)
var fingerPose []byte
var message string
switch pose {
case "fist":
fingerPose = []byte{64, 64, 64, 64, 64, 64}
message = "已设置握拳姿势"
case "open":
fingerPose = []byte{192, 192, 192, 192, 192, 192}
message = "已设置完全张开姿势"
case "pinch":
fingerPose = []byte{120, 120, 64, 64, 64, 64}
message = "已设置捏取姿势"
case "thumbsup":
fingerPose = []byte{64, 192, 192, 192, 192, 64}
message = "已设置竖起大拇指姿势"
case "point":
fingerPose = []byte{192, 64, 192, 192, 192, 64}
message = "已设置食指指点姿势"
// 数字手势
case "1":
fingerPose = []byte{192, 64, 192, 192, 192, 64}
message = "已设置数字 1 手势"
case "2":
fingerPose = []byte{192, 64, 64, 192, 192, 64}
message = "已设置数字 2 手势"
case "3":
fingerPose = []byte{192, 64, 64, 64, 192, 64}
message = "已设置数字 3 手势"
case "4":
fingerPose = []byte{192, 64, 64, 64, 64, 64}
message = "已设置数字 4 手势"
case "5":
fingerPose = []byte{192, 192, 192, 192, 192, 192}
message = "已设置数字 5 手势"
case "6":
fingerPose = []byte{64, 192, 192, 192, 192, 64}
message = "已设置数字 6 手势"
case "7":
fingerPose = []byte{64, 64, 192, 192, 192, 64}
message = "已设置数字 7 手势"
case "8":
fingerPose = []byte{64, 64, 64, 192, 192, 64}
message = "已设置数字 8 手势"
case "9":
fingerPose = []byte{64, 64, 64, 64, 192, 64}
message = "已设置数字 9 手势"
default:
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "无效的预设姿势",
})
return
}
// 解析手型 ID从查询参数或使用接口配置
handId := uint32(0)
if handType != "" {
handId = parseHandType(handType, 0, ifName)
}
if err := sendFingerPose(ifName, fingerPose, handType, handId); err != nil {
c.JSON(http.StatusInternalServerError, define.ApiResponse{
Status: "error",
Error: "设置预设姿势失败:" + err.Error(),
})
return
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: message,
Data: map[string]interface{}{"interface": ifName, "pose": fingerPose},
})
})
// 动画控制 API - 更新支持手型
api.POST("/animation", func(c *gin.Context) {
var req AnimationRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "无效的动画请求:" + err.Error(),
})
return
}
// 如果未指定接口,使用默认接口
if req.Interface == "" {
req.Interface = config.DefaultInterface
}
// 验证接口
if !isValidInterface(req.Interface) {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: fmt.Sprintf("无效的接口 %s可用接口: %v", req.Interface, config.AvailableInterfaces),
})
return
}
// 停止当前动画
stopAllAnimations(req.Interface)
// 如果是停止命令,直接返回
if req.Type == "stop" {
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: fmt.Sprintf("%s 动画已停止", req.Interface),
})
return
}
// 处理速度参数
if req.Speed <= 0 {
req.Speed = 500 // 默认速度
}
// 根据类型启动动画
switch req.Type {
case "wave":
startWaveAnimation(req.Interface, req.Speed, req.HandType, req.HandId)
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: fmt.Sprintf("%s 波浪动画已启动", req.Interface),
Data: map[string]interface{}{"interface": req.Interface, "speed": req.Speed},
})
case "sway":
startSwayAnimation(req.Interface, req.Speed, req.HandType, req.HandId)
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: fmt.Sprintf("%s 横向摆动动画已启动", req.Interface),
Data: map[string]interface{}{"interface": req.Interface, "speed": req.Speed},
})
default:
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: "无效的动画类型",
})
}
})
// 获取传感器数据 API
api.GET("/sensors", func(c *gin.Context) {
// 从查询参数获取接口名称
ifName := c.Query("interface")
sensorMutex.RLock()
defer sensorMutex.RUnlock()
if ifName != "" {
// 验证接口
if !isValidInterface(ifName) {
c.JSON(http.StatusBadRequest, define.ApiResponse{
Status: "error",
Error: fmt.Sprintf("无效的接口 %s可用接口: %v", ifName, config.AvailableInterfaces),
})
return
}
// 请求特定接口的数据
if sensorData, ok := sensorDataMap[ifName]; ok {
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Data: sensorData,
})
} else {
c.JSON(http.StatusInternalServerError, define.ApiResponse{
Status: "error",
Error: "传感器数据不存在",
})
}
} else {
// 返回所有接口的数据
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Data: sensorDataMap,
})
}
})
// 系统状态 API - 更新包含手型配置
api.GET("/status", func(c *gin.Context) {
animationMutex.Lock()
animationStatus := make(map[string]bool)
for _, ifName := range config.AvailableInterfaces {
animationStatus[ifName] = animationActive[ifName]
}
animationMutex.Unlock()
// 检查 CAN 服务状态
canStatus := checkCanServiceStatus()
// 获取手型配置
handConfigMutex.RLock()
handConfigsData := make(map[string]interface{})
for ifName, handConfig := range handConfigs {
handConfigsData[ifName] = map[string]interface{}{
"handType": handConfig.HandType,
"handId": handConfig.HandId,
}
}
handConfigMutex.RUnlock()
interfaceStatuses := make(map[string]interface{})
for _, ifName := range config.AvailableInterfaces {
interfaceStatuses[ifName] = map[string]interface{}{
"active": canStatus[ifName],
"animationActive": animationStatus[ifName],
"handConfig": handConfigsData[ifName],
}
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Data: map[string]interface{}{
"interfaces": interfaceStatuses,
"uptime": time.Since(serverStartTime).String(),
"canServiceURL": config.CanServiceURL,
"defaultInterface": config.DefaultInterface,
"availableInterfaces": config.AvailableInterfaces,
"activeInterfaces": len(canStatus),
"handConfigs": handConfigsData,
},
})
})
// 获取可用接口列表 API - 修复数据格式
api.GET("/interfaces", func(c *gin.Context) {
// 确保返回前端期望的数据格式
responseData := map[string]interface{}{
"availableInterfaces": config.AvailableInterfaces,
"defaultInterface": config.DefaultInterface,
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Data: responseData,
})
})
// 获取手型配置 API - 新增
api.GET("/hand-configs", func(c *gin.Context) {
handConfigMutex.RLock()
defer handConfigMutex.RUnlock()
result := make(map[string]interface{})
for _, ifName := range config.AvailableInterfaces {
if handConfig, exists := handConfigs[ifName]; exists {
result[ifName] = map[string]interface{}{
"handType": handConfig.HandType,
"handId": handConfig.HandId,
}
} else {
// 返回默认配置
result[ifName] = map[string]interface{}{
"handType": "right",
"handId": HAND_TYPE_RIGHT,
}
}
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Data: result,
})
})
// 健康检查端点 - 新增,用于调试
api.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Message: "CAN Control Service is running",
Data: map[string]interface{}{
"timestamp": time.Now(),
"availableInterfaces": config.AvailableInterfaces,
"defaultInterface": config.DefaultInterface,
"serviceVersion": "1.0.0-hand-type-support",
},
})
})
}
}
func printUsage() {
fmt.Println("CAN Control Service with Hand Type Support")
fmt.Println("Usage:")
fmt.Println(" -can-url string CAN 服务的 URL (default: http://127.0.0.1:5260)")
fmt.Println(" -port string Web 服务的端口 (default: 9099)")
fmt.Println(" -interface string 默认 CAN 接口")
fmt.Println(" -can-interfaces string 支持的 CAN 接口列表,用逗号分隔")
fmt.Println("")
fmt.Println("Environment Variables:")
fmt.Println(" CAN_SERVICE_URL CAN 服务的 URL")
fmt.Println(" WEB_PORT Web 服务的端口")
fmt.Println(" DEFAULT_INTERFACE 默认 CAN 接口")
fmt.Println(" CAN_INTERFACES 支持的 CAN 接口列表,用逗号分隔")
fmt.Println("")
fmt.Println("New Features:")
fmt.Println(" - Support for left/right hand configuration")
fmt.Println(" - Dynamic CAN ID assignment based on hand type")
fmt.Println(" - Hand type API endpoints")
fmt.Println(" - Enhanced logging with hand type information")
fmt.Println("")
fmt.Println("Examples:")
fmt.Println(" ./control-service -can-interfaces can0,can1,vcan0")
fmt.Println(" ./control-service -interface can1 -can-interfaces can0,can1")
fmt.Println(" CAN_INTERFACES=can0,can1,vcan0 ./control-service")
fmt.Println(" CAN_SERVICE_URL=http://localhost:5260 ./control-service")
}
func main() {
// 检查是否请求帮助
if len(os.Args) > 1 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
printUsage()
return
}
// 解析配置
config = cli.ParseConfig()
// 验证配置
if len(config.AvailableInterfaces) == 0 {
log.Fatal("❌ 没有可用的 CAN 接口")
}
if config.DefaultInterface == "" {
log.Fatal("❌ 没有设置默认 CAN 接口")
}
// 记录启动时间
serverStartTime = time.Now()
log.Printf("🚀 启动 CAN 控制服务 (支持左右手配置)")
// 初始化服务
initService()
// 启动传感器数据模拟
readSensorData()
// 设置 Gin 模式
gin.SetMode(gin.ReleaseMode)
// 创建 Gin 引擎
r := gin.Default()
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"*"}, // 允许的域,*表示允许所有
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
// 设置 API 路由
setupRoutes(r)
// 启动服务器
log.Printf("🌐 CAN 控制服务运行在 http://localhost:%s", config.WebPort)
log.Printf("📡 连接到 CAN 服务: %s", config.CanServiceURL)
log.Printf("🎯 默认接口: %s", config.DefaultInterface)
log.Printf("🔌 可用接口: %v", config.AvailableInterfaces)
log.Printf("🤖 支持左右手动态配置")
if err := r.Run(":" + config.WebPort); err != nil {
log.Fatalf("❌ 服务启动失败: %v", err)
}
}