from pydantic import BaseModel, Field from typing import Dict, List, Optional, Any, Literal from datetime import datetime from enum import Enum # ============= ROBOT COMMAND MODELS ============= class JointUpdate(BaseModel): name: str value: float speed: Optional[float] = None class RobotCommand(BaseModel): timestamp: int joints: List[JointUpdate] duration: Optional[int] = None metadata: Optional[Dict[str, Any]] = None class CommandSequence(BaseModel): id: str name: str commands: List[RobotCommand] loop: Optional[bool] = False total_duration: int # ============= COMMUNICATION MODELS ============= class ControlMessage(BaseModel): type: Literal["command", "sequence", "heartbeat", "status_request"] timestamp: str data: Optional[Dict[str, Any]] = None class StatusMessage(BaseModel): type: Literal["status", "joint_states", "error", "heartbeat_ack", "slave_status"] timestamp: str slave_id: Optional[str] = None data: Optional[Dict[str, Any]] = None # ============= ROBOT MODELS ============= class JointLimits(BaseModel): lower: Optional[float] = None upper: Optional[float] = None velocity: Optional[float] = None effort: Optional[float] = None class DriverJointState(BaseModel): name: str servo_id: int type: Literal["revolute", "continuous"] virtual_value: float real_value: Optional[float] = None limits: Optional[JointLimits] = None class ConnectionStatus(BaseModel): is_connected: bool last_connected: Optional[datetime] = None error: Optional[str] = None # ============= ROBOT MANAGEMENT ============= class Robot(BaseModel): id: str name: str robot_type: str created_at: datetime joints: List[DriverJointState] = [] # Connection state master_connected: bool = False master_connection_id: Optional[str] = None slave_connections: List[str] = [] # Control state last_command_source: Literal["master", "manual", "none"] = "none" last_command_time: Optional[datetime] = None class RobotStatus(BaseModel): robot_id: str master_connected: bool slave_count: int last_command_source: str last_command_time: Optional[datetime] last_seen: datetime class CreateRobotRequest(BaseModel): name: Optional[str] = None robot_type: str = "so-arm100" # ============= DEMO SEQUENCES ============= DEMO_SEQUENCES = [ CommandSequence( id="gentle-wave", name="Gentle Wave Pattern", total_duration=6000, commands=[ RobotCommand( timestamp=0, joints=[ JointUpdate(name="Rotation", value=-10), JointUpdate(name="Pitch", value=8), JointUpdate(name="Elbow", value=-12) ], duration=2000 ), RobotCommand( timestamp=2000, joints=[JointUpdate(name="Wrist_Roll", value=10)], duration=1000 ), RobotCommand( timestamp=3000, joints=[JointUpdate(name="Wrist_Roll", value=-10)], duration=1000 ), RobotCommand( timestamp=4000, joints=[ JointUpdate(name="Wrist_Roll", value=0), JointUpdate(name="Rotation", value=0), JointUpdate(name="Pitch", value=0), JointUpdate(name="Elbow", value=0) ], duration=2000 ) ] ), CommandSequence( id="small-scan", name="Small Scanning Pattern", total_duration=8000, commands=[ RobotCommand( timestamp=0, joints=[ JointUpdate(name="Rotation", value=-15), JointUpdate(name="Pitch", value=10) ], duration=2000 ), RobotCommand( timestamp=2000, joints=[JointUpdate(name="Rotation", value=15)], duration=3000 ), RobotCommand( timestamp=5000, joints=[ JointUpdate(name="Rotation", value=0), JointUpdate(name="Pitch", value=0) ], duration=3000 ) ] ), CommandSequence( id="tiny-flex", name="Tiny Flex Pattern", total_duration=8000, commands=[ RobotCommand( timestamp=0, joints=[ JointUpdate(name="Elbow", value=-15), JointUpdate(name="Wrist_Pitch", value=8) ], duration=2000 ), RobotCommand( timestamp=2000, joints=[JointUpdate(name="Jaw", value=8)], duration=1000 ), RobotCommand( timestamp=3000, joints=[JointUpdate(name="Elbow", value=-25)], duration=2000 ), RobotCommand( timestamp=5000, joints=[ JointUpdate(name="Jaw", value=0), JointUpdate(name="Elbow", value=0), JointUpdate(name="Wrist_Pitch", value=0) ], duration=3000 ) ] ) ]