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