Compare commits

...

10 Commits

Author SHA1 Message Date
Su Yang
2f20c36139
Merge pull request #11 from eli-yip/refactor
Some checks failed
CodeQL / Analyze (go) (push) Has been cancelled
Release Dashboard Server / goreleaser (push) Has been cancelled
refactor: implement interfaces handler
2025-06-04 12:16:46 +08:00
Jason1
3b70112d71
Merge branch 'linker-bot:main' into refactor 2025-06-04 11:00:35 +08:00
Eli Yip
c3093eb57b
chore: remove legacy api backup 2025-06-04 09:52:11 +08:00
Eli Yip
200f2e1751
feat: implement legacy status api 2025-06-04 09:51:00 +08:00
Eli Yip
f8aa2138ab
refactor: keep legacy sensor data 2025-06-04 09:27:14 +08:00
Su Yang
7a1e6f8eb3
Merge pull request #10 from eli-yip/main
refactor: huge refactoring
2025-06-03 13:14:03 +08:00
Eli Yip
e1f60366c8
fix: execute command dead lock 2025-06-03 13:05:42 +08:00
Eli Yip
b0bfd95ed9
chore: remove target component method in Command interface 2025-06-03 12:07:04 +08:00
Eli Yip
ca2b1e8fd4
Revert "chore: remove old api"
This reverts commit 4dc43e208d1d754b02bba03c166a15d346fd3d4c.
2025-06-03 11:13:59 +08:00
Eli Yip
4dc43e208d
chore: remove old api 2025-06-03 10:26:52 +08:00
12 changed files with 148 additions and 930 deletions

View File

