diff --git a/motor_stage_ui/motor_stage_gui.py b/motor_stage_ui/motor_stage_gui.py index 36827b4..095a3f7 100644 --- a/motor_stage_ui/motor_stage_gui.py +++ b/motor_stage_ui/motor_stage_gui.py @@ -1,7 +1,7 @@ from motor_stage_ui.pi_stages_interface import PIStagesInterface from motor_stage_ui.pi_stages_interface import SerialInterface +from motor_stage_ui.test.utils import SerialInterfaceMock from motor_stage_ui import logger -import motor_stage_ui import yaml import logging @@ -9,6 +9,7 @@ from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QLineEdit, QLabel import sys import os +from pathlib import Path """ @@ -357,10 +358,19 @@ def get_position_clicked( def main(): - app = QApplication(sys.argv) + try: + if os.environ["TEST"]: + path = Path(__file__).parent / "test" + config_path = path / "test_configuration.yaml" + interface = SerialInterfaceMock + + except KeyError: + path = Path(__file__).parent + config_path = path / "configuration.yaml" + interface = SerialInterface - path = os.path.dirname(motor_stage_ui.__file__) - window = MainWindow(path + "/configuration.yaml") + app = QApplication(sys.argv) + window = MainWindow(config_path, interface=interface) window.show() app.exec() diff --git a/motor_stage_ui/motor_stage_terminal.py b/motor_stage_ui/motor_stage_terminal.py index 6b7d574..5e05e58 100644 --- a/motor_stage_ui/motor_stage_terminal.py +++ b/motor_stage_ui/motor_stage_terminal.py @@ -1,6 +1,9 @@ from motor_stage_ui.pi_stages_interface import PIStagesInterface as MC import motor_stage_ui +from motor_stage_ui.pi_stages_interface import SerialInterface +from motor_stage_ui.test.utils import SerialInterfaceMock +from pathlib import Path import os import click import yaml @@ -23,12 +26,24 @@ def motor(conf): Args: conf (str): Needs path to configuration yaml. This path is converted into a click object and passed to the individual functions. """ + try: + if os.environ["TEST"]: + path = Path(__file__).parent / "test" + config_path = path / "test_configuration.yaml" + test = True + + except KeyError: + path = Path(__file__).parent + config_path = path / "configuration.yaml" + test = False conf.ensure_object(dict) - path = os.path.dirname(motor_stage_ui.__file__) - config_path = path + "/configuration.yaml" with open(config_path, "r") as file: conf.obj["CONF"] = yaml.full_load(file) + if test: + conf.obj["MOCK"] = True + else: + conf.obj["MOCK"] = False @click.command() @@ -40,9 +55,14 @@ def init(conf, motor_name: str): Args: motor_name (str): name of the motorstage """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, ) mc.init_motor(conf.obj["CONF"][motor_name]["address"]) @@ -59,9 +79,14 @@ def move(conf, motor_name: str, a: str): motor_name (str): name of the motorstage a (str): Move amount """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, ) mc.move_relative( conf.obj["CONF"][motor_name]["address"], @@ -84,9 +109,14 @@ def moveto(conf, motor_name: str, a: str): motor_name (str): name of the motorstage a (str): Move to position """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, ) mc.move_to_position( conf.obj["CONF"][motor_name]["address"], @@ -106,9 +136,14 @@ def pos(conf, motor_name: str): Args: motor_name (str): name of the motorstage """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, ) click.echo( "Position of: " @@ -119,7 +154,7 @@ def pos(conf, motor_name: str): conf.obj["CONF"][motor_name]["address"], conf.obj["CONF"][motor_name]["unit"], conf.obj["CONF"][motor_name]["stage_type"], - conf.obj["CONF"][motor_name]["step_size"], + float(conf.obj["CONF"][motor_name]["step_size"]), ) ) + " " @@ -136,9 +171,14 @@ def stop(conf, motor_name: str): Args: motor_name (str): name of the motorstage """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, ) mc.abort(conf.obj["CONF"][motor_name]["address"]) @@ -152,9 +192,14 @@ def sethome(conf, motor_name: str): Args: motor_name (str): name of the motorstage """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, ) mc.set_home(conf.obj["CONF"][motor_name]["address"]) @@ -168,9 +213,14 @@ def gohome(conf, motor_name: str): Args: motor_name (str): name of the motorstage """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, ) mc.go_home(conf.obj["CONF"][motor_name]["address"]) @@ -184,11 +234,21 @@ def status(conf, motor_name: str): Args: motor_name (str): name of the motorstage """ + if conf.obj["MOCK"]: + interface = SerialInterfaceMock + else: + interface = SerialInterface mc = MC( port=conf.obj["CONF"][motor_name]["port"], baud_rate=conf.obj["CONF"][motor_name]["baud_rate"], + interface=interface, + ) + click.echo( + "Status of: " + + motor_name + + " " + + str(mc.get_stat(conf.obj["CONF"][motor_name]["address"])) ) - mc.get_stat(conf.obj["CONF"][motor_name]["address"]) motor.add_command(init) diff --git a/motor_stage_ui/pi_stages_interface.py b/motor_stage_ui/pi_stages_interface.py index b43a42d..2c90d8b 100644 --- a/motor_stage_ui/pi_stages_interface.py +++ b/motor_stage_ui/pi_stages_interface.py @@ -199,9 +199,8 @@ def get_stat(self, address: int) -> None: address (int): Address of the motorstage """ err_msg = self._write_read("TS", address) - self.log.warning( - "Status of motor stage with address %i: %s" % (address, err_msg) - ) + self.log.debug("Status of motor stage with address %i: %s" % (address, err_msg)) + return err_msg def abort(self, address: int) -> None: """Stops all movement of the motorstage. @@ -269,7 +268,7 @@ def get_position( address (int): Address of the motorstage unit (str): output unit stage (int): stage type either 'rotation' or translation - step_size (int): step size of the motorstage given in deg or um + step_size (float): step size of the motorstage given in deg or um Returns: str: current position of motorstage in unit 3 digits precision respectively diff --git a/motor_stage_ui/test/test_gui.py b/motor_stage_ui/test/test_gui.py index 7b35748..2518c0d 100644 --- a/motor_stage_ui/test/test_gui.py +++ b/motor_stage_ui/test/test_gui.py @@ -1,13 +1,17 @@ from pathlib import Path +import yaml import pytest from motor_stage_ui.motor_stage_gui import MainWindow from motor_stage_ui.test.utils import SerialInterfaceMock from PyQt5 import QtCore +import logging FILEPATH = Path(__file__).parent CONFIG_FILE = FILEPATH / "test_configuration.yaml" INTERFACE = SerialInterfaceMock +with open(CONFIG_FILE) as yaml_file: + TESTCONFIG = yaml.safe_load(yaml_file) @pytest.fixture @@ -17,9 +21,172 @@ def app(qtbot): def test_init_clicked(app): - app.mouseClick(app.init_clicked, app.QtCore.Qt.MouseButton.LeftButton) - assert app.motor.serial_interface._serial_commands[-3:] == [ + ADDRESS = TESTCONFIG["x_axis"]["address"] + app.init_clicked(address=ADDRESS, index=0) + assert app.motor[0].serial_interface._serial_commands[-3:] == [ b"\x010MN\r", b"\x010RT\r", b"\x010SV200000\r", ] + + ADDRESS = TESTCONFIG["rot"]["address"] + app.init_clicked(address=ADDRESS, index=1) + assert app.motor[1].serial_interface._serial_commands[-3:] == [ + b"\x012MN\r", + b"\x012RT\r", + b"\x012SV200000\r", + ] + + +def test_move_back_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + UNIT = TESTCONFIG["x_axis"]["unit"] + STEPSIZE = TESTCONFIG["x_axis"]["step_size"] + STAGE = TESTCONFIG["x_axis"]["stage_type"] + app.move_back_clicked( + address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE, index=0 + ) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010MR-55555\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + UNIT = TESTCONFIG["rot"]["unit"] + STEPSIZE = float(TESTCONFIG["rot"]["step_size"]) + STAGE = TESTCONFIG["rot"]["stage_type"] + app.move_back_clicked( + address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE, index=1 + ) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012MR-29411\r" + + +def test_move_ahead_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + UNIT = TESTCONFIG["x_axis"]["unit"] + STEPSIZE = TESTCONFIG["x_axis"]["step_size"] + STAGE = TESTCONFIG["x_axis"]["stage_type"] + app.move_ahead_clicked( + address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE, index=0 + ) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010MR55555\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + UNIT = TESTCONFIG["rot"]["unit"] + STEPSIZE = float(TESTCONFIG["rot"]["step_size"]) + STAGE = TESTCONFIG["rot"]["stage_type"] + app.move_ahead_clicked( + address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE, index=1 + ) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012MR29411\r" + + +def test_abort_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + app.abort_clicked(address=ADDRESS, index=0) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010AB\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + app.abort_clicked(address=ADDRESS, index=1) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012AB\r" + + +def test_set_home_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + app.set_home_clicked(address=ADDRESS, index=0) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010DH\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + app.set_home_clicked(address=ADDRESS, index=1) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012DH\r" + + +def test_go_home_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + app.go_home_clicked(address=ADDRESS, index=0) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010GH\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + app.go_home_clicked(address=ADDRESS, index=1) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012GH\r" + + +def test_set_position_abs_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + UNIT = TESTCONFIG["x_axis"]["unit"] + STEPSIZE = TESTCONFIG["x_axis"]["step_size"] + STAGE = TESTCONFIG["x_axis"]["stage_type"] + app.set_position_abs_clicked( + address=ADDRESS, + textbox="2cm", + unit=UNIT, + stage=STAGE, + step_size=STEPSIZE, + index=0, + ) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010MA1111111\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + UNIT = TESTCONFIG["rot"]["unit"] + STEPSIZE = float(TESTCONFIG["rot"]["step_size"]) + STAGE = TESTCONFIG["rot"]["stage_type"] + app.set_position_abs_clicked( + address=ADDRESS, + textbox="1deg", + unit=UNIT, + stage=STAGE, + step_size=STEPSIZE, + index=1, + ) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012MA29411\r" + + +def test_set_position_rel_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + UNIT = TESTCONFIG["x_axis"]["unit"] + STEPSIZE = TESTCONFIG["x_axis"]["step_size"] + STAGE = TESTCONFIG["x_axis"]["stage_type"] + app.set_position_rel_clicked( + address=ADDRESS, + textbox="2cm", + unit=UNIT, + stage=STAGE, + step_size=STEPSIZE, + index=0, + ) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010MR1111111\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + UNIT = TESTCONFIG["rot"]["unit"] + STEPSIZE = float(TESTCONFIG["rot"]["step_size"]) + STAGE = TESTCONFIG["rot"]["stage_type"] + app.set_position_rel_clicked( + address=ADDRESS, + textbox="1.5deg", + unit=UNIT, + stage=STAGE, + step_size=STEPSIZE, + index=1, + ) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012MR44117\r" + + +def test_get_position_clicked(app): + ADDRESS = TESTCONFIG["x_axis"]["address"] + UNIT = TESTCONFIG["x_axis"]["unit"] + STEPSIZE = TESTCONFIG["x_axis"]["step_size"] + STAGE = TESTCONFIG["x_axis"]["stage_type"] + app.get_position_clicked( + address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE, index=0 + ) + assert app.motor[0].serial_interface._serial_commands[-1] == b"\x010TP\r" + + ADDRESS = TESTCONFIG["rot"]["address"] + UNIT = TESTCONFIG["rot"]["unit"] + STEPSIZE = float(TESTCONFIG["rot"]["step_size"]) + STAGE = TESTCONFIG["rot"]["stage_type"] + app.get_position_clicked( + address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE, index=1 + ) + assert app.motor[1].serial_interface._serial_commands[-1] == b"\x012TP\r" + + +if __name__ == "__main__": + pytest.main() diff --git a/motor_stage_ui/test/test_pi_stage_interface.py b/motor_stage_ui/test/test_pi_stage_interface.py index 65f35d8..cf071c0 100644 --- a/motor_stage_ui/test/test_pi_stage_interface.py +++ b/motor_stage_ui/test/test_pi_stage_interface.py @@ -55,28 +55,16 @@ def test_find_edge(): STEPSIZE = TESTCONFIG["x_axis"]["step_size"] STAGE = TESTCONFIG["x_axis"]["stage_type"] - def __get_position(self, address): - # Blank get Position function - return 0 - - func_type = type(PISTAGES._get_position) - PISTAGES._get_position = func_type(__get_position, PISTAGES) PISTAGES.find_edge(address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE) - assert PISTAGES.serial_interface._serial_commands[-1] == b"\x010FE0\r" + assert PISTAGES.serial_interface._serial_commands[-1] == b"\x010TP\r" ADDRESS = TESTCONFIG["rot"]["address"] UNIT = TESTCONFIG["rot"]["unit"] STEPSIZE = float(TESTCONFIG["rot"]["step_size"]) STAGE = TESTCONFIG["rot"]["stage_type"] - def __get_position(self, address): - # Blank get Position function - return 0 - - func_type = type(PISTAGES._get_position) - PISTAGES._get_position = func_type(__get_position, PISTAGES) PISTAGES.find_edge(address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE) - assert PISTAGES.serial_interface._serial_commands[-1] == b"\x012FE0\r" + assert PISTAGES.serial_interface._serial_commands[-1] == b"\x012TP\r" def test_set_home() -> None: @@ -155,12 +143,6 @@ def test_get_position(): STEPSIZE = TESTCONFIG["x_axis"]["step_size"] STAGE = TESTCONFIG["x_axis"]["stage_type"] - def __get_position(self, address): - # Blank get Position function - return 0 - - func_type = type(PISTAGES._get_position) - PISTAGES._get_position = func_type(__get_position, PISTAGES) assert ( PISTAGES.get_position( address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE @@ -173,12 +155,6 @@ def __get_position(self, address): STEPSIZE = float(TESTCONFIG["rot"]["step_size"]) STAGE = TESTCONFIG["rot"]["stage_type"] - def __get_position(self, address): - # Blank get Position function - return 0 - - func_type = type(PISTAGES._get_position) - PISTAGES._get_position = func_type(__get_position, PISTAGES) assert ( PISTAGES.get_position( address=ADDRESS, unit=UNIT, stage=STAGE, step_size=STEPSIZE diff --git a/motor_stage_ui/test/test_terminal_ui.py b/motor_stage_ui/test/test_terminal_ui.py index 6f3a19a..35a1bc0 100644 --- a/motor_stage_ui/test/test_terminal_ui.py +++ b/motor_stage_ui/test/test_terminal_ui.py @@ -1,27 +1,80 @@ from click.testing import CliRunner +import os import motor_stage_ui.motor_stage_terminal as terminal_ui +os.environ["TEST"] = "True" + + def test_motor(): runner = CliRunner() - runner.invoke(terminal_ui.motor) + result = runner.invoke(terminal_ui.motor) + assert result.exit_code == 0 def test_init(): runner = CliRunner() - runner.invoke(terminal_ui.init) + result = runner.invoke(terminal_ui.motor, ["init", "x_axis"]) + assert result.exit_code == 0 + result = runner.invoke(terminal_ui.motor, ["init", "rot"]) + assert result.exit_code == 0 -def test_gohome(): +def test_move(): runner = CliRunner() - runner.invoke(terminal_ui.gohome, ["x_axis"]) + result = runner.invoke(terminal_ui.motor, ["move", "-a", "2cm", "x_axis"]) + assert result.exit_code == 0 + result = runner.invoke(terminal_ui.motor, ["move", "-a", "2deg", "rot"]) + assert result.exit_code == 0 + + +def test_moveto(): + runner = CliRunner() + result = runner.invoke(terminal_ui.motor, ["moveto", "-a", "-1cm", "x_axis"]) + assert result.exit_code == 0 + result = runner.invoke(terminal_ui.motor, ["moveto", "-a", "-1deg", "rot"]) + assert result.exit_code == 0 def test_pos(): runner = CliRunner() - runner.invoke(terminal_ui.pos, ["x_axis"]) + result = runner.invoke(terminal_ui.motor, ["pos", "x_axis"]) + assert result.exit_code == 0 + assert result.output == "Position of: x_axis 0.000 mm\n" + result = runner.invoke(terminal_ui.motor, ["pos", "rot"]) + assert result.exit_code == 0 + assert result.output == "Position of: rot 0.000 deg\n" -def test_move(): +def test_stop(): + runner = CliRunner() + result = runner.invoke(terminal_ui.motor, ["stop", "x_axis"]) + assert result.exit_code == 0 + result = runner.invoke(terminal_ui.motor, ["stop", "rot"]) + assert result.exit_code == 0 + + +def test_sethome(): + runner = CliRunner() + result = runner.invoke(terminal_ui.motor, ["sethome", "x_axis"]) + assert result.exit_code == 0 + result = runner.invoke(terminal_ui.motor, ["sethome", "rot"]) + assert result.exit_code == 0 + + +def test_gohome(): + runner = CliRunner() + result = runner.invoke(terminal_ui.motor, ["gohome", "x_axis"]) + assert result.exit_code == 0 + result = runner.invoke(terminal_ui.motor, ["gohome", "rot"]) + assert result.exit_code == 0 + + +def test_status(): runner = CliRunner() - runner.invoke(terminal_ui.move, ["-a 3cm x_axis"]) + result = runner.invoke(terminal_ui.motor, ["status", "x_axis"]) + assert result.exit_code == 0 + assert result.output == "Status of: x_axis \x010TS\n" + result = runner.invoke(terminal_ui.motor, ["status", "rot"]) + assert result.exit_code == 0 + assert result.output == "Status of: rot \x012TS\n" diff --git a/motor_stage_ui/test/utils.py b/motor_stage_ui/test/utils.py index f7fea3b..00ef375 100644 --- a/motor_stage_ui/test/utils.py +++ b/motor_stage_ui/test/utils.py @@ -37,6 +37,8 @@ def _read(self): str: message """ msg = self._serial_commands[-1].decode().strip().replace(self._terminator, "") + if msg[-2:] == "TP": + msg = "0.000" if msg == "": self.log.error("No responds from serial interface.") raise ValueError diff --git a/pyproject.toml b/pyproject.toml index d1d38f2..0fab8fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ ] [project.optional-dependencies] -test = ["pytest", "coverage"] +test = ["pytest", "coverage", "pytest-qt"] [project.urls] "Repository" = "https://github.com/SiLab-Bonn/motor_stage_ui"