Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
*.pyc
build/
myactuator_rmd_py.egg-info/

dist/
47 changes: 45 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.20)
project(myactuator_rmd VERSION 0.0.1)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

option(PYTHON_BINDINGS "Building Python bindings" OFF)
option(BUILD_TESTING "Build unit and integration tests" OFF)
option(SETUP_TEST_IFNAME "Set-up the test VCAN interface automatically" OFF)
Expand Down Expand Up @@ -39,7 +42,26 @@ target_include_directories(myactuator_rmd PUBLIC
$<INSTALL_INTERFACE:include>
)
set(MYACTUATOR_RMD_LIBRARIES "")
target_link_libraries(myactuator_rmd PUBLIC

# --- Check for CAN frame member name ---
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("
#include <linux/can.h>
int main() {
struct can_frame frame;
frame.can_dlc = 0;
return 0;
}" HAVE_CAN_DLC)

if(HAVE_CAN_DLC)
message(STATUS "Detected can_frame.can_dlc member - using this field name.")
target_compile_definitions(myactuator_rmd PUBLIC HAVE_CAN_DLC)
else()
message(STATUS "Using can_frame.len member for newer Linux CAN versions.")
endif()
# --------------------------------------

target_link_libraries(myactuator_rmd PUBLIC
${MYACTUATOR_RMD_LIBRARIES}
)
install(DIRECTORY include/
Expand All @@ -57,14 +79,35 @@ if(PYTHON_BINDINGS)
Development
Interpreter
)
find_package(pybind11 CONFIG REQUIRED)

# --- Dynamically find pybind11 CMake directory ---
execute_process(
COMMAND "${Python3_EXECUTABLE}" -m pybind11 --cmakedir
OUTPUT_VARIABLE PYBIND11_CMAKE_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE PYBIND11_RESULT
ERROR_QUIET
)
if(PYBIND11_RESULT EQUAL 0 AND IS_DIRECTORY "${PYBIND11_CMAKE_DIR}")
set(pybind11_DIR "${PYBIND11_CMAKE_DIR}")
message(STATUS "Found pybind11 CMake dir via Python: ${pybind11_DIR}")
else()
message(WARNING "Could not find pybind11 CMake dir via 'python3 -m pybind11 --cmakedir'. Falling back to standard find_package.")
endif()
# -------------------------------------------------

find_package(pybind11 REQUIRED)

pybind11_add_module(myactuator_rmd_py
bindings/myactuator_rmd.cpp
)
target_compile_features(myactuator_rmd_py PUBLIC
cxx_std_17
)
# Add HAVE_CAN_DLC definition if needed by bindings (unlikely, but for completeness)
if(HAVE_CAN_DLC)
target_compile_definitions(myactuator_rmd_py PUBLIC HAVE_CAN_DLC)
endif()
target_link_libraries(myactuator_rmd_py PUBLIC
myactuator_rmd
)
Expand Down
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include myactuator_rmd_py.pyi
275 changes: 275 additions & 0 deletions myactuator_rmd_py.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
"""Python type stubs for the myactuator_rmd_py module.
Generated based on the pybind11 bindings.
"""

import enum
from datetime import timedelta
from typing import Any, Type, Final, List, TypeVar, ClassVar

# === Base Exceptions ===

class ActuatorException(Exception): ...

class ProtocolException(ActuatorException): ...

class ValueRangeException(ActuatorException): ...

# === Base Driver Classes ===

class Driver:
# Base class, structure inferred from usage
...

class CanDriver(Driver):
def __init__(self, can_interface_name: str) -> None: ...

# === Actuator State Types ===

class PiGains:
kp: int
ki: int
def __init__(self, kp: int, ki: int) -> None: ...

class Gains:
current: PiGains
speed: PiGains
position: PiGains
def __init__(self, current: PiGains, speed: PiGains, position: PiGains) -> None: ...
# Overload for direct int initialization
# def __init__(self, current_kp: int, current_ki: int, speed_kp: int, speed_ki: int, position_kp: int, position_ki: int) -> None: ...

class AccelerationType(enum.Enum):
POSITION_PLANNING_ACCELERATION: ClassVar[AccelerationType]
POSITION_PLANNING_DECELERATION: ClassVar[AccelerationType]
VELOCITY_PLANNING_ACCELERATION: ClassVar[AccelerationType]
VELOCITY_PLANNING_DECELERATION: ClassVar[AccelerationType]

class CanBaudRate(enum.Enum):
KBPS500: ClassVar[CanBaudRate]
MBPS1: ClassVar[CanBaudRate]

class ControlMode(enum.Enum):
NONE: ClassVar[ControlMode]
CURRENT: ClassVar[ControlMode]
VELOCITY: ClassVar[ControlMode]
POSITION: ClassVar[ControlMode]

class ErrorCode(enum.Enum):
NO_ERROR: ClassVar[ErrorCode]
MOTOR_STALL: ClassVar[ErrorCode]
LOW_VOLTAGE: ClassVar[ErrorCode]
OVERVOLTAGE: ClassVar[ErrorCode]
OVERCURRENT: ClassVar[ErrorCode]
POWER_OVERRUN: ClassVar[ErrorCode]
SPEEDING: ClassVar[ErrorCode]
UNSPECIFIED_1: ClassVar[ErrorCode]
UNSPECIFIED_2: ClassVar[ErrorCode]
UNSPECIFIED_3: ClassVar[ErrorCode]
OVERTEMPERATURE: ClassVar[ErrorCode]
ENCODER_CALIBRATION_ERROR: ClassVar[ErrorCode]

class MotorStatus1:
temperature: Final[int]
is_brake_released: Final[bool]
voltage: Final[float]
error_code: Final[ErrorCode]
def __init__(self, temperature: int, is_brake_released: bool, voltage: float, error_code: ErrorCode) -> None: ...

class MotorStatus2:
temperature: Final[int]
current: Final[float]
shaft_speed: Final[float]
shaft_angle: Final[float]
def __init__(self, temperature: int, current: float, shaft_speed: float, shaft_angle: float) -> None: ...

class MotorStatus3:
temperature: Final[int]
current_phase_a: Final[float]
current_phase_b: Final[float]
current_phase_c: Final[float]
def __init__(self, temperature: int, current_phase_a: float, current_phase_b: float, current_phase_c: float) -> None: ...

# Feedback type returned by send* methods, structure mirrors MotorStatus2
class Feedback:
temperature: Final[int]
current: Final[float]
shaft_speed: Final[float]
shaft_angle: Final[float]
# Actual __init__ might differ or not be exposed directly
def __init__(self, temperature: int, current: float, shaft_speed: float, shaft_angle: float) -> None: ...

# === CAN Types ===

class Frame:
# Assuming std::array<std::uint8_t, 8> maps to list[int]
def __init__(self, id: int, data: List[int]) -> None: ...
def getId(self) -> int: ...
def getData(self) -> List[int]: ...

class Node:
def __init__(self, interface_name: str) -> None: ...
# setRecvFilter takes struct can_filter*, difficult to type precisely
def setRecvFilter(self, filter: Any) -> None: ...
def read(self) -> Frame: ...
def write(self, frame: Frame) -> None: ...

# CAN Exceptions
class SocketException(Exception): ...

class CanException(SocketException): ...

class TxTimeoutError(CanException): ...

class LostArbitrationError(CanException): ...

class ControllerProblemError(CanException): ...

class ProtocolViolationError(CanException): ...

class TransceiverStatusError(CanException): ...

class NoAcknowledgeError(CanException): ...

class BusOffError(CanException): ...

class BusError(CanException): ...

class ControllerRestartedError(CanException): ...

# === Actuator Interface ===

class ActuatorInterface:
def __init__(self, driver: Driver, actuator_id: int) -> None: ...

def getAcceleration(self) -> int: ...
def getCanId(self) -> int: ...
def getControllerGains(self) -> Gains: ...
def getControlMode(self) -> ControlMode: ...
def getMotorModel(self) -> str: ...
def getMotorPower(self) -> float: ...
def getMotorStatus1(self) -> MotorStatus1: ...
def getMotorStatus2(self) -> MotorStatus2: ...
def getMotorStatus3(self) -> MotorStatus3: ...
def getMultiTurnAngle(self) -> float: ...
def getMultiTurnEncoderPosition(self) -> int: ...
def getMultiTurnEncoderOriginalPosition(self) -> int: ...
def getMultiTurnEncoderZeroOffset(self) -> int: ...
def getRuntime(self) -> timedelta: ...
def getSingleTurnAngle(self) -> float: ...
def getSingleTurnEncoderPosition(self) -> int: ...
def getVersionDate(self) -> int: ...

def lockBrake(self) -> None: ...
def releaseBrake(self) -> None: ...
def reset(self) -> None: ...

def sendCurrentSetpoint(self, current: float) -> Feedback: ...
def sendPositionAbsoluteSetpoint(self, position: float, max_speed: float = 500.0) -> Feedback: ...
def sendTorqueSetpoint(self, torque: float, torque_constant: float) -> Feedback: ...
def sendVelocitySetpoint(self, speed: float) -> Feedback: ...

def setAcceleration(self, acceleration: int, mode: AccelerationType) -> None: ...
def setCanBaudRate(self, baud_rate: CanBaudRate) -> None: ...
def setCanId(self, can_id: int) -> None: ...
def setControllerGains(self, gains: Gains, is_persistent: bool = False) -> Gains: ...
def setCurrentPositionAsEncoderZero(self) -> int: ...
def setEncoderZero(self, encoder_offset: int) -> None: ...
def setTimeout(self, timeout: timedelta) -> None: ...
def shutdownMotor(self) -> None: ...
def stopMotor(self) -> None: ...

# === Actuator Constants ===

# Define a base class for actuator constants structure
class ActuatorConstantsBase:
reducer_ratio: Final[float]
rated_speed: Final[float]
rated_current: Final[float]
rated_power: Final[float]
rated_torque: Final[float]
torque_constant: Final[float]
rotor_inertia: Final[float]

class X4V2(ActuatorConstantsBase): ...
class X4V3(ActuatorConstantsBase): ...
class X4_3(ActuatorConstantsBase): ...
class X4_24(ActuatorConstantsBase): ...
class X6V2(ActuatorConstantsBase): ...
class X6S2V2(ActuatorConstantsBase): ...
class X6V3(ActuatorConstantsBase): ...
class X6_7(ActuatorConstantsBase): ...
class X6_8(ActuatorConstantsBase): ...
class X6_40(ActuatorConstantsBase): ...
class X8V2(ActuatorConstantsBase): ...
class X8ProV2(ActuatorConstantsBase): ...
class X8S2V3(ActuatorConstantsBase): ...
class X8HV3(ActuatorConstantsBase): ...
class X8ProHV3(ActuatorConstantsBase): ...
class X8_20(ActuatorConstantsBase): ...
class X8_25(ActuatorConstantsBase): ...
class X8_60(ActuatorConstantsBase): ...
class X8_90(ActuatorConstantsBase): ...
class X10V3(ActuatorConstantsBase): ...
class X10S2V3(ActuatorConstantsBase): ...
class X10_40(ActuatorConstantsBase): ...
class X10_100(ActuatorConstantsBase): ...
class X12_150(ActuatorConstantsBase): ...
class X15_400(ActuatorConstantsBase): ...


# === Submodule Definitions ===

# Define modules that will be exposed at the top-level
class actuator_state:
AccelerationType: Type[AccelerationType]
CanBaudRate: Type[CanBaudRate]
ControlMode: Type[ControlMode]
ErrorCode: Type[ErrorCode]
PiGains: Type[PiGains]
Gains: Type[Gains]
MotorStatus1: Type[MotorStatus1]
MotorStatus2: Type[MotorStatus2]
MotorStatus3: Type[MotorStatus3]
Feedback: Type[Feedback]

class can:
Frame: Type[Frame]
Node: Type[Node]
SocketException: Type[SocketException]
CanException: Type[CanException]
TxTimeoutError: Type[TxTimeoutError]
LostArbitrationError: Type[LostArbitrationError]
ControllerProblemError: Type[ControllerProblemError]
ProtocolViolationError: Type[ProtocolViolationError]
TransceiverStatusError: Type[TransceiverStatusError]
NoAcknowledgeError: Type[NoAcknowledgeError]
BusOffError: Type[BusOffError]
BusError: Type[BusError]
ControllerRestartedError: Type[ControllerRestartedError]

class actuator_constants:
X4V2: Type[X4V2]
X4V3: Type[X4V3]
X4_3: Type[X4_3]
X4_24: Type[X4_24]
X6V2: Type[X6V2]
X6S2V2: Type[X6S2V2]
X6V3: Type[X6V3]
X6_7: Type[X6_7]
X6_8: Type[X6_8]
X6_40: Type[X6_40]
X8V2: Type[X8V2]
X8ProV2: Type[X8ProV2]
X8S2V3: Type[X8S2V3]
X8HV3: Type[X8HV3]
X8ProHV3: Type[X8ProHV3]
X8_20: Type[X8_20]
X8_25: Type[X8_25]
X8_60: Type[X8_60]
X8_90: Type[X8_90]
X10V3: Type[X10V3]
X10S2V3: Type[X10S2V3]
X10_40: Type[X10_40]
X10_100: Type[X10_100]
X12_150: Type[X12_150]
X15_400: Type[X15_400]
Loading