feat: implement device and l10 model

This commit is contained in:
Eli Yip 2025-05-27 15:24:23 +08:00
parent 16180d221e
commit 553873799e
No known key found for this signature in database
GPG Key ID: C98B69D4CF7D7EC5
5 changed files with 423 additions and 0 deletions

80
pkg/device/commands.go Normal file
View File

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

60
pkg/device/device.go Normal file
View File

@ -0,0 +1,60 @@
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 // 断开设备连接
}
// 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
}

35
pkg/device/factory.go Normal file
View File

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

View File

@ -0,0 +1,8 @@
package models
import "hands/pkg/device"
func init() {
// 注册 L10 设备类型
device.RegisterDeviceType("L10", NewL10Hand)
}

240
pkg/device/models/l10.go Normal file
View File

@ -0,0 +1,240 @@
package models
import (
"fmt"
"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"
}
// 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" // 默认接口
}
handType, ok := config["hand_type"].(define.HandType)
if !ok {
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(),
},
}
// 初始化组件
if err := hand.initializeComponents(config); err != nil {
return nil, fmt.Errorf("初始化组件失败:%w", err)
}
return hand, nil
}
func (h *L10Hand) GetHandType() define.HandType {
return h.handType
}
func (h *L10Hand) SetHandType(handType define.HandType) error {
h.handType = handType
return nil
}
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) ExecuteCommand(cmd device.Command) error {
h.mutex.Lock()
defer h.mutex.Unlock()
// 将通用指令转换为 L10 特定的 CAN 消息
rawMsg, err := h.commandToRawMessage(cmd)
if err != nil {
return fmt.Errorf("转换指令失败:%w", err)
}
// 发送到 can-bridge 服务
if err := h.communicator.SendMessage(rawMsg); err != nil {
h.status.ErrorCount++
h.status.LastError = err.Error()
return fmt.Errorf("发送指令失败:%w", err)
}
h.status.LastUpdate = time.Now()
return nil
}
func (h *L10Hand) commandToRawMessage(cmd device.Command) (communication.RawMessage, error) {
var canID uint32
var data []byte
switch cmd.Type() {
case "SetFingerPose":
// 根据目标组件确定 CAN ID
canID = h.getFingerCanID(cmd.TargetComponent())
data = cmd.Payload()
case "SetPalmPose":
canID = h.getPalmCanID()
data = cmd.Payload()
default:
return communication.RawMessage{}, fmt.Errorf("不支持的指令类型: %s", cmd.Type())
}
return communication.RawMessage{
Interface: h.canInterface,
ID: canID,
Data: data,
}, nil
}
func (h *L10Hand) getFingerCanID(targetComponent string) uint32 {
// L10 设备的手指 CAN ID 映射
fingerIDs := map[string]uint32{
"finger_thumb": 0x100,
"finger_index": 0x101,
"finger_middle": 0x102,
"finger_ring": 0x103,
"finger_pinky": 0x104,
}
if id, exists := fingerIDs[targetComponent]; exists {
return id
}
return 0x100 // 默认拇指
}
func (h *L10Hand) getPalmCanID() uint32 {
return 0x200 // L10 设备的手掌 CAN ID
}
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()
// 检查与 can-bridge 服务的连接
if !h.communicator.IsConnected() {
return fmt.Errorf("无法连接到 can-bridge 服务")
}
// 检查 CAN 接口状态
isActive, err := h.communicator.GetInterfaceStatus(h.canInterface)
if err != nil {
return fmt.Errorf("检查 CAN 接口状态失败:%w", err)
}
if !isActive {
return fmt.Errorf("CAN接口 %s 未激活", h.canInterface)
}
h.status.IsConnected = true
h.status.IsActive = true
h.status.LastUpdate = time.Now()
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()
return nil
}