@ -7,7 +7,6 @@ import (
"hands/config"
"hands/define"
"hands/device"
"github.com/gin-gonic/gin"
)
@ -430,7 +429,6 @@ func (s *LegacyServer) handleAnimation(c *gin.Context) {
}
// handleSensors 获取传感器数据处理函数
// TODO: 现在的传感器数据都是模拟的,先不进行严格定义,等到有文档了之后修改设备层和这里
func (s *LegacyServer) handleSensors(c *gin.Context) {
// 从查询参数获取接口名称
ifName := c.Query("interface")
@ -455,32 +453,17 @@ func (s *LegacyServer) handleSensors(c *gin.Context) {
return
}
// 获取设备的传感器组件
sensorComponents := dev.GetComponents(device.SensorComponent)
// 构建传感器数据响应
sensorData := make(map[string]any)
for _, component := range sensorComponents {
sensorId := component.GetID()
data, err := dev.ReadSensorData(sensorId)
if err != nil {
// 如果读取失败,记录错误状态
sensorData[sensorId] = map[string]any{
"error": err.Error(),
"timestamp": time.Now(),
}
continue
}
sensorData[sensorId] = map[string]any{
"values": data.Values(),
"timestamp": data.Timestamp(),
}
sensorData, err := dev.ReadSensorData()
if err != nil {
c.JSON(http.StatusInternalServerError, define.ApiResponse{
Status: "error",
Error: "获取传感器数据失败:" + err.Error(),
})
}
c.JSON(http.StatusOK, define.ApiResponse{
Status: "success",
Data: sensorData,
Data: sensorData.Values(),
})
} else {
// 返回所有接口的传感器数据
@ -490,35 +473,17 @@ func (s *LegacyServer) handleSensors(c *gin.Context) {
// 获取对应的设备
dev, err := s.mapper.GetDeviceForInterface(ifName)
if err != nil {
allSensorData[ifName] = map[string]any{
"error": "设备不可用:" + err.Error(),
}
allSensorData[ifName] = map[string]any{"error": "设备不可用:" + err.Error()}
continue
}
// 获取设备的传感器组件
sensorComponents := dev.GetComponents(device.SensorComponent)
// 构建传感器数据响应
sensorData := make(map[string]any)
for _, component := range sensorComponents {
sensorId := component.GetID()
data, err := dev.ReadSensorData(sensorId)
if err != nil {
sensorData[sensorId] = map[string]any{
"error": err.Error(),
"timestamp": time.Now(),
}
continue
}
sensorData[sensorId] = map[string]any{
"values": data.Values(),
"timestamp": data.Timestamp(),
}
sensorData, err := dev.ReadSensorData()
if err != nil {
allSensorData[ifName] = map[string]any{"error": "设备不可用:" + err.Error()}
continue
}
allSensorData[ifName] = sensorData
allSensorData[ifName] = sensorData.Values()
}
c.JSON(http.StatusOK, define.ApiResponse{
@ -543,6 +508,13 @@ func (s *LegacyServer) handleStatus(c *gin.Context) {
// 检查 CAN 服务状态 - 通过尝试获取设备状态来判断
canStatus := make(map[string]bool)
for ifName, handConfig := range allHandConfigs {
handConfigsData[ifName] = map[string]any{
"handType": handConfig.HandType,
"handId": handConfig.HandId,
}
}
for _, ifName := range config.Config.AvailableInterfaces {
// 获取对应的设备
dev, err := s.mapper.GetDeviceForInterface(ifName)
@ -560,32 +532,11 @@ func (s *LegacyServer) handleStatus(c *gin.Context) {
animationStatus[ifName] = animEngine.IsRunning()
// 获取设备状态来判断 CAN 服务状态
status, err := dev.GetStatus()
rawCanStatus, err := dev.GetCanStatus()
if err != nil {
canStatus[ifName] = false
} else {
canStatus[ifName] = status.IsConnected && status.IsActive
}
// 获取手型配置
if handConfig, exists := allHandConfigs[ifName]; exists {
handConfigsData[ifName] = map[string]any{
"handType": handConfig.HandType,
"handId": handConfig.HandId,
}
} else {
// 从设备获取当前手型
handType := dev.GetHandType()
handTypeStr := "right"
handId := uint32(define.HAND_TYPE_RIGHT)
if handType == define.HAND_TYPE_LEFT {
handTypeStr = "left"
handId = uint32(define.HAND_TYPE_LEFT)
}
handConfigsData[ifName] = map[string]any{
"handType": handTypeStr,
"handId": handId,
}
canStatus[ifName] = rawCanStatus[ifName]
}
}

View File

@ -67,8 +67,7 @@ func (s *Server) SetupRoutes(r *gin.Engine) {
// 传感器数据路由
sensors := deviceRoutes.Group("/sensors")
{
sensors.GET("", s.handleGetSensors) // 获取所有传感器数据
sensors.GET("/:sensorId", s.handleGetSensorData) // 获取特定传感器数据
sensors.GET("", s.handleGetSensors) // 获取所有传感器数据
}
// 设备状态路由

View File

@ -3,7 +3,6 @@ package api
import (
"fmt"
"net/http"
"time"
"hands/device"
@ -24,103 +23,17 @@ func (s *Server) handleGetSensors(c *gin.Context) {
return
}
// 获取设备的传感器组件
sensorComponents := dev.GetComponents(device.SensorComponent)
sensors := make([]SensorDataResponse, 0, len(sensorComponents))
// 遍历所有传感器组件,读取数据
for _, component := range sensorComponents {
sensorId := component.GetID()
// 读取传感器数据
sensorData, err := dev.ReadSensorData(sensorId)
if err != nil {
// 如果读取失败,创建一个错误状态的传感器数据
sensors = append(sensors, SensorDataResponse{
SensorID: sensorId,
Timestamp: time.Now(),
Values: map[string]any{
"error": err.Error(),
"status": "error",
},
})
continue
}
// 转换为响应格式
sensorResponse := SensorDataResponse{
SensorID: sensorData.SensorID(),
Timestamp: sensorData.Timestamp(),
Values: sensorData.Values(),
}
sensors = append(sensors, sensorResponse)
}
response := SensorListResponse{
Sensors: sensors,
Total: len(sensors),
}
c.JSON(http.StatusOK, ApiResponse{
Status: "success",
Data: response,
})
}
// handleGetSensorData 获取特定传感器数据
func (s *Server) handleGetSensorData(c *gin.Context) {
deviceId := c.Param("deviceId")
sensorId := c.Param("sensorId")
// 获取设备
dev, err := s.deviceManager.GetDevice(deviceId)
if err != nil {
c.JSON(http.StatusNotFound, ApiResponse{
Status: "error",
Error: fmt.Sprintf("设备 %s 不存在", deviceId),
})
return
}
// 验证传感器是否存在
sensorComponents := dev.GetComponents(device.SensorComponent)
sensorExists := false
for _, component := range sensorComponents {
if component.GetID() == sensorId {
sensorExists = true
break
}
}
if !sensorExists {
c.JSON(http.StatusNotFound, ApiResponse{
Status: "error",
Error: fmt.Sprintf("设备 %s 上不存在传感器 %s", deviceId, sensorId),
})
return
}
// 读取传感器数据
sensorData, err := dev.ReadSensorData(sensorId)
sensorData, err := dev.ReadSensorData()
if err != nil {
c.JSON(http.StatusInternalServerError, ApiResponse{
Status: "error",
Error: fmt.Sprintf("读取传感器 %s 数据失败:%v", sensorId, err),
Error: fmt.Sprintf("读取传感器数据失败:%v", err),
})
return
}
// 转换为响应格式
response := SensorDataResponse{
SensorID: sensorData.SensorID(),
Timestamp: sensorData.Timestamp(),
Values: sensorData.Values(),
}
c.JSON(http.StatusOK, ApiResponse{
Status: "success",
Data: response,
Data: sensorData.Values(),
})
}

View File

@ -5,6 +5,8 @@ import (
"context"
"encoding/json"
"fmt"
"hands/config"
"hands/define"
"io"
"net/http"
"time"
@ -23,9 +25,6 @@ type Communicator interface {
// SendMessage 将 RawMessage 通过 HTTP POST 请求发送到 can-bridge 服务
SendMessage(ctx context.Context, msg RawMessage) error
// GetInterfaceStatus 获取指定 CAN 接口的状态
GetInterfaceStatus(ifName string) (isActive bool, err error)
// GetAllInterfaceStatuses 获取所有已知 CAN 接口的状态
GetAllInterfaceStatuses() (statuses map[string]bool, err error)
@ -45,9 +44,7 @@ type CanBridgeClient struct {
func NewCanBridgeClient(serviceURL string) Communicator {
return &CanBridgeClient{
serviceURL: serviceURL,
client: &http.Client{
Timeout: 5 * time.Second,
},
client: &http.Client{Timeout: 5 * time.Second},
}
}
@ -60,7 +57,7 @@ func (c *CanBridgeClient) SendMessage(ctx context.Context, msg RawMessage) error
url := fmt.Sprintf("%s/api/can", c.serviceURL)
// 创建带有 context 的请求
req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData))
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonData))
if err != nil {
return fmt.Errorf("创建 HTTP 请求失败:%w", err)
}
@ -80,47 +77,49 @@ func (c *CanBridgeClient) SendMessage(ctx context.Context, msg RawMessage) error
return nil
}
func (c *CanBridgeClient) GetInterfaceStatus(ifName string) (bool, error) {
url := fmt.Sprintf("%s/api/status/%s", c.serviceURL, ifName)
resp, err := c.client.Get(url)
if err != nil {
return false, fmt.Errorf("获取接口状态失败:%w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return false, fmt.Errorf("can-bridge 服务返回错误:%d", resp.StatusCode)
}
var status struct {
Active bool `json:"active"`
}
if err := json.NewDecoder(resp.Body).Decode(&status); err != nil {
return false, fmt.Errorf("解析状态响应失败:%w", err)
}
return status.Active, nil
}
func (c *CanBridgeClient) GetAllInterfaceStatuses() (map[string]bool, error) {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
url := fmt.Sprintf("%s/api/status", c.serviceURL)
resp, err := c.client.Get(url)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("获取所有接口状态失败:%w", err)
}
resp, err := c.client.Do(req)
if err != nil {
return nil, fmt.Errorf("发送 HTTP 请求失败:%w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("can-bridge 服务返回错误:%d", resp.StatusCode)
}
var statuses map[string]bool
if err := json.NewDecoder(resp.Body).Decode(&statuses); err != nil {
var statusResp define.ApiResponse
if err := json.NewDecoder(resp.Body).Decode(&statusResp); err != nil {
return nil, fmt.Errorf("解析状态响应失败:%w", err)
}
return statuses, nil
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, nil
}
func (c *CanBridgeClient) SetServiceURL(url string) { c.serviceURL = url }

View File

@ -1,78 +0,0 @@
package component
import (
"fmt"
"hands/device"
"math/rand/v2"
"time"
)
// PressureSensor 压力传感器实现
type PressureSensor struct {
id string
config map[string]any
isActive bool
samplingRate int
lastReading time.Time
}
func NewPressureSensor(id string, config map[string]any) *PressureSensor {
return &PressureSensor{
id: id,
config: config,
isActive: true,
samplingRate: 100,
lastReading: time.Now(),
}
}
func (p *PressureSensor) GetID() string {
return p.id
}
func (p *PressureSensor) GetType() device.ComponentType {
return device.SensorComponent
}
func (p *PressureSensor) GetConfiguration() map[string]any {
return p.config
}
func (p *PressureSensor) IsActive() bool {
return p.isActive
}
func (p *PressureSensor) ReadData() (device.SensorData, error) {
if !p.isActive {
return nil, fmt.Errorf("传感器 %s 未激活", p.id)
}
// 模拟压力数据读取
// 在实际实现中,这里应该从 can-bridge 或其他数据源读取真实数据
pressure := rand.Float64() * 100 // 0-100 的随机压力值
values := map[string]any{
"pressure": pressure,
"unit": "kPa",
"location": p.config["location"],
}
p.lastReading = time.Now()
return NewSensorData(p.id, values), nil
}
func (p *PressureSensor) GetDataType() string {
return "pressure"
}
func (p *PressureSensor) GetSamplingRate() int {
return p.samplingRate
}
func (p *PressureSensor) SetSamplingRate(rate int) error {
if rate <= 0 || rate > 1000 {
return fmt.Errorf("采样率必须在 1-1000Hz 之间")
}
p.samplingRate = rate
return nil
}

View File

@ -2,41 +2,69 @@ package component
import (
"hands/device"
"math/rand/v2"
"time"
)
// Sensor 传感器组件接口
type Sensor interface {
device.Component
// device.Component
ReadData() (device.SensorData, error)
GetDataType() string
GetSamplingRate() int
SetSamplingRate(rate int) error
// GetDataType() string
// GetSamplingRate() int
// SetSamplingRate(rate int) error
MockData()
}
// SensorDataImpl 传感器数据的具体实现
type SensorDataImpl struct {
timestamp time.Time
values map[string]any
sensorID string
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"`
}
func NewSensorData(sensorID string, values map[string]any) *SensorDataImpl {
func NewSensorData(ifName string) *SensorDataImpl {
return &SensorDataImpl{
timestamp: time.Now(),
values: values,
sensorID: sensorID,
Interface: ifName,
Thumb: 0,
Index: 0,
Middle: 0,
Ring: 0,
Pinky: 0,
PalmPosition: []byte{128, 128, 128, 128},
LastUpdate: time.Now(),
}
}
func (s *SensorDataImpl) Timestamp() time.Time {
return s.timestamp
func (s *SensorDataImpl) MockData() {
go func() {
for {
s.Thumb = rand.IntN(101)
s.Index = rand.IntN(101)
s.Middle = rand.IntN(101)
s.Ring = rand.IntN(101)
s.Pinky = rand.IntN(101)
s.LastUpdate = time.Now()
time.Sleep(500 * time.Millisecond)
}
}()
}
func (s *SensorDataImpl) Values() map[string]any {
return s.values
return map[string]any{
"thumb": s.Thumb,
"index": s.Index,
"middle": s.Middle,
"ring": s.Ring,
"pinky": s.Pinky,
"palmPosition": s.PalmPosition,
"lastUpdate": s.LastUpdate,
}
}
func (s *SensorDataImpl) SensorID() string {
return s.sensorID
}
func (s *SensorDataImpl) ReadData() (device.SensorData, error) { return s, nil }

View File

@ -1,80 +1,40 @@
package device
// FingerPoseCommand 手指姿态指令
type FingerPoseCommand struct {
fingerID string
poseData []byte
targetComp string
type FingerPoseCommand struct{ poseData []byte }
func NewFingerPoseCommand(poseData []byte) *FingerPoseCommand {
return &FingerPoseCommand{poseData: poseData}
}
func NewFingerPoseCommand(fingerID string, poseData []byte) *FingerPoseCommand {
return &FingerPoseCommand{
fingerID: fingerID,
poseData: poseData,
targetComp: "finger_" + fingerID,
}
}
func (c *FingerPoseCommand) Type() string { return "SetFingerPose" }
func (c *FingerPoseCommand) Type() string {
return "SetFingerPose"
}
func (c *FingerPoseCommand) Payload() []byte {
return c.poseData
}
func (c *FingerPoseCommand) TargetComponent() string {
return c.targetComp
}
func (c *FingerPoseCommand) Payload() []byte { return c.poseData }
// PalmPoseCommand 手掌姿态指令
type PalmPoseCommand struct {
poseData []byte
targetComp string
}
type PalmPoseCommand struct{ poseData []byte }
func NewPalmPoseCommand(poseData []byte) *PalmPoseCommand {
return &PalmPoseCommand{
poseData: poseData,
targetComp: "palm",
}
return &PalmPoseCommand{poseData: poseData}
}
func (c *PalmPoseCommand) Type() string {
return "SetPalmPose"
}
func (c *PalmPoseCommand) Type() string { return "SetPalmPose" }
func (c *PalmPoseCommand) Payload() []byte {
return c.poseData
}
func (c *PalmPoseCommand) TargetComponent() string {
return c.targetComp
}
func (c *PalmPoseCommand) Payload() []byte { return c.poseData }
// GenericCommand 通用指令
type GenericCommand struct {
cmdType string
payload []byte
targetComp string
cmdType string
payload []byte
}
func NewGenericCommand(cmdType string, payload []byte, targetComp string) *GenericCommand {
return &GenericCommand{
cmdType: cmdType,
payload: payload,
targetComp: targetComp,
cmdType: cmdType,
payload: payload,
}
}
func (c *GenericCommand) Type() string {
return c.cmdType
}
func (c *GenericCommand) Type() string { return c.cmdType }
func (c *GenericCommand) Payload() []byte {
return c.payload
}
func (c *GenericCommand) TargetComponent() string {
return c.targetComp
}
func (c *GenericCommand) Payload() []byte { return c.payload }

View File

@ -12,8 +12,9 @@ type Device interface {
GetHandType() define.HandType // 获取设备手型
SetHandType(handType define.HandType) error // 设置设备手型
ExecuteCommand(cmd Command) error // 执行一个通用指令
ReadSensorData(sensorID string) (SensorData, error) // 读取特定传感器数据
ReadSensorData() (SensorData, error) // 读取特定传感器数据
GetComponents(componentType ComponentType) []Component // 获取指定类型的组件
GetCanStatus() (map[string]bool, error)
GetStatus() (DeviceStatus, error) // 获取设备状态
Connect() error // 连接设备
Disconnect() error // 断开设备连接
@ -31,16 +32,15 @@ type Device interface {
// Command 代表一个发送给设备的指令
type Command interface {
Type() string // 指令类型,例如 "SetFingerPose", "SetPalmAngle"
Payload() []byte // 指令的实际数据
TargetComponent() string // 目标组件 ID
Type() string // 指令类型,例如 "SetFingerPose", "SetPalmAngle"
Payload() []byte // 指令的实际数据
}
// SensorData 代表从传感器读取的数据
type SensorData interface {
Timestamp() time.Time
// Timestamp() time.Time
Values() map[string]any // 例如 {"pressure": 100, "angle": 30.5}
SensorID() string
// SensorID() string
}
// ComponentType 定义组件类型
@ -54,10 +54,10 @@ const (
// Component 代表设备的一个可插拔组件
type Component interface {
GetID() string
GetType() ComponentType
GetConfiguration() map[string]interface{} // 组件的特定配置
IsActive() bool
// GetID() string
// GetType() ComponentType
// GetConfiguration() map[string]interface{} // 组件的特定配置
// IsActive() bool
}
// DeviceStatus 代表设备状态

View File

@ -142,7 +142,7 @@ func (h *L10Hand) SetFingerPose(pose []byte) error {
}
// 创建指令
cmd := device.NewFingerPoseCommand("all", perturbedPose)
cmd := device.NewFingerPoseCommand(perturbedPose)
// 执行指令
err := h.ExecuteCommand(cmd)
@ -197,11 +197,9 @@ func (h *L10Hand) ResetPose() error {
return nil
}
// commandToRawMessage 将通用指令转换为 L10 特定的 CAN 消息
func (h *L10Hand) commandToRawMessage(cmd device.Command) (communication.RawMessage, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
// commandToRawMessageUnsafe 将通用指令转换为 L10 特定的 CAN 消息(不加锁版本)
// 注意:此方法不是线程安全的,只应在已获取适当锁的情况下调用
func (h *L10Hand) commandToRawMessageUnsafe(cmd device.Command) (communication.RawMessage, error) {
var data []byte
canID := uint32(h.handType)
@ -238,8 +236,8 @@ func (h *L10Hand) ExecuteCommand(cmd device.Command) error {
return fmt.Errorf("设备 %s 未连接或未激活", h.id)
}
// 转换指令为 CAN 消息
rawMsg, err := h.commandToRawMessage(cmd)
// 转换指令为 CAN 消息(使用不加锁版本,因为已经在写锁保护下)
rawMsg, err := h.commandToRawMessageUnsafe(cmd)
if err != nil {
h.status.ErrorCount++
h.status.LastError = err.Error()
@ -259,22 +257,15 @@ func (h *L10Hand) ExecuteCommand(cmd device.Command) error {
}
h.status.LastUpdate = time.Now()
// 成功的日志记录移到 SetFingerPose 和 SetPalmPose 中,因为那里有更详细的信息
return nil
}
// --- 其他 L10Hand 方法 (initializeComponents, GetID, GetModel, ReadSensorData, etc.) 保持不变 ---
// --- 确保它们存在且与您上传的版本一致 ---
func (h *L10Hand) initializeComponents(_ map[string]any) error {
// 初始化传感器组件
sensors := []device.Component{
component.NewPressureSensor("pressure_thumb", map[string]any{"location": "thumb"}),
component.NewPressureSensor("pressure_index", map[string]any{"location": "index"}),
component.NewPressureSensor("pressure_middle", map[string]any{"location": "middle"}),
component.NewPressureSensor("pressure_ring", map[string]any{"location": "ring"}),
component.NewPressureSensor("pressure_pinky", map[string]any{"location": "pinky"}),
}
defaultSensor := component.NewSensorData(h.canInterface)
defaultSensor.MockData()
sensors := []device.Component{defaultSensor}
h.components[device.SensorComponent] = sensors
return nil
}
@ -287,19 +278,17 @@ func (h *L10Hand) GetModel() string {
return h.model
}
func (h *L10Hand) ReadSensorData(sensorID string) (device.SensorData, error) {
func (h *L10Hand) ReadSensorData() (device.SensorData, error) {
h.mutex.RLock()
defer h.mutex.RUnlock()
sensors := h.components[device.SensorComponent]
for _, comp := range sensors {
if comp.GetID() == sensorID {
if sensor, ok := comp.(component.Sensor); ok {
return sensor.ReadData()
}
if sensor, ok := comp.(component.Sensor); ok {
return sensor.ReadData()
}
}
return nil, fmt.Errorf("传感器 %s 不存在", sensorID)
return nil, fmt.Errorf("传感器不存在")
}
func (h *L10Hand) GetComponents(componentType device.ComponentType) []device.Component {
@ -383,3 +372,7 @@ func (h *L10Hand) GetPresetDescription(presetName string) string {
func (h *L10Hand) GetPresetDetails(presetName string) (device.PresetPose, bool) {
return h.presetManager.GetPreset(presetName)
}
func (h *L10Hand) GetCanStatus() (map[string]bool, error) {
return h.communicator.GetAllInterfaceStatuses()
}

View File

@ -1,468 +0,0 @@
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",
},
})
}

View File

@ -1,29 +0,0 @@
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"`
}

View File

@ -1,50 +0,0 @@
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)
}
}