zdt_motor Brick:用 Python 控制一台 ZDT 电机app/bricks/zdt_motor/config.py
"""集中保存电机地址、固件、方向和校验方式等公共设置。"""
from dataclasses import dataclass
from enum import Enum, IntEnum
from .errors import ZDTConfigurationError
class Firmware(str, Enum):
"""电机菜单中的 FWType;必须按实际值选择 Emm 或 X。"""
EMM = "emm"
X = "x"
class ChecksumType(str, Enum):
"""ZDT 自由协议校验方式。"""
FIXED_6B = "fixed_6b"
XOR = "xor"
CRC8 = "crc8"
class Direction(IntEnum):
"""手册中的电机运动方向。"""
CW = 0
CCW = 1
class MotionMode(IntEnum):
"""位置运动参考方式。"""
RELATIVE_TO_LAST_TARGET = 0
ABSOLUTE = 1
RELATIVE_TO_CURRENT = 2
class HomeMode(IntEnum):
"""手册定义的回零模式。"""
NEAREST = 0
DIRECTIONAL = 1
COLLISION = 2
LIMIT_SWITCH = 3
ABSOLUTE_ZERO = 4
POWER_LOSS_POSITION = 5
@dataclass(frozen=True)
class MotorConfig:
"""一台电机的地址、固件、细分、步距角和超时设置。"""
model: str = "X57S"
firmware: Firmware = Firmware.EMM
motor_id: int = 1
checksum: ChecksumType = ChecksumType.FIXED_6B
microstep: int = 16
step_angle_degrees: float = 1.8
timeout_s: float = 0.5
def __post_init__(self):
"""
@description : 规范化并校验单电机配置
@param : 无参数
@return : 无返回值
"""
object.__setattr__(self, "model", str(self.model).upper())
object.__setattr__(self, "firmware", parse_firmware(self.firmware))
object.__setattr__(self, "checksum", parse_checksum_type(self.checksum))
validate_motor_id(self.motor_id)
validate_int("microstep", self.microstep, 1, 256)
if self.step_angle_degrees not in (0.9, 1.8):
raise ZDTConfigurationError("step_angle_degrees must be 0.9 or 1.8")
if self.timeout_s <= 0:
raise ZDTConfigurationError("timeout_s must be greater than zero")
def parse_firmware(value):
"""
@description : 把字符串或枚举转换为 Firmware
@param value : emm、x 或 Firmware 枚举
@return : Firmware 枚举
"""
try:
return value if isinstance(value, Firmware) else Firmware(str(value).lower())
except ValueError as error:
raise ZDTConfigurationError("firmware must be 'emm' or 'x'") from error
def parse_checksum_type(value):
"""
@description : 把字符串或枚举转换为 ChecksumType
@param value : fixed_6b、xor、crc8 或对应枚举
@return : ChecksumType 枚举
"""
try:
if isinstance(value, ChecksumType):
return value
normalized = str(value).lower().replace("-", "_")
if normalized in ("6b", "fixed", "fixed6b"):
normalized = "fixed_6b"
return ChecksumType(normalized)
except ValueError as error:
raise ZDTConfigurationError(
"checksum must be fixed_6b, xor, or crc8"
) from error
def parse_direction(value):
"""
@description : 把方向字符串、数字或枚举转换为 Direction
@param value : cw、ccw、0、1 或 Direction
@return : Direction 枚举
"""
if isinstance(value, Direction):
return value
if isinstance(value, str):
normalized = value.strip().lower()
if normalized == "cw":
return Direction.CW
if normalized == "ccw":
return Direction.CCW
try:
return Direction(value)
except (TypeError, ValueError) as error:
raise ZDTConfigurationError("direction must be 'cw' or 'ccw'") from error
def validate_motor_id(motor_id, *, allow_broadcast=False):
"""
@description : 校验 ZDT 电机地址
@param motor_id : 待校验地址
@param allow_broadcast: 是否允许广播地址0
@return : 合法整数地址
"""
if isinstance(motor_id, bool) or not isinstance(motor_id, int):
raise ZDTConfigurationError("motor_id must be an integer")
minimum = 0 if allow_broadcast else 1
if motor_id < minimum or motor_id > 255:
raise ZDTConfigurationError(
f"motor_id must be in range {minimum}-255"
)
return motor_id
def validate_int(name, value, minimum, maximum):
"""
@description : 校验不接受布尔值的整数范围
@param name : 参数名称
@param value : 参数值
@param minimum : 最小允许值
@param maximum : 最大允许值
@return : 合法整数
"""
if isinstance(value, bool) or not isinstance(value, int):
raise ZDTConfigurationError(f"{name} must be an integer")
if value < minimum or value > maximum:
raise ZDTConfigurationError(
f"{name} must be in range {minimum}-{maximum}"
)
return value
def validate_number(name, value, minimum, maximum):
"""
@description : 校验工程单位数值范围
@param name : 参数名称
@param value : 参数值
@param minimum : 最小允许值
@param maximum : 最大允许值
@return : 合法浮点数
"""
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise ZDTConfigurationError(f"{name} must be a number")
normalized = float(value)
if normalized < minimum or normalized > maximum:
raise ZDTConfigurationError(
f"{name} must be in range {minimum}-{maximum}"
)
return normalized