diff --git a/pkg/communication/communicator.go b/pkg/communication/communicator.go deleted file mode 100644 index 41d2323..0000000 --- a/pkg/communication/communicator.go +++ /dev/null @@ -1,122 +0,0 @@ -package communication - -import ( - "bytes" - "encoding/json" - "fmt" - "io" - "net/http" - "time" -) - -// TODO: ID 的作用是什么 -// RawMessage 代表发送给 can-bridge 服务或从其接收的原始消息结构 -type RawMessage struct { - Interface string `json:"interface"` // 目标 CAN 接口名,例如 "can0", "vcan1" - ID uint32 `json:"id"` // CAN 帧的 ID - Data []byte `json:"data"` // CAN 帧的数据负载 -} - -// Communicator 定义了与 can-bridge Web 服务进行通信的接口 -type Communicator interface { - // SendMessage 将 RawMessage 通过 HTTP POST 请求发送到 can-bridge 服务 - SendMessage(msg RawMessage) error - - // GetInterfaceStatus 获取指定 CAN 接口的状态 - GetInterfaceStatus(ifName string) (isActive bool, err error) - - // GetAllInterfaceStatuses 获取所有已知 CAN 接口的状态 - GetAllInterfaceStatuses() (statuses map[string]bool, err error) - - // SetServiceURL 设置 can-bridge 服务的 URL - SetServiceURL(url string) - - // IsConnected 检查与 can-bridge 服务的连接状态 - IsConnected() bool -} - -// CanBridgeClient 实现与 can-bridge 服务的 HTTP 通信 -type CanBridgeClient struct { - serviceURL string - client *http.Client -} - -func NewCanBridgeClient(serviceURL string) Communicator { - return &CanBridgeClient{ - serviceURL: serviceURL, - client: &http.Client{ - Timeout: 5 * time.Second, - }, - } -} - -func (c *CanBridgeClient) SendMessage(msg RawMessage) error { - jsonData, err := json.Marshal(msg) - if err != nil { - return fmt.Errorf("序列化消息失败:%w", err) - } - - url := fmt.Sprintf("%s/api/can", c.serviceURL) - resp, err := c.client.Post(url, "application/json", bytes.NewBuffer(jsonData)) - if err != nil { - return fmt.Errorf("发送 HTTP 请求失败:%w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("can-bridge服务返回错误: %d, %s", resp.StatusCode, string(body)) - } - - 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) { - url := fmt.Sprintf("%s/api/status", c.serviceURL) - resp, err := c.client.Get(url) - if err != nil { - return nil, fmt.Errorf("获取所有接口状态失败:%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 { - return nil, fmt.Errorf("解析状态响应失败:%w", err) - } - - return statuses, nil -} - -func (c *CanBridgeClient) SetServiceURL(url string) { c.serviceURL = url } - -func (c *CanBridgeClient) IsConnected() bool { - _, err := c.GetAllInterfaceStatuses() - return err == nil -} diff --git a/pkg/component/pressure_sensor.go b/pkg/component/pressure_sensor.go deleted file mode 100644 index 1c1d454..0000000 --- a/pkg/component/pressure_sensor.go +++ /dev/null @@ -1,78 +0,0 @@ -package component - -import ( - "fmt" - "hands/pkg/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 -} diff --git a/pkg/component/sensor.go b/pkg/component/sensor.go deleted file mode 100644 index c684e19..0000000 --- a/pkg/component/sensor.go +++ /dev/null @@ -1,42 +0,0 @@ -package component - -import ( - "hands/pkg/device" - "time" -) - -// Sensor 传感器组件接口 -type Sensor interface { - device.Component - ReadData() (device.SensorData, error) - GetDataType() string - GetSamplingRate() int - SetSamplingRate(rate int) error -} - -// SensorDataImpl 传感器数据的具体实现 -type SensorDataImpl struct { - timestamp time.Time - values map[string]any - sensorID string -} - -func NewSensorData(sensorID string, values map[string]any) *SensorDataImpl { - return &SensorDataImpl{ - timestamp: time.Now(), - values: values, - sensorID: sensorID, - } -} - -func (s *SensorDataImpl) Timestamp() time.Time { - return s.timestamp -} - -func (s *SensorDataImpl) Values() map[string]any { - return s.values -} - -func (s *SensorDataImpl) SensorID() string { - return s.sensorID -} diff --git a/pkg/device/animation.go b/pkg/device/animation.go deleted file mode 100644 index 1cf1718..0000000 --- a/pkg/device/animation.go +++ /dev/null @@ -1,12 +0,0 @@ -package device - -// Animation 定义了一个动画序列的行为 -type Animation interface { - // Run 执行动画的一个周期或直到被停止 - // executor: 用于执行姿态指令 - // stop: 接收停止信号的通道 - // speedMs: 动画执行的速度(毫秒) - Run(executor PoseExecutor, stop <-chan struct{}, speedMs int) error - // Name 返回动画的名称 - Name() string -} diff --git a/pkg/device/commands.go b/pkg/device/commands.go deleted file mode 100644 index fb14a71..0000000 --- a/pkg/device/commands.go +++ /dev/null @@ -1,80 +0,0 @@ -package device - -// FingerPoseCommand 手指姿态指令 -type FingerPoseCommand struct { - fingerID string - poseData []byte - targetComp string -} - -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) Payload() []byte { - return c.poseData -} - -func (c *FingerPoseCommand) TargetComponent() string { - return c.targetComp -} - -// PalmPoseCommand 手掌姿态指令 -type PalmPoseCommand struct { - poseData []byte - targetComp string -} - -func NewPalmPoseCommand(poseData []byte) *PalmPoseCommand { - return &PalmPoseCommand{ - poseData: poseData, - targetComp: "palm", - } -} - -func (c *PalmPoseCommand) Type() string { - return "SetPalmPose" -} - -func (c *PalmPoseCommand) Payload() []byte { - return c.poseData -} - -func (c *PalmPoseCommand) TargetComponent() string { - return c.targetComp -} - -// GenericCommand 通用指令 -type GenericCommand struct { - cmdType string - payload []byte - targetComp string -} - -func NewGenericCommand(cmdType string, payload []byte, targetComp string) *GenericCommand { - return &GenericCommand{ - cmdType: cmdType, - payload: payload, - targetComp: targetComp, - } -} - -func (c *GenericCommand) Type() string { - return c.cmdType -} - -func (c *GenericCommand) Payload() []byte { - return c.payload -} - -func (c *GenericCommand) TargetComponent() string { - return c.targetComp -} diff --git a/pkg/device/device.go b/pkg/device/device.go deleted file mode 100644 index fe27b56..0000000 --- a/pkg/device/device.go +++ /dev/null @@ -1,64 +0,0 @@ -package device - -import ( - "hands/define" - "time" -) - -// Device 代表一个可控制的设备单元 -type Device interface { - GetID() string // 获取设备唯一标识 - GetModel() string // 获取设备型号 (例如 "L10", "L20") - GetHandType() define.HandType // 获取设备手型 - SetHandType(handType define.HandType) error // 设置设备手型 - ExecuteCommand(cmd Command) error // 执行一个通用指令 - ReadSensorData(sensorID string) (SensorData, error) // 读取特定传感器数据 - GetComponents(componentType ComponentType) []Component // 获取指定类型的组件 - GetStatus() (DeviceStatus, error) // 获取设备状态 - Connect() error // 连接设备 - Disconnect() error // 断开设备连接 - - // --- 新增 --- - PoseExecutor // 嵌入 PoseExecutor 接口,Device 需实现它 - GetAnimationEngine() *AnimationEngine // 获取设备的动画引擎 -} - -// Command 代表一个发送给设备的指令 -type Command interface { - Type() string // 指令类型,例如 "SetFingerPose", "SetPalmAngle" - Payload() []byte // 指令的实际数据 - TargetComponent() string // 目标组件 ID -} - -// SensorData 代表从传感器读取的数据 -type SensorData interface { - Timestamp() time.Time - Values() map[string]any // 例如 {"pressure": 100, "angle": 30.5} - SensorID() string -} - -// ComponentType 定义组件类型 -type ComponentType string - -const ( - SensorComponent ComponentType = "sensor" - SkinComponent ComponentType = "skin" - ActuatorComponent ComponentType = "actuator" -) - -// Component 代表设备的一个可插拔组件 -type Component interface { - GetID() string - GetType() ComponentType - GetConfiguration() map[string]interface{} // 组件的特定配置 - IsActive() bool -} - -// DeviceStatus 代表设备状态 -type DeviceStatus struct { - IsConnected bool - IsActive bool - LastUpdate time.Time - ErrorCount int - LastError string -} diff --git a/pkg/device/engine.go b/pkg/device/engine.go deleted file mode 100644 index e30d568..0000000 --- a/pkg/device/engine.go +++ /dev/null @@ -1,191 +0,0 @@ -package device - -import ( - "fmt" - "log" - "sync" -) - -// defaultAnimationSpeedMs 定义默认动画速度(毫秒) -const defaultAnimationSpeedMs = 500 - -// AnimationEngine 管理和执行动画 -type AnimationEngine struct { - executor PoseExecutor // 关联的姿态执行器 - animations map[string]Animation // 注册的动画 - stopChan chan struct{} // 当前动画的停止通道 - current string // 当前运行的动画名称 - isRunning bool // 是否有动画在运行 - engineMutex sync.Mutex // 保护引擎状态 (isRunning, current, stopChan) - registerMutex sync.RWMutex // 保护动画注册表 (animations) -} - -// NewAnimationEngine 创建一个新的动画引擎 -func NewAnimationEngine(executor PoseExecutor) *AnimationEngine { - return &AnimationEngine{ - executor: executor, - animations: make(map[string]Animation), - } -} - -// Register 注册一个动画 -func (e *AnimationEngine) Register(anim Animation) { - e.registerMutex.Lock() - defer e.registerMutex.Unlock() - - if anim == nil { - log.Printf("⚠️ 尝试注册一个空动画") - return - } - - name := anim.Name() - if _, exists := e.animations[name]; exists { - log.Printf("⚠️ 动画 %s 已注册,将被覆盖", name) - } - e.animations[name] = anim - log.Printf("✅ 动画 %s 已注册", name) -} - -// getAnimation 安全地获取一个已注册的动画 -func (e *AnimationEngine) getAnimation(name string) (Animation, bool) { - e.registerMutex.RLock() - defer e.registerMutex.RUnlock() - anim, exists := e.animations[name] - return anim, exists -} - -// getDeviceName 尝试获取设备 ID 用于日志记录 -func (e *AnimationEngine) getDeviceName() string { - // 尝试通过接口断言获取 ID - if idProvider, ok := e.executor.(interface{ GetID() string }); ok { - return idProvider.GetID() - } - return "设备" // 默认名称 -} - -// Start 启动一个动画 -func (e *AnimationEngine) Start(name string, speedMs int) error { - e.engineMutex.Lock() - defer e.engineMutex.Unlock() // 确保在任何情况下都释放锁 - - anim, exists := e.getAnimation(name) - if !exists { - return fmt.Errorf("❌ 动画 %s 未注册", name) - } - - // 如果有动画在运行,先发送停止信号 - if e.isRunning { - log.Printf("ℹ️ 正在停止当前动画 %s 以启动 %s...", e.current, name) - close(e.stopChan) - // 注意:我们不在此处等待旧动画结束。 - // 新动画将立即启动,旧动画的 goroutine 在收到信号后会退出。 - // 其 defer 中的 `stopChan` 比较会确保它不会干扰新动画的状态。 - } - - // 设置新动画状态 - e.stopChan = make(chan struct{}) // 创建新的停止通道 - e.isRunning = true - e.current = name - - // 验证并设置速度 - actualSpeedMs := speedMs - if actualSpeedMs <= 0 { - actualSpeedMs = defaultAnimationSpeedMs - } - - log.Printf("🚀 准备启动动画 %s (设备: %s, 速度: %dms)", name, e.getDeviceName(), actualSpeedMs) - - // 启动动画 goroutine - go e.runAnimationLoop(anim, e.stopChan, actualSpeedMs) - - return nil -} - -// Stop 停止当前正在运行的动画 -func (e *AnimationEngine) Stop() error { - e.engineMutex.Lock() - defer e.engineMutex.Unlock() - - if !e.isRunning { - log.Printf("ℹ️ 当前没有动画在运行 (设备: %s)", e.getDeviceName()) - return nil - } - - log.Printf("⏳ 正在发送停止信号给动画 %s (设备: %s)...", e.current, e.getDeviceName()) - close(e.stopChan) // 发送停止信号 - e.isRunning = false // 立即标记为未运行,防止重复停止 - e.current = "" - // 动画的 goroutine 将在下一次检查通道时退出, - // 并在其 defer 块中执行最终的清理(包括 ResetPose)。 - - return nil -} - -// IsRunning 检查是否有动画在运行 -func (e *AnimationEngine) IsRunning() bool { - e.engineMutex.Lock() - defer e.engineMutex.Unlock() - return e.isRunning -} - -// runAnimationLoop 是动画执行的核心循环,在单独的 Goroutine 中运行。 -func (e *AnimationEngine) runAnimationLoop(anim Animation, stopChan <-chan struct{}, speedMs int) { - deviceName := e.getDeviceName() - animName := anim.Name() - - // 使用 defer 确保无论如何都能执行清理逻辑 - defer e.handleLoopExit(stopChan, deviceName, animName) - - log.Printf("▶️ %s 动画 %s 已启动", deviceName, animName) - - // 动画主循环 - for { - select { - case <-stopChan: - log.Printf("🛑 %s 动画 %s 被显式停止", deviceName, animName) - return // 接收到停止信号,退出循环 - default: - // 执行一轮动画 - err := anim.Run(e.executor, stopChan, speedMs) - if err != nil { - log.Printf("❌ %s 动画 %s 执行出错: %v", deviceName, animName, err) - return // 出错则退出 - } - - // 再次检查停止信号,防止 Run 结束后才收到信号 - select { - case <-stopChan: - log.Printf("🛑 %s 动画 %s 在周期结束时被停止", deviceName, animName) - return - default: - // 继续下一个循环 - } - } - } -} - -// handleLoopExit 是动画 Goroutine 退出时执行的清理函数。 -func (e *AnimationEngine) handleLoopExit(stopChan <-chan struct{}, deviceName, animName string) { - e.engineMutex.Lock() - defer e.engineMutex.Unlock() - - // --- 关键并发控制 --- - // 检查当前引擎的 stopChan 是否与此 Goroutine 启动时的 stopChan 相同。 - // 如果不相同,说明一个新的动画已经启动,并且接管了引擎状态。 - // 这种情况下,旧的 Goroutine 不应该修改引擎状态或重置姿态, - // 以避免干扰新动画。 - if stopChan == e.stopChan { - // 只有当自己仍然是“活跃”的动画时,才更新状态并重置姿态 - e.isRunning = false - e.current = "" - log.Printf("👋 %s 动画 %s 已完成或停止,正在重置姿态...", deviceName, animName) - if err := e.executor.ResetPose(); err != nil { - log.Printf("⚠️ %s 动画结束后重置姿态失败: %v", deviceName, err) - } else { - log.Printf("✅ %s 姿态已重置", deviceName) - } - } else { - // 如果 stopChan 不同,说明自己是旧的 Goroutine,只需安静退出 - log.Printf("ℹ️ 旧的 %s 动画 %s goroutine 退出,但新动画已启动,无需重置。", deviceName, animName) - } -} diff --git a/pkg/device/factory.go b/pkg/device/factory.go deleted file mode 100644 index b147563..0000000 --- a/pkg/device/factory.go +++ /dev/null @@ -1,35 +0,0 @@ -package device - -import "fmt" - -// DeviceFactory 设备工厂 -type DeviceFactory struct { - constructors map[string]func(config map[string]any) (Device, error) -} - -var defaultFactory = &DeviceFactory{ - constructors: make(map[string]func(config map[string]any) (Device, error)), -} - -// RegisterDeviceType 注册设备类型 -func RegisterDeviceType(modelName string, constructor func(config map[string]any) (Device, error)) { - defaultFactory.constructors[modelName] = constructor -} - -// CreateDevice 创建设备实例 -func CreateDevice(modelName string, config map[string]any) (Device, error) { - constructor, ok := defaultFactory.constructors[modelName] - if !ok { - return nil, fmt.Errorf("未知的设备型号: %s", modelName) - } - return constructor(config) -} - -// GetSupportedModels 获取支持的设备型号列表 -func GetSupportedModels() []string { - models := make([]string, 0, len(defaultFactory.constructors)) - for model := range defaultFactory.constructors { - models = append(models, model) - } - return models -} diff --git a/pkg/device/manager.go b/pkg/device/manager.go deleted file mode 100644 index d7d6e8c..0000000 --- a/pkg/device/manager.go +++ /dev/null @@ -1,63 +0,0 @@ -package device - -import ( - "fmt" - "sync" -) - -// DeviceManager 管理设备实例 -type DeviceManager struct { - devices map[string]Device - mutex sync.RWMutex -} - -func NewDeviceManager() *DeviceManager { return &DeviceManager{devices: make(map[string]Device)} } - -func (m *DeviceManager) RegisterDevice(dev Device) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - id := dev.GetID() - if _, exists := m.devices[id]; exists { - return fmt.Errorf("设备 %s 已存在", id) - } - - m.devices[id] = dev - return nil -} - -func (m *DeviceManager) GetDevice(id string) (Device, error) { - m.mutex.RLock() - defer m.mutex.RUnlock() - - dev, exists := m.devices[id] - if !exists { - return nil, fmt.Errorf("设备 %s 不存在", id) - } - - return dev, nil -} - -func (m *DeviceManager) GetAllDevices() []Device { - m.mutex.RLock() - defer m.mutex.RUnlock() - - devices := make([]Device, 0, len(m.devices)) - for _, dev := range m.devices { - devices = append(devices, dev) - } - - return devices -} - -func (m *DeviceManager) RemoveDevice(id string) error { - m.mutex.Lock() - defer m.mutex.Unlock() - - if _, exists := m.devices[id]; !exists { - return fmt.Errorf("设备 %s 不存在", id) - } - - delete(m.devices, id) - return nil -} diff --git a/pkg/device/models/init.go b/pkg/device/models/init.go deleted file mode 100644 index c3910c7..0000000 --- a/pkg/device/models/init.go +++ /dev/null @@ -1,8 +0,0 @@ -package models - -import "hands/pkg/device" - -func init() { - // 注册 L10 设备类型 - device.RegisterDeviceType("L10", NewL10Hand) -} diff --git a/pkg/device/models/l10.go b/pkg/device/models/l10.go deleted file mode 100644 index 6f0f429..0000000 --- a/pkg/device/models/l10.go +++ /dev/null @@ -1,330 +0,0 @@ -package models - -import ( - "fmt" - "log" - "math/rand/v2" - "sync" - "time" - - "hands/define" - "hands/pkg/communication" - "hands/pkg/component" - "hands/pkg/device" -) - -// L10Hand L10 型号手部设备实现 -type L10Hand struct { - id string - model string - handType define.HandType - communicator communication.Communicator - components map[device.ComponentType][]device.Component - status device.DeviceStatus - mutex sync.RWMutex - canInterface string // CAN 接口名称,如 "can0" - animationEngine *device.AnimationEngine // 动画引擎 -} - -// 在 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) -} - -// NewL10Hand 创建 L10 手部设备实例 -func NewL10Hand(config map[string]any) (device.Device, error) { - id, ok := config["id"].(string) - if !ok { - return nil, fmt.Errorf("缺少设备 ID 配置") - } - - serviceURL, ok := config["can_service_url"].(string) - if !ok { - return nil, fmt.Errorf("缺少 can 服务 URL 配置") - } - - canInterface, ok := config["can_interface"].(string) - if !ok { - canInterface = "can0" // 默认接口 - } - - handTypeStr, ok := config["hand_type"].(string) - handType := define.HAND_TYPE_RIGHT // 默认右手 - if ok && handTypeStr == "left" { - handType = define.HAND_TYPE_LEFT - } - - // 创建通信客户端 - comm := communication.NewCanBridgeClient(serviceURL) - - hand := &L10Hand{ - id: id, - model: "L10", - handType: handType, - communicator: comm, - components: make(map[device.ComponentType][]device.Component), - canInterface: canInterface, - status: device.DeviceStatus{ - IsConnected: false, - IsActive: false, - LastUpdate: time.Now(), - }, - } - - // 初始化动画引擎,将 hand 自身作为 PoseExecutor - hand.animationEngine = device.NewAnimationEngine(hand) - - // 注册默认动画 - hand.animationEngine.Register(NewL10WaveAnimation()) - hand.animationEngine.Register(NewL10SwayAnimation()) - - // 初始化组件 - if err := hand.initializeComponents(config); err != nil { - return nil, fmt.Errorf("初始化组件失败:%w", err) - } - - log.Printf("✅ 设备 L10 (%s, %s) 创建成功", id, handType.String()) - return hand, nil -} - -// GetHandType 获取设备手型 -func (h *L10Hand) GetHandType() define.HandType { - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.handType -} - -// SetHandType 设置设备手型 -func (h *L10Hand) SetHandType(handType define.HandType) error { - h.mutex.Lock() - defer h.mutex.Unlock() - if handType != define.HAND_TYPE_LEFT && handType != define.HAND_TYPE_RIGHT { - return fmt.Errorf("无效的手型:%d", handType) - } - h.handType = handType - log.Printf("🔧 设备 %s 手型已更新: %s", h.id, handType.String()) - return nil -} - -// GetAnimationEngine 获取动画引擎 -func (h *L10Hand) GetAnimationEngine() *device.AnimationEngine { - return h.animationEngine -} - -// SetFingerPose 设置手指姿态 (实现 PoseExecutor) -func (h *L10Hand) SetFingerPose(pose []byte) error { - if len(pose) != 6 { - return fmt.Errorf("无效的手指姿态数据长度,需要 6 个字节") - } - - // 添加随机扰动 - perturbedPose := make([]byte, len(pose)) - for i, v := range pose { - perturbedPose[i] = perturb(v, 5) - } - - // 创建指令 - cmd := device.NewFingerPoseCommand("all", perturbedPose) - - // 执行指令 - err := h.ExecuteCommand(cmd) - if err == nil { - log.Printf("✅ %s (%s) 手指动作已发送: [%X %X %X %X %X %X]", - h.id, h.GetHandType().String(), perturbedPose[0], perturbedPose[1], perturbedPose[2], - perturbedPose[3], perturbedPose[4], perturbedPose[5]) - } - return err -} - -// SetPalmPose 设置手掌姿态 (实现 PoseExecutor) -func (h *L10Hand) SetPalmPose(pose []byte) error { - if len(pose) != 4 { - return fmt.Errorf("无效的手掌姿态数据长度,需要 4 个字节") - } - - // 添加随机扰动 - perturbedPose := make([]byte, len(pose)) - for i, v := range pose { - perturbedPose[i] = perturb(v, 8) - } - - // 创建指令 - cmd := device.NewPalmPoseCommand(perturbedPose) - - // 执行指令 - err := h.ExecuteCommand(cmd) - if err == nil { - log.Printf("✅ %s (%s) 掌部姿态已发送: [%X %X %X %X]", - h.id, h.GetHandType().String(), perturbedPose[0], perturbedPose[1], perturbedPose[2], perturbedPose[3]) - } - return err -} - -// ResetPose 重置到默认姿态 (实现 PoseExecutor) -func (h *L10Hand) ResetPose() error { - log.Printf("🔄 正在重置设备 %s (%s) 到默认姿态...", h.id, h.GetHandType().String()) - defaultFingerPose := []byte{64, 64, 64, 64, 64, 64} // 0x40 - 半开 - defaultPalmPose := []byte{128, 128, 128, 128} // 0x80 - 居中 - - if err := h.SetFingerPose(defaultFingerPose); err != nil { - log.Printf("❌ %s 重置手指姿势失败: %v", h.id, err) - return err - } - time.Sleep(20 * time.Millisecond) // 短暂延时 - if err := h.SetPalmPose(defaultPalmPose); err != nil { - log.Printf("❌ %s 重置掌部姿势失败: %v", h.id, err) - return err - } - log.Printf("✅ 设备 %s 已重置到默认姿态", h.id) - return nil -} - -// commandToRawMessage 将通用指令转换为 L10 特定的 CAN 消息 -func (h *L10Hand) commandToRawMessage(cmd device.Command) (communication.RawMessage, error) { - h.mutex.RLock() - defer h.mutex.RUnlock() - - var data []byte - canID := uint32(h.handType) - - switch cmd.Type() { - case "SetFingerPose": - // 添加 0x01 前缀 - data = append([]byte{0x01}, cmd.Payload()...) - if len(data) > 8 { // CAN 消息数据长度限制 - return communication.RawMessage{}, fmt.Errorf("手指姿态数据过长") - } - case "SetPalmPose": - // 添加 0x04 前缀 - data = append([]byte{0x04}, cmd.Payload()...) - if len(data) > 8 { // CAN 消息数据长度限制 - return communication.RawMessage{}, fmt.Errorf("手掌姿态数据过长") - } - default: - return communication.RawMessage{}, fmt.Errorf("L10 不支持的指令类型: %s", cmd.Type()) - } - - return communication.RawMessage{ - Interface: h.canInterface, - ID: canID, - Data: data, - }, nil -} - -// ExecuteCommand 执行一个通用指令 -func (h *L10Hand) ExecuteCommand(cmd device.Command) error { - h.mutex.Lock() // 使用写锁,因为会更新状态 - defer h.mutex.Unlock() - - if !h.status.IsConnected || !h.status.IsActive { - return fmt.Errorf("设备 %s 未连接或未激活", h.id) - } - - // 转换指令为 CAN 消息 - rawMsg, err := h.commandToRawMessage(cmd) - if err != nil { - h.status.ErrorCount++ - h.status.LastError = err.Error() - return fmt.Errorf("转换指令失败:%w", err) - } - - // 发送到 can-bridge 服务 - if err := h.communicator.SendMessage(rawMsg); err != nil { - h.status.ErrorCount++ - h.status.LastError = err.Error() - log.Printf("❌ %s (%s) 发送指令失败: %v (ID: 0x%X, Data: %X)", h.id, h.handType.String(), err, rawMsg.ID, rawMsg.Data) - return fmt.Errorf("发送指令失败:%w", err) - } - - 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"}), - } - h.components[device.SensorComponent] = sensors - return nil -} - -func (h *L10Hand) GetID() string { - return h.id -} - -func (h *L10Hand) GetModel() string { - return h.model -} - -func (h *L10Hand) ReadSensorData(sensorID string) (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() - } - } - } - return nil, fmt.Errorf("传感器 %s 不存在", sensorID) -} - -func (h *L10Hand) GetComponents(componentType device.ComponentType) []device.Component { - h.mutex.RLock() - defer h.mutex.RUnlock() - - if components, exists := h.components[componentType]; exists { - result := make([]device.Component, len(components)) - copy(result, components) - return result - } - return []device.Component{} -} - -func (h *L10Hand) GetStatus() (device.DeviceStatus, error) { - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.status, nil -} - -func (h *L10Hand) Connect() error { - h.mutex.Lock() - defer h.mutex.Unlock() - - // TODO: 假设连接总是成功,除非有显式错误 - h.status.IsConnected = true - h.status.IsActive = true - h.status.LastUpdate = time.Now() - log.Printf("🔗 设备 %s 已连接", h.id) - return nil -} - -func (h *L10Hand) Disconnect() error { - h.mutex.Lock() - defer h.mutex.Unlock() - - h.status.IsConnected = false - h.status.IsActive = false - h.status.LastUpdate = time.Now() - log.Printf("🔌 设备 %s 已断开", h.id) - return nil -} diff --git a/pkg/device/models/l10_animation.go b/pkg/device/models/l10_animation.go deleted file mode 100644 index db729d0..0000000 --- a/pkg/device/models/l10_animation.go +++ /dev/null @@ -1,125 +0,0 @@ -package models - -import ( - "hands/pkg/device" - "log" - "time" -) - -// --- L10WaveAnimation --- - -// L10WaveAnimation 实现 L10 的波浪动画 -type L10WaveAnimation struct{} - -// NewL10WaveAnimation 创建 L10 波浪动画实例 -func NewL10WaveAnimation() *L10WaveAnimation { return &L10WaveAnimation{} } - -func (w *L10WaveAnimation) Name() string { return "wave" } - -func (w *L10WaveAnimation) Run(executor device.PoseExecutor, stop <-chan struct{}, speedMs int) error { - fingerOrder := []int{0, 1, 2, 3, 4, 5} - open := byte(64) // 0x40 - close := byte(192) // 0xC0 - delay := time.Duration(speedMs) * time.Millisecond - - deviceName := "L10" - - // 波浪张开 - 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 := executor.SetFingerPose(pose); err != nil { - log.Printf("❌ %s 动画 %s 发送失败: %v", deviceName, w.Name(), err) - return err - } - - select { - case <-stop: - return nil // 动画被停止 - 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 := executor.SetFingerPose(pose); err != nil { - log.Printf("❌ %s 动画 %s 发送失败: %v", deviceName, w.Name(), err) - return err - } - - select { - case <-stop: - return nil // 动画被停止 - case <-time.After(delay): - // 继续 - } - } - - return nil // 完成一个周期 -} - -// --- L10SwayAnimation --- - -// L10SwayAnimation 实现 L10 的横向摆动动画 -type L10SwayAnimation struct{} - -// NewL10SwayAnimation 创建 L10 摆动动画实例 -func NewL10SwayAnimation() *L10SwayAnimation { return &L10SwayAnimation{} } - -func (s *L10SwayAnimation) Name() string { return "sway" } - -func (s *L10SwayAnimation) Run(executor device.PoseExecutor, stop <-chan struct{}, speedMs int) error { - leftPose := []byte{48, 48, 48, 48} // 0x30 - rightPose := []byte{208, 208, 208, 208} // 0xD0 - delay := time.Duration(speedMs) * time.Millisecond - - deviceName := "L10" - if idProvider, ok := executor.(interface{ GetID() string }); ok { - deviceName = idProvider.GetID() - } - - // 向左移动 - if err := executor.SetPalmPose(leftPose); err != nil { - log.Printf("❌ %s 动画 %s 发送失败: %v", deviceName, s.Name(), err) - return err - } - - select { - case <-stop: - return nil // 动画被停止 - case <-time.After(delay): - // 继续 - } - - // 向右移动 - if err := executor.SetPalmPose(rightPose); err != nil { - log.Printf("❌ %s 动画 %s 发送失败: %v", deviceName, s.Name(), err) - return err - } - - select { - case <-stop: - return nil // 动画被停止 - case <-time.After(delay): - // 继续 - } - - return nil // 完成一个周期 -} diff --git a/pkg/device/pose_executor.go b/pkg/device/pose_executor.go deleted file mode 100644 index eb79dcb..0000000 --- a/pkg/device/pose_executor.go +++ /dev/null @@ -1,20 +0,0 @@ -package device - -import "hands/define" - -// PoseExecutor 定义了执行基本姿态指令的能力 -type PoseExecutor interface { - // SetFingerPose 设置手指姿态 - // pose: 6 字节数据,代表 6 个手指的位置 - SetFingerPose(pose []byte) error - - // SetPalmPose 设置手掌姿态 - // pose: 4 字节数据,代表手掌的 4 个自由度 - SetPalmPose(pose []byte) error - - // ResetPose 重置到默认姿态 - ResetPose() error - - // GetHandType 获取当前手型 - GetHandType() define.HandType -}