#!/usr/bin/env python# -*- coding: utf-8 -*-## @Author: José Sánchez-Gallego (gallegoj@uw.edu)# @Date: 2018-08-27# @Filename: __init__.py# @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)from__future__importannotationsimportenumfromtypingimportDict,Type,UnionclassTypesEnumMeta(enum.EnumMeta):"""Metaclass to allow initialising an Enum from a string."""def__call__(cls:Type[enum.Enum],value:Union[str,int]):ifisinstance(value,str):forflagincls:ifflag.name.lower()==value.lower():returnenum.EnumMeta.__call__(cls,flag.value)raiseValueError(f"Invalid {cls.__name__} value: {value}")returnenum.EnumMeta.__call__(cls,value)
[docs]classCommandID(enum.IntEnum,metaclass=TypesEnumMeta):"""IDs associated with commands."""GET_ID=1GET_FIRMWARE_VERSION=2GET_STATUS=3SEND_NEW_TRAJECTORY=10SEND_TRAJECTORY_DATA=11TRAJECTORY_DATA_END=12SEND_TRAJECTORY_ABORT=13START_TRAJECTORY=14STOP_TRAJECTORY=15COLLISION_DETECTED=18GO_TO_DATUMS=20GO_TO_DATUM_ALPHA=21GO_TO_DATUM_BETA=22START_DATUM_CALIBRATION=23START_DATUM_CALIBRATION_ALPHA=24START_DATUM_CALIBRATION_BETA=25START_MOTOR_CALIBRATION=26START_MOTOR_CALIBRATION_ALPHA=26START_MOTOR_CALIBRATION_BETA=27GO_TO_ABSOLUTE_POSITION=30GO_TO_RELATIVE_POSITION=31GET_ACTUAL_POSITION=32SET_ACTUAL_POSITION=33GET_OFFSETS=34SET_OFFSETS=35SET_SPEED=40SET_CURRENT=41GET_HALL_CALIB_ERROR=45START_COGGING_CALIBRATION=47START_COGGING_CALIBRATION_ALPHA=48START_COGGING_CALIBRATION_BETA=49SAVE_INTERNAL_CALIBRATION=53GET_CURRENT=56GET_ALPHA_HALL_CALIB=104GET_BETA_HALL_CALIB=105SET_INCREASE_COLLISION_MARGIN=111SET_HOLDING_CURRENT=112GET_HOLDING_CURRENT=113HALL_ON=116HALL_OFF=117ALPHA_CLOSED_LOOP_COLLISION_DETECTION=118ALPHA_CLOSED_LOOP_WITHOUT_COLLISION_DETECTION=119ALPHA_OPEN_LOOP_COLLISION_DETECTION=120ALPHA_OPEN_LOOP_WITHOUT_COLLISION_DETECTION=121BETA_CLOSED_LOOP_COLLISION_DETECTION=122BETA_CLOSED_LOOP_WITHOUT_COLLISION_DETECTION=123BETA_OPEN_LOOP_COLLISION_DETECTION=124BETA_OPEN_LOOP_WITHOUT_COLLISION_DETECTION=125SWITCH_LED_ON=126SWITCH_LED_OFF=127SWITCH_ON_PRECISE_MOVE_ALPHA=128SWITCH_OFF_PRECISE_MOVE_ALPHA=129SWITCH_ON_PRECISE_MOVE_BETA=130SWITCH_OFF_PRECISE_MOVE_BETA=131GET_RAW_TEMPERATURE=132GET_NUMBER_TRAJECTORIES=139SET_NUMBER_TRAJECTORIES=140START_FIRMWARE_UPGRADE=200SEND_FIRMWARE_DATA=201
[docs]defget_command_class(self)->Type[Command]:"""Returns the class associated with this command."""ifselfinCOMMAND_LIST:returnCOMMAND_LIST[self]raiseValueError("The command does not have an associated class.")
from.baseimport*from.baseimportCommandfrom.bootloaderimport*from.calibrationimport*from.gotoimport*from.statusimport*from.trajectoryimport*defall_subclasses(cls):"""Recursive subclasses."""returnset(cls.__subclasses__()).union([sforcincls.__subclasses__()forsinall_subclasses(c)])# Generate a dictionary of commandsCOMMAND_LIST:Dict[int,Type[Command]]={cclass.command_id:cclassforcclassinall_subclasses(Command)ifcclass.command_idinCommandID}# Dynamically generate command classes for those commands for which we# didn't write a class. These classes are identical to a generic Command# but with a custom name.forcidinlist(CommandID):ifcidnotinCOMMAND_LIST:CommandClass=type(cid.name.title().replace("_",""),(Command,),{"command_id":cid,"broadcastable":False},)COMMAND_LIST[cid]=CommandClass