feat: implement device and l10 model
This commit is contained in:
parent
16180d221e
commit
553873799e
80
pkg/device/commands.go
Normal file
80
pkg/device/commands.go
Normal 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
60
pkg/device/device.go
Normal 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
35
pkg/device/factory.go
Normal 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
|
||||||
|
}
|
8
pkg/device/models/init.go
Normal file
8
pkg/device/models/init.go
Normal 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
240
pkg/device/models/l10.go
Normal 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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user