base_agent/uas/uas_control/command.py

281 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
无人系统控制指令模型
─────────────────────────────────────────────────────────────────
Command 指令基类dataclass
CommandResult 指令执行结果
CommandType 指令类型枚举(覆盖无人机/机器狗/通用)
CommandStatus 指令状态枚举
─────────────────────────────────────────────────────────────────
"""
from __future__ import annotations
import dataclasses
import time
import uuid
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict
# ─────────────────────────────────────────────────────────────────
# 指令类型枚举
# ─────────────────────────────────────────────────────────────────
class CommandType(Enum):
# ── 通用指令 ──────────────────────────────────────────────────
ARM = "arm" # 解锁/上电
DISARM = "disarm" # 加锁/断电
START = "start" # 启动
STOP = "stop" # 停止
PAUSE = "pause" # 暂停
RESUME = "resume" # 恢复
RESET = "reset" # 重置
EMERGENCY_STOP = "emergency_stop" # 紧急停止
SET_MODE = "set_mode" # 设置飞行/运动模式
HEARTBEAT = "heartbeat" # 心跳保活
# ── 运动指令 ──────────────────────────────────────────────────
TAKEOFF = "takeoff" # 起飞(无人机)
LAND = "land" # 降落(无人机)
RTL = "rtl" # 返回起飞点
GOTO = "goto" # 飞往/走往目标点
SET_VELOCITY = "set_velocity" # 设置速度向量
SET_HEADING = "set_heading" # 设置航向
SET_ALTITUDE = "set_altitude" # 设置目标高度
FOLLOW_ROUTE = "follow_route" # 执行预设航线
LOITER = "loiter" # 盘旋/原地待命
# ── 载荷指令 ──────────────────────────────────────────────────
PAYLOAD_ON = "payload_on" # 载荷上电
PAYLOAD_OFF = "payload_off" # 载荷断电
CAMERA_SHOOT = "camera_shoot" # 拍照
CAMERA_RECORD = "camera_record" # 录像开关
GIMBAL_CONTROL = "gimbal_control" # 云台控制
# ── 机器狗专有 ────────────────────────────────────────────────
SIT = "sit" # 坐下
STAND = "stand" # 站立
WALK = "walk" # 行走
TROT = "trot" # 小跑
CLIMB_STAIRS = "climb_stairs" # 爬楼梯
SET_GAIT = "set_gait" # 设置步态
# ── 自定义扩展 ────────────────────────────────────────────────
CUSTOM = "custom" # 自定义指令
# ─────────────────────────────────────────────────────────────────
# 指令状态枚举
# ─────────────────────────────────────────────────────────────────
class CommandStatus(Enum):
PENDING = "pending" # 待发送
SENT = "sent" # 已发送,等待确认
ACCEPTED = "accepted" # 设备已接受
EXECUTING = "executing" # 执行中
COMPLETED = "completed" # 执行完成
FAILED = "failed" # 执行失败
TIMEOUT = "timeout" # 超时未响应
REJECTED = "rejected" # 设备拒绝
# ─────────────────────────────────────────────────────────────────
# 指令基类
# ─────────────────────────────────────────────────────────────────
@dataclass
class Command:
"""
无人系统控制指令
所有指令均继承此类,通过 params 字典传递指令参数。
子类可新增强类型字段以提升可读性。
"""
# ── 标识 ──────────────────────────────────────────────────────
command_id: str = field(
default_factory=lambda: str(uuid.uuid4())[:8]
)
command_type: CommandType = CommandType.CUSTOM
target_id: str = "" # 目标设备 ID
# ── 参数 ──────────────────────────────────────────────────────
params: Dict[str, Any] = field(default_factory=dict)
# ── 时序 ──────────────────────────────────────────────────────
created_at: float = field(default_factory=time.time)
timeout_s: float = 10.0 # 超时时间(秒)
priority: int = 0 # 优先级(越大越高)
# ── 状态 ──────────────────────────────────────────────────────
status: CommandStatus = CommandStatus.PENDING
# ── 序列化 ────────────────────────────────────────────────────
def to_dict(self) -> Dict[str, Any]:
d = dataclasses.asdict(self)
d["command_type"] = self.command_type.value
d["status"] = self.status.value
return d
def __repr__(self) -> str:
return (f"Command({self.command_type.value}"
f"[{self.command_id}]"
f"{self.target_id}"
f" {self.status.value})")
# ─────────────────────────────────────────────────────────────────
# 常用指令工厂方法
# ─────────────────────────────────────────────────────────────────
class Commands:
"""常用指令快速构造工厂"""
@staticmethod
def guide(target_id: str):
return Command(command_type=CommandType.CUSTOM,
target_id=target_id)
@staticmethod
def arm(target_id: str, **kw) -> Command:
return Command(command_type=CommandType.ARM,
target_id=target_id, **kw)
@staticmethod
def disarm(target_id: str, **kw) -> Command:
return Command(command_type=CommandType.DISARM,
target_id=target_id, **kw)
@staticmethod
def takeoff(target_id: str, altitude: float,
speed: float = 3.0, **kw) -> Command:
return Command(command_type=CommandType.TAKEOFF,
target_id=target_id,
params={"altitude": altitude, "speed": speed},
**kw)
@staticmethod
def land(target_id: str, **kw) -> Command:
return Command(command_type=CommandType.LAND,
target_id=target_id, **kw)
@staticmethod
def rtl(target_id: str, **kw) -> Command:
return Command(command_type=CommandType.RTL,
target_id=target_id, **kw)
@staticmethod
def goto(target_id: str, lat: float, lon: float,
alt: float, speed: float = 5.0, **kw) -> Command:
return Command(command_type=CommandType.GOTO,
target_id=target_id,
params={"lat": lat, "lon": lon,
"alt": alt, "speed": speed},
**kw)
@staticmethod
def set_velocity(target_id: str,
vx: float, vy: float, vz: float = 0.0,
**kw) -> Command:
return Command(command_type=CommandType.SET_VELOCITY,
target_id=target_id,
params={"vx": vx, "vy": vy, "vz": vz},
**kw)
@staticmethod
def emergency_stop(target_id: str, **kw) -> Command:
return Command(command_type=CommandType.EMERGENCY_STOP,
target_id=target_id,
priority=999, **kw)
@staticmethod
def set_mode(target_id: str, mode: str, **kw) -> Command:
return Command(command_type=CommandType.SET_MODE,
target_id=target_id,
params={"mode": mode}, **kw)
@staticmethod
def sit(target_id: str, **kw) -> Command:
return Command(command_type=CommandType.SIT,
target_id=target_id, **kw)
@staticmethod
def stand(target_id: str, **kw) -> Command:
return Command(command_type=CommandType.STAND,
target_id=target_id, **kw)
@staticmethod
def walk(target_id: str, speed: float = 1.0,
heading: float = 0.0, **kw) -> Command:
return Command(command_type=CommandType.WALK,
target_id=target_id,
params={"speed": speed, "heading": heading},
**kw)
@staticmethod
def custom(target_id: str, model: int,
**params) -> Command:
return Command(command_type=CommandType.CUSTOM,
target_id=target_id,
params={"model": model, **params})
# ─────────────────────────────────────────────────────────────────
# 指令执行结果
# ─────────────────────────────────────────────────────────────────
@dataclass
class CommandResult:
"""指令执行结果"""
command_id: str = ""
command_type: str = ""
status: CommandStatus = CommandStatus.PENDING
success: bool = False
message: str = ""
data: Dict[str, Any] = field(default_factory=dict)
elapsed_ms: float = 0.0
timestamp: float = field(default_factory=time.time)
@classmethod
def ok(cls, cmd: Command, message: str = "成功",
data: dict = None, elapsed_ms: float = 0.0) -> "CommandResult":
return cls(
command_id = cmd.command_id,
command_type = cmd.command_type.value,
status = CommandStatus.COMPLETED,
success = True,
message = message,
data = data or {},
elapsed_ms = elapsed_ms,
)
@classmethod
def fail(cls, cmd: Command, message: str = "失败",
elapsed_ms: float = 0.0) -> "CommandResult":
return cls(
command_id = cmd.command_id,
command_type = cmd.command_type.value,
status = CommandStatus.FAILED,
success = False,
message = message,
elapsed_ms = elapsed_ms,
)
@classmethod
def timeout(cls, cmd: Command) -> "CommandResult":
return cls(
command_id = cmd.command_id,
command_type = cmd.command_type.value,
status = CommandStatus.TIMEOUT,
success = False,
message = f"指令超时(>{cmd.timeout_s}s",
)
def __repr__(self) -> str:
mark = "" if self.success else ""
return (f"{mark} CommandResult("
f"{self.command_type}[{self.command_id}] "
f"{self.status.value}: {self.message})")