diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ded34fb60f493e689b2ad5d020607795c2a920ac..74fe994651a31a7aa9a0962c22ea6300a295fca9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -48,3 +48,11 @@ run-pre-commit: python3.9: <<: *test image: python:3.9 + +python3.10: + <<: *test + image: python:3.10 + +python3.11: + <<: *test + image: python:3.11 diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/log/.gitkeep b/log/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/poetry.lock b/poetry.lock index bda0ede59e0968a8505efe4f830526990656c863..80a9a111e665b8a0e3bbb0b289ea9d358db4523d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -64,11 +64,11 @@ python-versions = ">=3.6.1" [[package]] name = "click" -version = "8.0.4" +version = "8.1.2" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -219,11 +219,11 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" -version = "2.17.0" +version = "2.18.1" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.7" [package.dependencies] cfgv = ">=2.0.0" @@ -342,7 +342,7 @@ python-versions = ">=3.6" [[package]] name = "virtualenv" -version = "20.13.4" +version = "20.14.0" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -360,8 +360,8 @@ testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", [metadata] lock-version = "1.1" -python-versions = ">=3.9,<3.11" -content-hash = "ab1f88cbf02688daeb723c77086aedf337fd9c15eac50157c4b0481537be2098" +python-versions = ">=3.9,<3.12" +content-hash = "abe8e0f79c0ea225ee0b4414ce6da0afa7360a1c61bf1050502ad5dbb084ef09" [metadata.files] "aspy.refactor-imports" = [ @@ -385,8 +385,8 @@ cfgv = [ {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] click = [ - {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, - {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, + {file = "click-8.1.2-py3-none-any.whl", hash = "sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e"}, + {file = "click-8.1.2.tar.gz", hash = "sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, @@ -463,8 +463,8 @@ pluggy = [ {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] pre-commit = [ - {file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"}, - {file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"}, + {file = "pre_commit-2.18.1-py2.py3-none-any.whl", hash = "sha256:02226e69564ebca1a070bd1f046af866aa1c318dbc430027c50ab832ed2b73f2"}, + {file = "pre_commit-2.18.1.tar.gz", hash = "sha256:5d445ee1fa8738d506881c5d84f83c62bb5be6b2838e32207433647e8e5ebe10"}, ] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, @@ -542,6 +542,6 @@ typing-extensions = [ {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, ] virtualenv = [ - {file = "virtualenv-20.13.4-py2.py3-none-any.whl", hash = "sha256:c3e01300fb8495bc00ed70741f5271fc95fed067eb7106297be73d30879af60c"}, - {file = "virtualenv-20.13.4.tar.gz", hash = "sha256:ce8901d3bbf3b90393498187f2d56797a8a452fb2d0d7efc6fd837554d6f679c"}, + {file = "virtualenv-20.14.0-py2.py3-none-any.whl", hash = "sha256:1e8588f35e8b42c6ec6841a13c5e88239de1e6e4e4cedfd3916b306dc826ec66"}, + {file = "virtualenv-20.14.0.tar.gz", hash = "sha256:8e5b402037287126e81ccde9432b95a8be5b19d36584f64957060a3488c11ca8"}, ] diff --git a/pyproject.toml b/pyproject.toml index 5a9b7d15dbbd7130870edfd8016a3fe9df06df0c..18858ae9f3af4dc5edb4de010a48d76db0ba343e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,19 +3,14 @@ requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" [tool.poetry] -name = "py_ur_ctl" +name = "ur_py_ctl" version = "0.1.0" -description = "package for script execution on UR5 for selectica project" -authors = [ - "Tihomir Zilic <tihomir.zilic@control.lth.se>", - "Anton Tetov <anton.johansson@control.lth.se>", - "Jacek Malek <jacek.malec@cs.lth.se>", - "Anders Robertsson <anders.robertsson@control.lth.se>", -] -repository = "https://gitlab.control.lth.se/selectica/py_ur_ctl" +description = "Python wrapper around URScript (for Universal Robotics robots)." +authors = ["Anton Tetov <anton.johansson@control.lth.se>"] +repository = "https://gitlab.control.lth.se/robotlab/ur_py_ctl" [tool.poetry.dependencies] -python = ">=3.9,<3.11" +python = ">=3.9,<3.12" [tool.poetry.dev-dependencies] flake8 = "^4.0.1" diff --git a/ur_py_ctl/__init__.py b/ur_py_ctl/__init__.py index 6c89e01d155f90a8c0580981969b868434168c47..1ca033b8975320864b6367e3ae5d15be96db1a6e 100644 --- a/ur_py_ctl/__init__.py +++ b/ur_py_ctl/__init__.py @@ -4,4 +4,17 @@ __version__ = "0.1.0" HERE = pathlib.Path(__file__).parent REPO_DIR = HERE.parent +DATA_DIR = REPO_DIR / "data" LOG_DIR = REPO_DIR / "log" + +from .urscript_commands import Motion # noqa: F401,E402 +from .urscript_commands import move_to_pose # noqa: F401,E402 +from .urscript_commands import move_to_conf # noqa: F401,E402 +from .urscript_commands import set_tcp # noqa: F401,E402 +from .urscript_commands import set_DO # noqa: F401,E402 +from .urscript_commands import read_DO # noqa: F401,E402 +from .urscript_commands import set_AO # noqa: F401,E402 +from .urscript_commands import read_AO # noqa: F401,E402 +from .urscript_commands import sleep # noqa: F401,E402 +from .urscript_commands import text_msg # noqa: F401,E402 +from .urscript_commands import popup # noqa: F401,E402 diff --git a/ur_py_ctl/urscript_commands.py b/ur_py_ctl/urscript_commands.py index e4b6bb9c0c8db79d46a7de6fe9c4dbebee1d33e9..8801c661f72bf6cc3351eebccd1e4a2968646c56 100644 --- a/ur_py_ctl/urscript_commands.py +++ b/ur_py_ctl/urscript_commands.py @@ -1,3 +1,4 @@ +from collections.abc import Sequence from functools import wraps @@ -9,11 +10,6 @@ def _add_whitespace(func): return wrapper -class Motion(object): - LINEAR = 0 - JOINT = 1 - - def _get_func(func_name, urscript_args, urscript_kwargs=None): args = urscript_args if urscript_kwargs: @@ -26,7 +22,12 @@ def _get_pose(x: float, y: float, z: float, ax: float, ay: float, az: float) -> return f"p[{x:.5f}, {y:.5f}, {z:.5f}, {ax:.5f}, {ay:.5f}, {az:.5f}]" -def _get_conf(j1: float, j2: float, j3: float, j4: float, j5: float, j6: float): +def _get_conf(joint_conf: Sequence[float]) -> str: + if len(joint_conf) != 6: + raise RuntimeError("Wrong amount of joint positions, should be 6.") + + j1, j2, j3, j4, j5, j6 = joint_conf + return f"[{j1:.5f}, {j2:.5f}, {j3:.5f}, {j4:.5f}, {j5:.5f}, {j6:.5f}]" @@ -37,7 +38,8 @@ def _get_move_kwargs(**kwargs) -> str: for key in kwargs: if key in ok_keywords: - urscript_kwargs.append(f"{key}={kwargs[key]:.5f]}") + if kwargs[key]: + urscript_kwargs.append(f"{key}={kwargs[key]:.5f]}") else: raise RuntimeError(f"Keyword name {key} not recognized.") @@ -47,8 +49,35 @@ def _get_move_kwargs(**kwargs) -> str: return " ,".join(kwargs) +class Motion(object): + LINEAR = 0 + JOINT = 1 + + @_add_whitespace def set_tcp(x: float, y: float, z: float, rx: float, ry: float, rz: float) -> str: + """Create a set TCP function call + + Arguments + --------- + x + X coordinate (m) + y + Y coordinate (m) + z + Z coordinate (m) + rx + X component of axis angle vector + ry + X component of axis angle vector + rz + X component of axis angle vector + + Returns + ------- + str + URScript function call + """ return _get_func("set_tcp", _get_pose(x, y, z, rx, ry, rz)) @@ -66,6 +95,39 @@ def move_to_pose( t: float = None, r: float = None, ) -> str: + """Create a move to pose function call + + Arguments + --------- + x + X coordinate (m) + y + Y coordinate (m) + z + Z coordinate (m) + rx + X component of axis angle vector + ry + X component of axis angle vector + rz + X component of axis angle vector + mode + Motion.LINEAR or Motion.JOINT. Defaults to Motion.JOINT + a + Tool acceleration (m/s²) + v + tool speed (m/s) + t + time (s) + r + blend radius (m) + + Returns + ------- + str + URScript function call + + """ func_name = "movel" if mode == Motion.LINEAR else "movej" pose = _get_pose(x, y, z, rx, ry, rz) @@ -76,48 +138,108 @@ def move_to_pose( @_add_whitespace def move_to_conf( - j1: float, - j2: float, - j3: float, - j4: float, - j5: float, - j6: float, + joint_conf: Sequence[float], v: float = None, a: float = None, t: float = None, r: float = None, ) -> str: - conf = _get_conf(j1, j2, j3, j4, j5, j6) + """Create a move to configuration function call + + Arguments + --------- + joint_conf + A list of joint positions (rad) + a + Joint acceleration of leading axis (rad/s²) + v + Joint speed of leading axis (rad/s) + t + time (s) + r + blend radius (m) + + Returns + ------- + str + URScript function call + """ + conf = _get_conf(joint_conf) move_kwargs = _get_move_kwargs(v=v, a=a, t=t, r=r) return _get_func("movej", conf, move_kwargs) @_add_whitespace -def textmsg(string: str) -> str: +def text_msg(string: str) -> str: + """Create a log function call + + Arguments + --------- + string + Message to print in log + + Returns + ------- + str + URScript function call + """ return f'textmsg("{string}")' +@_add_whitespace +def popup(string: str) -> str: + """Construct a function call that opens a popup on the teach pendant. + + Arguments + --------- + string + Message to print + + Notes + ----- + Popup arguments title, error & warning is not implemented in URScript. + """ + return f'popup("{string}")' + + @_add_whitespace def set_DO(pin: int, state: bool) -> str: - """Construct a function call that sets the state of a digital out.""" + """Construct a function call that sets the state of a digital out. + + Arguments + --------- + pin + Pin number + state + True or False + + Returns + ------- + str + URScript function call + """ # bool(state) should convert numeric representation to boolean value and # then into True or False as string. return _get_func("set_digital_out", f"{pin:d}, {bool(state)}") @_add_whitespace -def sleep(seconds: int) -> str: - """Construct a function call that instructs the controller to sleep.""" - return _get_func("sleep", str(seconds)) +def read_DO(pin: int) -> bool: + raise NotImplementedError() @_add_whitespace -def popup(string: str) -> str: - """Construct a function call that opens a popup on the teach pendant. +def set_AO(pin: int, value: float) -> str: + raise NotImplementedError() - Notes - ----- - Popup arguments title, error & warning is not implemented in URScript. - """ - return f'popup("{string}")' + +@_add_whitespace +def read_AO(pin: int) -> float: + raise NotImplementedError() + + +@_add_whitespace +def sleep(seconds: int) -> str: + """Construct a function call that instructs the controller to sleep.""" + return _get_func("sleep", str(seconds))