From bc4409a21c3c4eb8b28369ff6ade351b405ba074 Mon Sep 17 00:00:00 2001 From: Michael Osthege Date: Wed, 21 May 2025 23:48:17 +0200 Subject: [PATCH 01/10] Add pre-commit config --- .github/.github/pre-commit.yml | 16 ++++++++++++++++ .pre-commit-config.yaml | 29 +++++++++++++++++++++++++++++ pyproject.toml | 5 ++++- 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 .github/.github/pre-commit.yml create mode 100644 .pre-commit-config.yaml diff --git a/.github/.github/pre-commit.yml b/.github/.github/pre-commit.yml new file mode 100644 index 0000000..62cc8c5 --- /dev/null +++ b/.github/.github/pre-commit.yml @@ -0,0 +1,16 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [master] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - uses: pre-commit/action@v3.0.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..107e83b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,29 @@ +exclude: \.(html|js)$ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.15.0 + hooks: + - id: mypy + exclude: 'test_.*?\.py$' +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.10.0 + hooks: + # Run the formatter. + - id: ruff-format +- repo: https://github.com/psf/black + rev: 25.1.0 + hooks: + - id: black +- repo: https://github.com/PyCQA/isort + rev: 6.0.1 + hooks: + - id: isort + name: isort diff --git a/pyproject.toml b/pyproject.toml index e8dfe18..8777b6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,4 +10,7 @@ authors = [ dependencies = [] [tool.setuptools] -packages = ["cri_lib"] \ No newline at end of file +packages = ["cri_lib"] + +[tool.isort] +profile = "black" From d05973c2fb5642e6720023983bf68d7114adaec4 Mon Sep 17 00:00:00 2001 From: Michael Osthege Date: Thu, 22 May 2025 00:03:28 +0200 Subject: [PATCH 02/10] Apply auto-formatting pre-commit --- README.md | 7 ++--- cri_lib/__init__.py | 23 ++++++++------- cri_lib/cri_controller.py | 12 ++++---- cri_lib/cri_errors.py | 6 +++- cri_lib/cri_protocol_parser.py | 19 +++++++------ examples/relative_move.py | 35 +++++++++++++++++++++-- examples/start_program.py | 18 ++++++------ requirements.txt | 2 +- tests/test_cri_parser.py | 51 +++++++++++++++++----------------- 9 files changed, 103 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 5a17375..faea028 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Introduction +# Introduction Python package to interface an igus Robot Control via the CRI protocol. # Current Features @@ -8,7 +8,7 @@ Python package to interface an igus Robot Control via the CRI protocol. - Enable / Disable - Acquisition of active control - Override -- Referencing of +- Referencing of - single axis - all axes - Set joints to zero @@ -50,5 +50,4 @@ The robot state is continuously sent to the computer by the iRC/ReBeL. It gets r See `examples` directory. # Tests -This repository provides pytests for the message parser. They require `pytest` and `pytest-cov`, which can be installed via `pip install -r requirements.txt`. To run the tests (including coverage) execute the following command: `pytest -vv --cov=cri_lib --cov-report term-missing tests`. - +This repository provides pytests for the message parser. They require `pytest` and `pytest-cov`, which can be installed via `pip install -r requirements.txt`. To run the tests (including coverage) execute the following command: `pytest -vv --cov=cri_lib --cov-report term-missing tests`. diff --git a/cri_lib/__init__.py b/cri_lib/__init__.py index cca8f6b..5f3f04c 100644 --- a/cri_lib/__init__.py +++ b/cri_lib/__init__.py @@ -2,23 +2,22 @@ .. include:: ../README.md """ +from .cri_controller import CRIController +from .cri_errors import CRICommandTimeOutError, CRIConnectionError, CRIError +from .cri_protocol_parser import CRIProtocolParser from .robot_state import ( - RobotMode, + ErrorStates, + JointsState, KinematicsState, + OperationInfo, OperationMode, - RunState, - ReplayMode, - ErrorStates, - RobotCartesianPosition, PlatformCartesianPosition, - JointsState, - RobotState, PosVariable, - OperationInfo, ReferencingAxisState, ReferencingState, + ReplayMode, + RobotCartesianPosition, + RobotMode, + RobotState, + RunState, ) -from .cri_controller import CRIController -from .cri_protocol_parser import CRIProtocolParser - -from .cri_errors import CRIError, CRIConnectionError, CRICommandTimeOutError diff --git a/cri_lib/cri_controller.py b/cri_lib/cri_controller.py index 0b496f5..caaa38f 100644 --- a/cri_lib/cri_controller.py +++ b/cri_lib/cri_controller.py @@ -1,17 +1,15 @@ -from enum import Enum import logging import socket import threading -from time import sleep, time +from enum import Enum from pathlib import Path +from queue import Empty, Queue +from time import sleep, time from typing import Callable -from queue import Queue, Empty - -from .robot_state import RobotState, KinematicsState - -from .cri_protocol_parser import CRIProtocolParser from .cri_errors import CRICommandTimeOutError, CRIConnectionError +from .cri_protocol_parser import CRIProtocolParser +from .robot_state import KinematicsState, RobotState logger = logging.getLogger(__name__) diff --git a/cri_lib/cri_errors.py b/cri_lib/cri_errors.py index 4077803..4069dc5 100644 --- a/cri_lib/cri_errors.py +++ b/cri_lib/cri_errors.py @@ -1,14 +1,18 @@ class CRIError(Exception): """Base class for CRI-related errors.""" + class CRIConnectionError(CRIError): """Raised when there is a connection error.""" + def __init__(self, message="Not connected to iRC or connection lost."): self.message = message super().__init__(self.message) + class CRICommandTimeOutError(CRIError): """Raised when a command times out.""" + def __init__(self, message="Time out waiting for command response."): self.message = message - super().__init__(self.message) \ No newline at end of file + super().__init__(self.message) diff --git a/cri_lib/cri_protocol_parser.py b/cri_lib/cri_protocol_parser.py index 00049b0..6092820 100644 --- a/cri_lib/cri_protocol_parser.py +++ b/cri_lib/cri_protocol_parser.py @@ -2,24 +2,25 @@ from threading import Lock from .robot_state import ( + ErrorStates, + JointsState, KinematicsState, + OperationInfo, OperationMode, - RobotState, - RunState, - ReplayMode, - RobotMode, - JointsState, - RobotCartesianPosition, PlatformCartesianPosition, - ErrorStates, PosVariable, - OperationInfo, - ReferencingState, ReferencingAxisState, + ReferencingState, + ReplayMode, + RobotCartesianPosition, + RobotMode, + RobotState, + RunState, ) logger = logging.getLogger(__name__) + class CRIProtocolParser: """Class handling the parsing of CRI messages to the robot state.""" diff --git a/examples/relative_move.py b/examples/relative_move.py index 7b97074..bf72379 100644 --- a/examples/relative_move.py +++ b/examples/relative_move.py @@ -1,8 +1,11 @@ import logging + from cri_lib import CRIController # 🔹 Configure logging -logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s") +logging.basicConfig( + level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s" +) logger = logging.getLogger(__name__) # CRIController is the main interface for controlling the iRC @@ -31,10 +34,36 @@ # Perform relative movement logger.info("Moving base relative: +20mm in X, Y, Z...") -controller.move_base_relative(20.0, 20.0, 20.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, wait_move_finished=True, move_finished_timeout=1000) +controller.move_base_relative( + 20.0, + 20.0, + 20.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 10.0, + wait_move_finished=True, + move_finished_timeout=1000, +) logger.info("Moving back: -20mm in X, Y, Z...") -controller.move_base_relative(-20.0, -20.0, -20.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0, wait_move_finished=True, move_finished_timeout=1000) +controller.move_base_relative( + -20.0, + -20.0, + -20.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 10.0, + wait_move_finished=True, + move_finished_timeout=1000, +) # Disable motors and disconnect logger.info("Disabling motors and disconnecting...") diff --git a/examples/start_program.py b/examples/start_program.py index a25885f..8b8ef94 100644 --- a/examples/start_program.py +++ b/examples/start_program.py @@ -4,26 +4,28 @@ from cri_lib import CRIController # 🔹 Configure logging -logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s") +logging.basicConfig( + level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s" +) logger = logging.getLogger(__name__) # CRIController is the main interface for controlling the iRC controller = CRIController() -#connect to default iRC IP -#if not controller.connect("127.0.0.1", 3921): +# connect to default iRC IP +# if not controller.connect("127.0.0.1", 3921): if not controller.connect("192.168.3.11"): logger.error("Unable to connect") quit() -#acquire active control. +# acquire active control. controller.set_active_control(True) logger.info("Enabling motors...") -#enable motors +# enable motors controller.enable() logger.info("Waiting for kinematics to be ready...") -#wait until kinematics are ready to move +# wait until kinematics are ready to move controller.wait_for_kinematics_ready(10) controller.set_override(50.0) @@ -69,6 +71,6 @@ controller.close() quit() -#Disable motors and disconnect +# Disable motors and disconnect controller.disable() -controller.close() \ No newline at end of file +controller.close() diff --git a/requirements.txt b/requirements.txt index cffeec6..9955dec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ pytest -pytest-cov \ No newline at end of file +pytest-cov diff --git a/tests/test_cri_parser.py b/tests/test_cri_parser.py index 3f97df0..706493e 100644 --- a/tests/test_cri_parser.py +++ b/tests/test_cri_parser.py @@ -3,33 +3,33 @@ import pytest from cri_lib import ( + CRIController, + CRIProtocolParser, + ErrorStates, + JointsState, KinematicsState, + OperationInfo, OperationMode, - RobotState, - RunState, - ReplayMode, - RobotMode, - JointsState, - RobotCartesianPosition, PlatformCartesianPosition, - ErrorStates, PosVariable, - OperationInfo, - ReferencingState, ReferencingAxisState, + ReferencingState, + ReplayMode, + RobotCartesianPosition, + RobotMode, + RobotState, + RunState, ) -from cri_lib import CRIController, CRIProtocolParser - def test_parse_state(): test_message = """ -CRISTART 1234 STATUS MODE joint +CRISTART 1234 STATUS MODE joint POSJOINTSETPOINT 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00 10.00 11.00 12.00 13.00 14.00 15.00 16.00 POSJOINTCURRENT 1.00 2.00 3.00 4.00 5.00 6.00 7.00 8.00 9.00 10.00 11.00 12.00 13.00 14.00 15.00 16.00 -POSCARTROBOT 10.0 20.0 30.0 0.00 90.00 0.00 +POSCARTROBOT 10.0 20.0 30.0 0.00 90.00 0.00 POSCARTPLATFORM 10.0 20.0 180.00 -OVERRIDE 80.0 +OVERRIDE 80.0 DIN 0000000000000FF00 DOUT 0000000000000FF00 ESTOP 3 SUPPLY 23000 CURRENTALL 2600 CURRENTJOINTS 10 20 30 40 50 60 70 80 90 100 110 120 130 140 150 160 @@ -224,16 +224,16 @@ def test_parse_unknown_message_tpye(): def test_parse_variables(): """Test for variable message""" - test_message = """CRISTART 7 VARIABLES - ValuePosVariable #position 217.395 0 350.155 180 3.57994e-05 180 0 -20 110 0 90 0 0 0 0 - ValueNrVariable #programrunning 0 - ValueNrVariable #logicprogramrunning 0 - ValueNrVariable #parts-good 0 - ValueNrVariable #parts-bad 0 - ValuePosVariable #userframe-a 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 - ValuePosVariable #userframe-b 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 - ValuePosVariable #userframe-c 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 - ValuePosVariable #position-userframe 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 + test_message = """CRISTART 7 VARIABLES + ValuePosVariable #position 217.395 0 350.155 180 3.57994e-05 180 0 -20 110 0 90 0 0 0 0 + ValueNrVariable #programrunning 0 + ValueNrVariable #logicprogramrunning 0 + ValueNrVariable #parts-good 0 + ValueNrVariable #parts-bad 0 + ValuePosVariable #userframe-a 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 + ValuePosVariable #userframe-b 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 + ValuePosVariable #userframe-c 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 + ValuePosVariable #position-userframe 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 FooVariableType sdgsdsd 1 2 3 4 CRIEND""" variables = { @@ -560,6 +560,7 @@ def test_info_boardtemps(): assert controller.answer_events["info_boardtemp"].is_set() assert controller.robot_state.board_temps == pytest.approx(board_temps) + def test_info_motortemps(): test_message = "CRISTART 6789 INFO MotorTemp 21.1 22.2 23.3 24.4 25.5 26.6 27.7 28.8 29.9 30.0 31.1 32.2 33.3 34.4 35.5 36.6 CRIEND" @@ -587,4 +588,4 @@ def test_info_motortemps(): controller._parse_message(test_message) assert controller.answer_events["info_motortemp"].is_set() - assert controller.robot_state.motor_temps == pytest.approx(motor_temps) \ No newline at end of file + assert controller.robot_state.motor_temps == pytest.approx(motor_temps) From d260b74267d1e74ffd7b7a5b4ae2c0f8fe4d7abe Mon Sep 17 00:00:00 2001 From: Michael Osthege Date: Thu, 22 May 2025 00:29:59 +0200 Subject: [PATCH 03/10] Fix type issues --- cri_lib/cri_controller.py | 34 +++++++++++++++++++--------------- cri_lib/cri_protocol_parser.py | 27 +++++++++++++++------------ cri_lib/robot_state.py | 2 +- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/cri_lib/cri_controller.py b/cri_lib/cri_controller.py index caaa38f..49cde10 100644 --- a/cri_lib/cri_controller.py +++ b/cri_lib/cri_controller.py @@ -5,7 +5,7 @@ from pathlib import Path from queue import Empty, Queue from time import sleep, time -from typing import Callable +from typing import Any, Callable from .cri_errors import CRICommandTimeOutError, CRIConnectionError from .cri_protocol_parser import CRIProtocolParser @@ -32,14 +32,14 @@ class MotionType(Enum): CartTool = "CartTool" Platform = "Platform" - def __init__(self): + def __init__(self) -> None: self.robot_state: RobotState = RobotState() self.robot_state_lock = threading.Lock() self.parser = CRIProtocolParser(self.robot_state, self.robot_state_lock) self.connected = False - self.sock = None + self.sock: socket.socket | None = None self.socket_write_lock = threading.Lock() self.can_mode: bool = False @@ -56,7 +56,7 @@ def __init__(self): self.answer_events: dict[str, threading.Event] = {} self.error_messages: dict[str, str] = {} - self.status_callback = None + self.status_callback: Callable | None = None self.live_jog_active: bool = False self.jog_intervall = self.ALIVE_JOG_INTERVAL_SEC @@ -123,7 +123,7 @@ def close(self) -> None: Close network connection. Might block for a while waiting for the threads to finish. """ - if not self.connected: + if not self.connected or self.sock is None: return self._send_command("QUIT") @@ -161,7 +161,7 @@ def _send_command( If the command was sent the message_id gets returned or None if there was an error. """ - if not self.connected: + if not self.connected or self.sock is None: logger.error("Not connected. Use connect() to establish a connection.") raise CRIConnectionError( "Not connected. Use connect() to establish a connection." @@ -206,7 +206,6 @@ def _bg_alivejog_thread(self) -> None: """ Background Thread sending alivejog messages to keep connection alive. """ - while self.connected: if self.live_jog_active: with self.jog_speeds_lock: @@ -225,6 +224,10 @@ def _bg_receive_thread(self) -> None: """ Background thread receiving data and parsing it to the robot state. """ + if self.sock is None: + logger.error("Receive Thread: Not connected.") + return + message_buffer = bytearray() while self.connected: @@ -289,7 +292,7 @@ def _wait_for_answer( with self.answer_events_lock: if message_id not in self.answer_events: - return False + return None wait_event = self.answer_events[message_id] # prevent deadlock through answer_events_lock @@ -324,8 +327,8 @@ def _parse_message(self, message: str) -> None: msg_id = notification["answer"] if msg_id in self.answer_events: - if "error" in notification: - self.error_messages[msg_id] = notification["error"] + if (error_msg := notification.get("error", None)) is not None: + self.error_messages[msg_id] = error_msg self.answer_events[msg_id].set() @@ -426,7 +429,7 @@ def disable(self) -> bool: else: return False - def set_active_control(self, active: bool) -> None: + def set_active_control(self, active: bool) -> bool: """Acquire or return active control of robot Parameters @@ -559,7 +562,7 @@ def get_referencing_info(self): else: return False - def wait_for_kinematics_ready(self, timeout: float | None = 30) -> bool: + def wait_for_kinematics_ready(self, timeout: float = 30) -> bool: """Wait until drive state is indicated as ready. Parameters @@ -953,7 +956,7 @@ def move_tool_relative( else: return False - def stop_move(self) -> None: + def stop_move(self) -> bool: """Stop movement Returns @@ -1372,6 +1375,7 @@ def upload_file(self, path: str | Path, target_directory: str) -> bool: if self._send_command(command, True) is None: return False + return True def enable_can_bridge(self, enabled: bool) -> None: """Enables or diables CAN bridge mode. All other functions are disabled in CAN bridge mode. @@ -1413,7 +1417,7 @@ def can_send(self, msg_id: int, length: int, data: bytearray) -> None: def can_receive( self, blocking: bool = True, timeout: float | None = None - ) -> dict[str, any] | None: + ) -> dict[str, Any] | None: """Receive CAN message in CAN bridge mode from the recveive queue. Returns @@ -1423,7 +1427,7 @@ def can_receive( """ if not self.can_mode: logger.debug("can_receive: CAN mode not enabled") - return + return None try: item = self.can_queue.get(blocking, timeout) diff --git a/cri_lib/cri_protocol_parser.py b/cri_lib/cri_protocol_parser.py index 6092820..ddec120 100644 --- a/cri_lib/cri_protocol_parser.py +++ b/cri_lib/cri_protocol_parser.py @@ -1,5 +1,6 @@ import logging from threading import Lock +from typing import Any, Sequence from .robot_state import ( ErrorStates, @@ -28,7 +29,9 @@ def __init__(self, robot_state: RobotState, robot_state_lock: Lock): self.robot_state = robot_state self.robot_state_lock = robot_state_lock - def parse_message(self, message: str) -> str | None: + def parse_message( + self, message: str + ) -> dict[str, str] | dict[str, str | None] | None: """Parses a message to the RobotState of the class. Parameters: ----------- @@ -332,7 +335,7 @@ def _parse_gripperstate(self, parameters: list[str]) -> None: with self.robot_state_lock: self.robot_state.gripper_state = float(parameters[0]) - def _parse_variables(self, parameters: list[str]) -> None: + def _parse_variables(self, parameters: Sequence[str]) -> None: """ Parses a variables message to the robot state. @@ -341,7 +344,7 @@ def _parse_variables(self, parameters: list[str]) -> None: parameters: list[str] List of splitted strings between `VARIABLES` and `CRIEND` """ - variables = {} + variables: dict[str, float | PosVariable] = {} idx = 0 while idx < len(parameters): @@ -363,7 +366,7 @@ def _parse_variables(self, parameters: list[str]) -> None: with self.robot_state_lock: self.robot_state.variabels = variables - def _parse_opinfo(self, parameters: list[str]) -> None: + def _parse_opinfo(self, parameters: Sequence[str]) -> None: """ Parses a opinfo message to the robot state. @@ -379,7 +382,7 @@ def _parse_opinfo(self, parameters: list[str]) -> None: with self.robot_state_lock: self.robot_state.operation_info = OperationInfo(*values) - def _parse_cmd(self, parameters: list[str]) -> str: + def _parse_cmd(self, parameters: Sequence[str]) -> str | None: """ Parses a cmd message to the robot state. @@ -407,7 +410,7 @@ def _parse_cmd(self, parameters: list[str]) -> str: return None - def _parse_message_message(self, parameters: list[str]) -> None: + def _parse_message_message(self, parameters: Sequence[str]) -> None: """ Parses a message message to the robot state. @@ -457,7 +460,7 @@ def _parse_message_message(self, parameters: list[str]) -> None: else: logger.debug("MESSAGE: %s", " ".join(parameters)) - def _parse_can_bridge(self, parameters: list[str]) -> dict[str, any] | None: + def _parse_can_bridge(self, parameters: Sequence[str]) -> dict[str, Any] | None: """Parses a can bridge message. Parameters @@ -514,7 +517,7 @@ def _parse_can_bridge(self, parameters: list[str]) -> dict[str, any] | None: }, } - def _parse_config(self, parameters: list[str]) -> None: + def _parse_config(self, parameters: Sequence[str]) -> None: """ Parses a config message to the robot state. @@ -527,7 +530,7 @@ def _parse_config(self, parameters: list[str]) -> None: with self.robot_state_lock: self.robot_state.project_file = parameters[1] - def _parse_cmderror(self, parameters: list[str]) -> dict[str, str]: + def _parse_cmderror(self, parameters: Sequence[str]) -> dict[str, str]: """Parses a CMDERROR message to notify calling function Parameters: @@ -543,7 +546,7 @@ def _parse_cmderror(self, parameters: list[str]) -> dict[str, str]: return {"answer": parameters[0], "error": " ".join(parameters[1:])} - def _parse_info(self, parameters: list[str]) -> None: + def _parse_info(self, parameters: list[str]) -> str | None: """ Parses a info message to the robot state. @@ -597,7 +600,7 @@ def _parse_info(self, parameters: list[str]) -> None: else: return None - def _parse_execerror(self, parameters: list[str]) -> dict[str, str]: + def _parse_execerror(self, parameters: Sequence[str]) -> dict[str, str]: """Parses a EXECERROR message to notify calling function Parameters: @@ -614,7 +617,7 @@ def _parse_execerror(self, parameters: list[str]) -> dict[str, str]: return {"answer": "EXECEND", "error": " ".join(parameters)} @staticmethod - def _split_quotes_aware(msg: str) -> list[str]: + def _split_quotes_aware(msg: str) -> Sequence[str]: """ Splits a string at whitespaces but ignores whitespace whithin quotes. diff --git a/cri_lib/robot_state.py b/cri_lib/robot_state.py index 0e94f57..3a31cca 100644 --- a/cri_lib/robot_state.py +++ b/cri_lib/robot_state.py @@ -313,7 +313,7 @@ class RobotState: gripper_state: float = 0.0 """current opening value of the gripper""" - variabels: dict[str : [PosVariable | float]] = field(default_factory=dict) + variabels: dict[str, PosVariable | float] = field(default_factory=dict) """variables saved in robot controller""" operation_info: OperationInfo = field(default_factory=OperationInfo) From b467de4c832165c15864a37e7b349242906bd981 Mon Sep 17 00:00:00 2001 From: Michael Osthege Date: Thu, 22 May 2025 00:45:03 +0200 Subject: [PATCH 04/10] Run pytest in GitHub Actions --- .github/.github/tests.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/.github/tests.yml diff --git a/.github/.github/tests.yml b/.github/.github/tests.yml new file mode 100644 index 0000000..6fa498b --- /dev/null +++ b/.github/.github/tests.yml @@ -0,0 +1,26 @@ +name: tests + +on: + pull_request: + push: + branches: [master] + +jobs: + test-job: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + pip install -e . + pip install -r requirements.txt + - name: Test with pytest + run: | + pytest --cov=./cri_lib --cov-report term-missing tests From f7d2ed5f9b21702f84af160834704f2b1c4d55e7 Mon Sep 17 00:00:00 2001 From: Michael Osthege Date: Thu, 27 Nov 2025 13:28:50 +0100 Subject: [PATCH 05/10] Add `timeout` kwarg to referencing methods For larger portals the referencing can take more than 30 seconds. --- cri_lib/cri_controller.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cri_lib/cri_controller.py b/cri_lib/cri_controller.py index 49cde10..edaa853 100644 --- a/cri_lib/cri_controller.py +++ b/cri_lib/cri_controller.py @@ -481,7 +481,7 @@ def zero_all_joints(self) -> bool: else: return False - def reference_all_joints(self) -> bool: + def reference_all_joints(self, *, timeout: float = 30) -> bool: """Reference all joints. Long timout of 30 seconds. Returns @@ -493,7 +493,7 @@ def reference_all_joints(self) -> bool: if (msg_id := self._send_command("CMD ReferenceAllJoints", True)) is not None: if ( - error_msg := self._wait_for_answer(f"{msg_id}", timeout=30.0) + error_msg := self._wait_for_answer(f"{msg_id}", timeout=timeout) ) is not None: logger.debug("Error in ReferenceAllJoints command: %s", error_msg) return False @@ -502,7 +502,7 @@ def reference_all_joints(self) -> bool: else: return False - def reference_single_joint(self, joint: str) -> bool: + def reference_single_joint(self, joint: str, *, timeout: float = 30) -> bool: """Reference a single joint. Long timout of 30 seconds. Parameters @@ -528,7 +528,7 @@ def reference_single_joint(self, joint: str) -> bool: msg_id := self._send_command(f"CMD ReferenceSingleJoint {joint_msg}", True) ) is not None: if ( - error_msg := self._wait_for_answer(f"{msg_id}", timeout=30.0) + error_msg := self._wait_for_answer(f"{msg_id}", timeout=timeout) ) is not None: logger.debug("Error in ReferenceSingleJoint command: %s", error_msg) return False From babc023b4591afbf780741a04518c5197d9c6034 Mon Sep 17 00:00:00 2001 From: Bastian Richter Date: Thu, 27 Nov 2025 14:19:46 +0100 Subject: [PATCH 06/10] Metion pre-commit in readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index faea028..30fa522 100644 --- a/README.md +++ b/README.md @@ -51,3 +51,4 @@ See `examples` directory. # Tests This repository provides pytests for the message parser. They require `pytest` and `pytest-cov`, which can be installed via `pip install -r requirements.txt`. To run the tests (including coverage) execute the following command: `pytest -vv --cov=cri_lib --cov-report term-missing tests`. +Runs as pre-commit. \ No newline at end of file From ea599dbd010755bc36e8900ee545a63234bb9a3c Mon Sep 17 00:00:00 2001 From: Bastian Richter Date: Thu, 27 Nov 2025 14:23:13 +0100 Subject: [PATCH 07/10] Update pre-commit.yml for test in branch Change --- .github/.github/pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/.github/pre-commit.yml b/.github/.github/pre-commit.yml index 62cc8c5..c45ff80 100644 --- a/.github/.github/pre-commit.yml +++ b/.github/.github/pre-commit.yml @@ -3,7 +3,7 @@ name: pre-commit on: pull_request: push: - branches: [master] + branches: [master, pre-commit] jobs: pre-commit: From 9246f13d3c2c863ff8db1cabf7b5ab2afbe606eb Mon Sep 17 00:00:00 2001 From: Bastian Richter Date: Thu, 27 Nov 2025 14:24:52 +0100 Subject: [PATCH 08/10] test push for github action --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 30fa522..262db4b 100644 --- a/README.md +++ b/README.md @@ -51,4 +51,4 @@ See `examples` directory. # Tests This repository provides pytests for the message parser. They require `pytest` and `pytest-cov`, which can be installed via `pip install -r requirements.txt`. To run the tests (including coverage) execute the following command: `pytest -vv --cov=cri_lib --cov-report term-missing tests`. -Runs as pre-commit. \ No newline at end of file +Runs as pre-commit Github action. \ No newline at end of file From ae425430290aa7217e2c31f6e082ef7ee9fbf2a5 Mon Sep 17 00:00:00 2001 From: Bastian Richter Date: Thu, 27 Nov 2025 14:31:27 +0100 Subject: [PATCH 09/10] move actions to workflows folder --- .github/{.github => workflows}/pre-commit.yml | 0 .github/{.github => workflows}/tests.yml | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/{.github => workflows}/pre-commit.yml (100%) rename .github/{.github => workflows}/tests.yml (100%) diff --git a/.github/.github/pre-commit.yml b/.github/workflows/pre-commit.yml similarity index 100% rename from .github/.github/pre-commit.yml rename to .github/workflows/pre-commit.yml diff --git a/.github/.github/tests.yml b/.github/workflows/tests.yml similarity index 100% rename from .github/.github/tests.yml rename to .github/workflows/tests.yml From d7de0e6d34f339db472c9ee4b1de6b9b6cb968d0 Mon Sep 17 00:00:00 2001 From: Bastian Richter Date: Thu, 27 Nov 2025 14:39:40 +0100 Subject: [PATCH 10/10] adjust README.md to pre-commit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 262db4b..334206d 100644 --- a/README.md +++ b/README.md @@ -51,4 +51,4 @@ See `examples` directory. # Tests This repository provides pytests for the message parser. They require `pytest` and `pytest-cov`, which can be installed via `pip install -r requirements.txt`. To run the tests (including coverage) execute the following command: `pytest -vv --cov=cri_lib --cov-report term-missing tests`. -Runs as pre-commit Github action. \ No newline at end of file +Runs as pre-commit Github action.