| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481 |
- """
- Base Command Classes for Trixy Network Protocol
- This module defines the base classes for all network commands in the Trixy
- system. These classes provide:
- - Common interface for all commands
- - Serialization/deserialization support
- - Validation and error handling
- - Response management
- - Type safety and documentation
- All network commands inherit from these base classes to ensure consistency
- and proper integration with the Trixy protocol.
- """
- import time
- import uuid
- from typing import Any, Dict, Optional, Union, List
- from dataclasses import dataclass, field
- from enum import Enum, IntEnum
- from abc import ABC, abstractmethod
- import json
- def pprint(message: str) -> None:
- """Base command logging function."""
- print(f"[CMD.BASE] {message}")
- class CommandStatus(IntEnum):
- """Status codes for command execution."""
- SUCCESS = 0
- ERROR = 1
- PENDING = 2
- TIMEOUT = 3
- INVALID = 4
- UNAUTHORIZED = 5
- NOT_FOUND = 6
- CONFLICT = 7
- class ResponseType(Enum):
- """Types of command responses."""
- SUCCESS = "success"
- ERROR = "error"
- ACK = "acknowledgment"
- DATA = "data"
- PARTIAL = "partial"
- class CommandError(Exception):
- """Base exception for command-related errors."""
-
- def __init__(self, message: str, status: CommandStatus = CommandStatus.ERROR):
- super().__init__(message)
- self.status = status
- class ValidationError(CommandError):
- """Raised when command validation fails."""
-
- def __init__(self, message: str):
- super().__init__(message, CommandStatus.INVALID)
- @dataclass
- class TrixyCommand(ABC):
- """
- Base class for all Trixy network commands.
-
- This abstract base class defines the common interface that all commands
- must implement. It provides basic functionality for:
- - Unique command identification
- - Timestamp tracking
- - Serialization support
- - Validation
- - Response handling
-
- All command classes must inherit from this class and implement the
- required abstract methods.
- """
-
- # Core fields that all commands have
- command_id: str = field(default_factory=lambda: str(uuid.uuid4()))
- timestamp: float = field(default_factory=time.time)
- version: str = field(default="1.0.0")
- source: Optional[str] = field(default=None)
- destination: Optional[str] = field(default=None)
-
- def __post_init__(self):
- """Post-initialization validation and setup."""
- self.validate()
-
- @abstractmethod
- def validate(self) -> bool:
- """
- Validate the command data.
-
- Returns:
- bool: True if valid
-
- Raises:
- ValidationError: If validation fails
- """
- pass
-
- @abstractmethod
- def get_command_name(self) -> str:
- """
- Get the command name for protocol identification.
-
- Returns:
- str: Command name
- """
- pass
-
- def to_dict(self) -> Dict[str, Any]:
- """
- Convert command to dictionary for serialization.
-
- Returns:
- Dict[str, Any]: Command data as dictionary
- """
- result = {}
- for key, value in self.__dict__.items():
- if not key.startswith('_'):
- result[key] = value
- return result
-
- @classmethod
- def from_dict(cls, data: Dict[str, Any]) -> 'TrixyCommand':
- """
- Create command instance from dictionary.
-
- Args:
- data: Dictionary with command data
-
- Returns:
- TrixyCommand: Command instance
- """
- return cls(**data)
-
- def to_json(self) -> str:
- """
- Convert command to JSON string.
-
- Returns:
- str: JSON representation
- """
- return json.dumps(self.to_dict())
-
- @classmethod
- def from_json(cls, json_str: str) -> 'TrixyCommand':
- """
- Create command from JSON string.
-
- Args:
- json_str: JSON string
-
- Returns:
- TrixyCommand: Command instance
- """
- data = json.loads(json_str)
- return cls.from_dict(data)
-
- def get_response_class(self):
- """
- Get the expected response class for this command.
-
- Returns:
- Class: Response class or None if no response expected
- """
- return None
-
- def requires_response(self) -> bool:
- """
- Check if this command requires a response.
-
- Returns:
- bool: True if response is required
- """
- return self.get_response_class() is not None
-
- def get_timeout(self) -> float:
- """
- Get the timeout for this command in seconds.
-
- Returns:
- float: Timeout in seconds
- """
- return 30.0 # Default 30 second timeout
-
- def is_expired(self) -> bool:
- """
- Check if this command has expired based on its timeout.
-
- Returns:
- bool: True if expired
- """
- return time.time() - self.timestamp > self.get_timeout()
-
- def get_priority(self) -> int:
- """
- Get command priority (lower numbers = higher priority).
-
- Returns:
- int: Priority value (0-100)
- """
- return 50 # Normal priority
-
- def __str__(self) -> str:
- """String representation of the command."""
- return f"{self.get_command_name()}({self.command_id[:8]}...)"
-
- def __repr__(self) -> str:
- """Detailed string representation."""
- return f"{self.__class__.__name__}(id={self.command_id}, timestamp={self.timestamp})"
- @dataclass
- class CommandResponse(ABC):
- """
- Base class for all command responses.
-
- This class provides the standard interface for responses to commands.
- It includes status information, timing data, and optional response data.
- """
-
- # Response identification
- response_id: str = field(default_factory=lambda: str(uuid.uuid4()))
- command_id: str = field(default="")
-
- # Status and timing
- status: CommandStatus = field(default=CommandStatus.SUCCESS)
- response_type: ResponseType = field(default=ResponseType.SUCCESS)
- timestamp: float = field(default_factory=time.time)
-
- # Optional fields
- message: Optional[str] = field(default=None)
- error_details: Optional[Dict[str, Any]] = field(default=None)
- data: Optional[Dict[str, Any]] = field(default=None)
-
- def __post_init__(self):
- """Post-initialization setup."""
- self.validate()
-
- @abstractmethod
- def validate(self) -> bool:
- """
- Validate the response data.
-
- Returns:
- bool: True if valid
-
- Raises:
- ValidationError: If validation fails
- """
- pass
-
- @abstractmethod
- def get_response_name(self) -> str:
- """
- Get the response name for protocol identification.
-
- Returns:
- str: Response name
- """
- pass
-
- def is_success(self) -> bool:
- """
- Check if the response indicates success.
-
- Returns:
- bool: True if successful
- """
- return self.status == CommandStatus.SUCCESS
-
- def is_error(self) -> bool:
- """
- Check if the response indicates an error.
-
- Returns:
- bool: True if error
- """
- return self.status != CommandStatus.SUCCESS
-
- def get_error_message(self) -> str:
- """
- Get error message if response is an error.
-
- Returns:
- str: Error message or empty string
- """
- if self.is_error():
- return self.message or f"Command failed with status: {self.status.name}"
- return ""
-
- def to_dict(self) -> Dict[str, Any]:
- """
- Convert response to dictionary.
-
- Returns:
- Dict[str, Any]: Response data as dictionary
- """
- result = {}
- for key, value in self.__dict__.items():
- if not key.startswith('_'):
- if isinstance(value, Enum):
- result[key] = value.value
- else:
- result[key] = value
- return result
-
- @classmethod
- def from_dict(cls, data: Dict[str, Any]) -> 'CommandResponse':
- """
- Create response from dictionary.
-
- Args:
- data: Dictionary with response data
-
- Returns:
- CommandResponse: Response instance
- """
- # Handle enum fields
- if 'status' in data and isinstance(data['status'], (str, int)):
- data['status'] = CommandStatus(data['status'])
- if 'response_type' in data and isinstance(data['response_type'], str):
- data['response_type'] = ResponseType(data['response_type'])
-
- return cls(**data)
-
- def __str__(self) -> str:
- """String representation of the response."""
- return f"{self.get_response_name()}({self.status.name})"
- # Concrete response implementations for common cases
- @dataclass
- class SuccessResponse(CommandResponse):
- """Generic success response."""
-
- def validate(self) -> bool:
- """Validate success response."""
- if self.status != CommandStatus.SUCCESS:
- raise ValidationError("SuccessResponse must have SUCCESS status")
- return True
-
- def get_response_name(self) -> str:
- """Get response name."""
- return "SuccessResponse"
- @dataclass
- class ErrorResponse(CommandResponse):
- """Generic error response."""
-
- def __post_init__(self):
- """Set error status if not already set."""
- if self.status == CommandStatus.SUCCESS:
- self.status = CommandStatus.ERROR
- self.response_type = ResponseType.ERROR
- super().__post_init__()
-
- def validate(self) -> bool:
- """Validate error response."""
- if self.status == CommandStatus.SUCCESS:
- raise ValidationError("ErrorResponse cannot have SUCCESS status")
- if not self.message:
- raise ValidationError("ErrorResponse must have an error message")
- return True
-
- def get_response_name(self) -> str:
- """Get response name."""
- return "ErrorResponse"
- @dataclass
- class AckResponse(CommandResponse):
- """Simple acknowledgment response."""
-
- def __post_init__(self):
- """Set ACK response type."""
- self.response_type = ResponseType.ACK
- super().__post_init__()
-
- def validate(self) -> bool:
- """Validate ACK response."""
- return True
-
- def get_response_name(self) -> str:
- """Get response name."""
- return "AckResponse"
- # Utility functions
- def create_success_response(
- command_id: str,
- message: str = "Command completed successfully",
- data: Optional[Dict[str, Any]] = None
- ) -> SuccessResponse:
- """
- Create a success response for a command.
-
- Args:
- command_id: ID of the command being responded to
- message: Success message
- data: Optional response data
-
- Returns:
- SuccessResponse: Success response
- """
- return SuccessResponse(
- command_id=command_id,
- message=message,
- data=data
- )
- def create_error_response(
- command_id: str,
- message: str,
- status: CommandStatus = CommandStatus.ERROR,
- error_details: Optional[Dict[str, Any]] = None
- ) -> ErrorResponse:
- """
- Create an error response for a command.
-
- Args:
- command_id: ID of the command being responded to
- message: Error message
- status: Error status code
- error_details: Optional error details
-
- Returns:
- ErrorResponse: Error response
- """
- return ErrorResponse(
- command_id=command_id,
- message=message,
- status=status,
- error_details=error_details
- )
- def create_ack_response(command_id: str, message: str = "Command acknowledged") -> AckResponse:
- """
- Create an acknowledgment response for a command.
-
- Args:
- command_id: ID of the command being responded to
- message: ACK message
-
- Returns:
- AckResponse: Acknowledgment response
- """
- return AckResponse(
- command_id=command_id,
- message=message
- )
- # Module exports
- __all__ = [
- "TrixyCommand",
- "CommandResponse",
- "CommandStatus",
- "ResponseType",
- "CommandError",
- "ValidationError",
- "SuccessResponse",
- "ErrorResponse",
- "AckResponse",
- "create_success_response",
- "create_error_response",
- "create_ack_response",
- ]
|