From e9a878895a7c42daa9a69b7146d9215653d41a7b Mon Sep 17 00:00:00 2001 From: Eli Yip Date: Tue, 3 Jun 2025 09:07:20 +0800 Subject: [PATCH] chore: add back legacy api --- legacy-api/handler.go | 468 ++++++++++++++++++++++++++++++++++++++++++ legacy-api/models.go | 29 +++ legacy-api/router.go | 50 +++++ 3 files changed, 547 insertions(+) create mode 100644 legacy-api/handler.go create mode 100644 legacy-api/models.go create mode 100644 legacy-api/router.go diff --git a/legacy-api/handler.go b/legacy-api/handler.go new file mode 100644 index 0000000..b3a98d1 --- /dev/null +++ b/legacy-api/handler.go @@ -0,0 +1,468 @@ +package api + +import ( + "fmt" + "hands/config" + "hands/define" + "hands/hands" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +// 手型设置处理函数 +func HandleHandType(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 !config.IsValidInterface(req.Interface) { + c.JSON(http.StatusBadRequest, define.ApiResponse{ + Status: "error", + Error: fmt.Sprintf("无效的接口 %s,可用接口: %v", req.Interface, config.Config.AvailableInterfaces), + }) + return + } + + // 验证手型 ID + if req.HandType == "left" && req.HandId != uint32(define.HAND_TYPE_LEFT) { + req.HandId = uint32(define.HAND_TYPE_LEFT) + } else if req.HandType == "right" && req.HandId != uint32(define.HAND_TYPE_RIGHT) { + req.HandId = uint32(define.HAND_TYPE_RIGHT) + } + + // 设置手型配置 + hands.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]any{ + "interface": req.Interface, + "handType": req.HandType, + "handId": req.HandId, + }, + }) +} + +// 手指姿态处理函数 +func HandleFingers(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.Config.DefaultInterface + } + + // 验证接口 + if !config.IsValidInterface(req.Interface) { + c.JSON(http.StatusBadRequest, define.ApiResponse{ + Status: "error", + Error: fmt.Sprintf("无效的接口 %s,可用接口: %v", req.Interface, config.Config.AvailableInterfaces), + }) + return + } + + hands.StopAllAnimations(req.Interface) + + if err := hands.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]any{"interface": req.Interface, "pose": req.Pose}, + }) +} + +// 掌部姿态处理函数 +func HandlePalm(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.Config.DefaultInterface + } + + // 验证接口 + if !config.IsValidInterface(req.Interface) { + c.JSON(http.StatusBadRequest, define.ApiResponse{ + Status: "error", + Error: fmt.Sprintf("无效的接口 %s,可用接口: %v", req.Interface, config.Config.AvailableInterfaces), + }) + return + } + + hands.StopAllAnimations(req.Interface) + + if err := hands.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]any{"interface": req.Interface, "pose": req.Pose}, + }) +} + +// 预设姿势处理函数 +func HandlePreset(c *gin.Context) { + pose := c.Param("pose") + + // 从查询参数获取接口名称和手型 + ifName := c.Query("interface") + handType := c.Query("handType") + + if ifName == "" { + ifName = config.Config.DefaultInterface + } + + // 验证接口 + if !config.IsValidInterface(ifName) { + c.JSON(http.StatusBadRequest, define.ApiResponse{ + Status: "error", + Error: fmt.Sprintf("无效的接口 %s,可用接口: %v", ifName, config.Config.AvailableInterfaces), + }) + return + } + + hands.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 = hands.ParseHandType(handType, 0, ifName) + } + + if err := hands.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]any{"interface": ifName, "pose": fingerPose}, + }) +} + +// 动画控制处理函数 +func HandleAnimation(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.Config.DefaultInterface + } + + // 验证接口 + if !config.IsValidInterface(req.Interface) { + c.JSON(http.StatusBadRequest, define.ApiResponse{ + Status: "error", + Error: fmt.Sprintf("无效的接口 %s,可用接口: %v", req.Interface, config.Config.AvailableInterfaces), + }) + return + } + + // 停止当前动画 + hands.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": + hands.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]any{"interface": req.Interface, "speed": req.Speed}, + }) + case "sway": + hands.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]any{"interface": req.Interface, "speed": req.Speed}, + }) + default: + c.JSON(http.StatusBadRequest, define.ApiResponse{ + Status: "error", + Error: "无效的动画类型", + }) + } +} + +// 获取传感器数据处理函数 +func HandleSensors(c *gin.Context) { + // 从查询参数获取接口名称 + ifName := c.Query("interface") + + hands.SensorMutex.RLock() + defer hands.SensorMutex.RUnlock() + + if ifName != "" { + // 验证接口 + if !config.IsValidInterface(ifName) { + c.JSON(http.StatusBadRequest, define.ApiResponse{ + Status: "error", + Error: fmt.Sprintf("无效的接口 %s,可用接口: %v", ifName, config.Config.AvailableInterfaces), + }) + return + } + + // 请求特定接口的数据 + if sensorData, ok := hands.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: hands.SensorDataMap, + }) + } +} + +// 系统状态处理函数 +func HandleStatus(c *gin.Context) { + hands.AnimationMutex.Lock() + animationStatus := make(map[string]bool) + for _, ifName := range config.Config.AvailableInterfaces { + animationStatus[ifName] = hands.AnimationActive[ifName] + } + hands.AnimationMutex.Unlock() + + // 检查 CAN 服务状态 + canStatus := hands.CheckCanServiceStatus() + + // 获取手型配置 + hands.HandConfigMutex.RLock() + handConfigsData := make(map[string]any) + for ifName, handConfig := range hands.HandConfigs { + handConfigsData[ifName] = map[string]any{ + "handType": handConfig.HandType, + "handId": handConfig.HandId, + } + } + hands.HandConfigMutex.RUnlock() + + interfaceStatuses := make(map[string]any) + for _, ifName := range config.Config.AvailableInterfaces { + interfaceStatuses[ifName] = map[string]any{ + "active": canStatus[ifName], + "animationActive": animationStatus[ifName], + "handConfig": handConfigsData[ifName], + } + } + + c.JSON(http.StatusOK, define.ApiResponse{ + Status: "success", + Data: map[string]any{ + "interfaces": interfaceStatuses, + "uptime": time.Since(ServerStartTime).String(), + "canServiceURL": config.Config.CanServiceURL, + "defaultInterface": config.Config.DefaultInterface, + "availableInterfaces": config.Config.AvailableInterfaces, + "activeInterfaces": len(canStatus), + "handConfigs": handConfigsData, + }, + }) +} + +// 获取可用接口列表处理函数 +func HandleInterfaces(c *gin.Context) { + responseData := map[string]any{ + "availableInterfaces": config.Config.AvailableInterfaces, + "defaultInterface": config.Config.DefaultInterface, + } + + c.JSON(http.StatusOK, define.ApiResponse{ + Status: "success", + Data: responseData, + }) +} + +// 获取手型配置处理函数 +func HandleHandConfigs(c *gin.Context) { + hands.HandConfigMutex.RLock() + defer hands.HandConfigMutex.RUnlock() + + result := make(map[string]any) + for _, ifName := range config.Config.AvailableInterfaces { + if handConfig, exists := hands.HandConfigs[ifName]; exists { + result[ifName] = map[string]any{ + "handType": handConfig.HandType, + "handId": handConfig.HandId, + } + } else { + // 返回默认配置 + result[ifName] = map[string]any{ + "handType": "right", + "handId": define.HAND_TYPE_RIGHT, + } + } + } + + c.JSON(http.StatusOK, define.ApiResponse{ + Status: "success", + Data: result, + }) +} + +// 健康检查处理函数 +func HandleHealth(c *gin.Context) { + c.JSON(http.StatusOK, define.ApiResponse{ + Status: "success", + Message: "CAN Control Service is running", + Data: map[string]any{ + "timestamp": time.Now(), + "availableInterfaces": config.Config.AvailableInterfaces, + "defaultInterface": config.Config.DefaultInterface, + "serviceVersion": "1.0.0-hand-type-support", + }, + }) +} diff --git a/legacy-api/models.go b/legacy-api/models.go new file mode 100644 index 0000000..9f09550 --- /dev/null +++ b/legacy-api/models.go @@ -0,0 +1,29 @@ +package 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"` +} diff --git a/legacy-api/router.go b/legacy-api/router.go new file mode 100644 index 0000000..b7987c2 --- /dev/null +++ b/legacy-api/router.go @@ -0,0 +1,50 @@ +package api + +import ( + "time" + + "github.com/gin-gonic/gin" +) + +// 全局变量 +var ( + ServerStartTime time.Time +) + +func SetupRoutes(r *gin.Engine) { + r.StaticFile("/", "./static/index.html") + r.Static("/static", "./static") + + api := r.Group("/api") + { + // 手型设置 API + api.POST("/hand-type", HandleHandType) + + // 手指姿态 API + api.POST("/fingers", HandleFingers) + + // 掌部姿态 API + api.POST("/palm", HandlePalm) + + // 预设姿势 API + api.POST("/preset/:pose", HandlePreset) + + // 动画控制 API + api.POST("/animation", HandleAnimation) + + // 获取传感器数据 API + api.GET("/sensors", HandleSensors) + + // 系统状态 API + api.GET("/status", HandleStatus) + + // 获取可用接口列表 API + api.GET("/interfaces", HandleInterfaces) + + // 获取手型配置 API + api.GET("/hand-configs", HandleHandConfigs) + + // 健康检查端点 + api.GET("/health", HandleHealth) + } +}