336 lines
9.4 KiB
Go
336 lines
9.4 KiB
Go
package models
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"math/rand/v2"
|
|
"sync"
|
|
"time"
|
|
|
|
"hands/communication"
|
|
"hands/component"
|
|
"hands/define"
|
|
"hands/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 手部设备实例
|
|
// 参数 config 是设备配置,包含以下字段:
|
|
// - id: 设备 ID
|
|
// - can_service_url: CAN 服务 URL
|
|
// - can_interface: CAN 接口名称,如 "can0"
|
|
// - hand_type: 手型,可选值为 "left" 或 "right",默认值为 "right"
|
|
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
|
|
}
|