Skip to content
Merged
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
32 changes: 32 additions & 0 deletions dobotWrapperPy/dobotapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
tagCPParams,
tagDevice,
tagEMOTOR,
tagEMOTORDistance,
tagEndEffectorParams,
tagIODO,
tagIOMultiplexing,
Expand Down Expand Up @@ -2206,6 +2207,37 @@ def set_e_motor(
return None
return None

def set_e_motor_distance(
self, params: tagEMOTORDistance, wait: bool = False, is_queued: bool = False
) -> Optional[int]:
"""
Controls an external motor (stepper).
Protocol ID: 135. Can be queued.

Args:
params: External motor parameters.
wait: If True and command is queued, waits for execution.
is_queued: If True, command is added to the queue.

Returns:
Queued command index if is_queued is True, else None.
"""
response = self._send_command_with_params(
CommunicationProtocolIDs.EMOTOR, # ID 135
ControlValues.ReadWrite,
params.pack(), # EMotor
wait,
put_on_queue=is_queued,
)
if is_queued:
if response.params and len(response.params) >= 8:
return int(struct.unpack("<Q", response.params)[0]) #
warnings.warn(
f"SET_EMOTOR queued but no valid index returned. Params: {response.params.hex()}"
)
return None
return None

def set_color_sensor(
self, params: tagDevice, wait: bool = False, is_queued: bool = False
) -> Optional[int]:
Expand Down
32 changes: 31 additions & 1 deletion dobotWrapperPy/dobotasync.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .dobotapi import DobotApi
import math
from .dobotConnection import DobotConnection
import warnings
import struct
Expand All @@ -17,6 +18,7 @@
from .paramsStructures import (
tagIOMultiplexing,
tagWithL,
tagEMOTORDistance,
tagDevice,
tagPTPCommonParams,
tagPTPCoordinateParams,
Expand Down Expand Up @@ -85,7 +87,9 @@ def __del__(self) -> None:
if hasattr(self, "dobotApiInterface") and self.dobotApiInterface is not None:
del self.dobotApiInterface

def _run_in_loop(self, func: Callable[..., _T], *args: typing.Any) -> typing.Awaitable[_T]:
def _run_in_loop(
self, func: Callable[..., _T], *args: typing.Any
) -> typing.Awaitable[_T]:
if self._loop is None:
raise Exception("Dobot not connected")
return self._loop.run_in_executor(None, func, *args)
Expand Down Expand Up @@ -488,6 +492,32 @@ async def set_motor(self, address: EMotorIndex, enable: bool, speed: int) -> Non
True,
)

async def set_motor_distance(
self, address: EMotorIndex, enable: bool, speed: int, distance: int
) -> None:
await self._run_in_loop(
self.dobotApiInterface.set_e_motor_distance,
tagEMOTORDistance(address, enable, speed, distance),
True,
True,
)

async def move_conveyor_belt(
self, speed: int, distance_cm: int, address: EMotorIndex, direction: int = 1
) -> None:

STEP_PER_CIRCLE = 360.0 / 1.8 * 10.0 * 16.0
MM_PER_CIRCLE = 3.1415926535898 * 36.0
if 0.0 <= speed <= 100.0 and (direction == 1 or direction == -1):
motor_speed = math.floor(
speed * STEP_PER_CIRCLE / MM_PER_CIRCLE * direction
)
await self.set_motor_distance(address, True, motor_speed, distance_cm)
else:
raise Exception(
f"Wrong speed or direction. Current params: Speed: {speed}, Distance: {distance_cm} cm, Direction: {direction}, Address: {address.value}"
)

async def set_color_sensor(
self, enable: bool, port: int, version: TagVersionColorSensorAndIR
) -> None:
Expand Down
80 changes: 80 additions & 0 deletions dobotWrapperPy/paramsStructures.py
Original file line number Diff line number Diff line change
Expand Up @@ -2251,6 +2251,86 @@ def unpack(cls, data: bytes) -> "tagEMOTOR":
)


@dataclass
class tagEMOTORDistance:
"""Represents an EMOTOR distance command."""

# uint8 - Represents EMotorIndex
address: EMotorIndex
# uint8 - Boolean flag for instruction enabled
insEnabled: bool
# double - Speed of the motor
speed: int
# int - Distance travelled
distance: int

def pack(self) -> bytes:
"""
Packs the tagEMOTOR object into a bytes sequence.

Packs the index (EMotorIndex value as uint8), insEnabled (boolean as uint8),
and speed (double) into a byte string. Uses little-endian byte order.

Returns:
A bytes object representing the packed data (1 + 1 + 8 = 10 bytes).
"""
# Format: < (little-endian), B (uint8), B (uint8), d (double)
# Total size = 1 + 1 + 8 = 10 bytes
format_string = "<BBiI"
return struct.pack(
format_string,
self.address.value, # Pack the enum value
1 if self.insEnabled else 0, # Pack boolean as 1 or 0
self.speed,
)

@classmethod
def unpack(cls, data: bytes) -> "tagEMOTORDistance":
"""
Unpacks a bytes sequence into a tagEMOTOR object.

Args:
data: The bytes to unpack, expected to be 10 bytes (2 uint8s + 1 double).

Returns:
A tagEMOTOR object.

Raises:
struct.error: If the input bytes are not the expected size (10 bytes).
ValueError: If the unpacked byte value for index does not correspond to a valid EMotorIndex enum member.
"""
format_string = "<BBiI"
expected_size = struct.calcsize(format_string)

if len(data) != expected_size:
raise struct.error(f"Expected {expected_size} bytes, but got {len(data)}")

(
unpacked_byte_index,
unpacked_byte_ins_enabled,
unpacked_double_speed,
unpacked_distance,
) = struct.unpack(format_string, data)

try:
address = EMotorIndex(unpacked_byte_index)
except ValueError:
raise ValueError(
f"Invalid EMotorIndex value encountered: {unpacked_byte_index}"
)

ins_enabled = unpacked_byte_ins_enabled == 1
speed = unpacked_double_speed
distance = unpacked_distance

return cls(
address=address,
insEnabled=ins_enabled,
speed=speed,
distance=distance,
)


@dataclass
class tagDevice:
"""Represents a generic device tag."""
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
setuptools.setup(
name="dobotWrapperPy",
packages=["dobotWrapperPy", "dobotWrapperPy.enums"],
package_data={"dobotWrapperPy": ["py.typed", "*.pyi", "types/*.pyi", "*.pyi", "types/dobotWrapperPy/*.pyi", "types/dobotWrapperPy/enums/*.pyi"]},
package_data={"dobotWrapperPy": ["py.typed", "*.pyi", "types/*.pyi"], "dobotWrapperPy.enums": ["py.typed", "*.pyi"]},
include_package_data=True,
version="1.2.1",
description="Python library for Dobot Magician For Minitechnicus Courses",
Expand Down