Loading .gitlab-ci.yml 0 → 100644 +67 −0 Original line number Diff line number Diff line # This file is a template, and might need editing before it works on your project. # This is a sample GitLab CI/CD configuration file that should run without any modifications. # It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts, # it uses echo commands to simulate the pipeline execution. # # A pipeline is composed of independent jobs that run scripts, grouped into stages. # Stages run in sequential order, but jobs within stages run in parallel. # # For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages # # You can copy and paste this template into a new `.gitlab-ci.yml` file. # You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. # # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml stages: # List of stages for jobs, and their order of execution - lint - deploy default: image: name: labinfo.ing.he-arc.ch:5050/igib/shared/ci-docker/poetry1.2.0-python3.10 entrypoint: [ "" ] lint-black: stage: lint tags: - docker rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always allow_failure: false script: - poetry install --only lint - poetry run black --check ms210/ lint-pylint: stage: lint tags: - docker rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always allow_failure: false script: - poetry install --only lint - poetry run pylint --fail-under=8 ms210/ deploy-job: # This job runs only when the merge is accepted stage: deploy # It only runs when *both* jobs in the test stage complete successfully. tags: - docker environment: production rules: - if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push" when: always - when: never script: - poetry install --only main - poetry config repositories.gitlab https://labinfo.ing.he-arc.ch/gitlab/api/v4/projects/$CI_PROJECT_ID/packages/pypi - poetry config http-basic.gitlab gitlab-ci-token "$CI_JOB_TOKEN" - poetry build - poetry publish --repository gitlab No newline at end of file README.md +57 −5 Original line number Diff line number Diff line Loading @@ -2,6 +2,14 @@ This repository contains a small python module to control the MS210 Channel Mixer by Advanced Illumination > **NOTE**: > > For linux user add your user to the *dialout* group to access the serial port > > ```bash > sudo usermod -aG dialout $USER > ``` ## Dependencies | Name | Version | Loading @@ -9,13 +17,57 @@ This repository contains a small python module to control the MS210 Channel Mixe | Python | >= 3.10 | | Poetry | >= 1.2.0 | ## Add package Install python dependencies: ```bash poetry install ``` TODO: ## App ## Linux For linux user add your user to the *dialout* group to access the serial port A QT based graphical interface to control the **MS210** is available and can be run with the following command: ```bash sudo usermod -aG dialout $USER poetry run controller ```  ## Add the MS210 package Instructions to add the MS210 package to your project ### Poetry Add a secondary source to your poetry project ```bash poetry source add -s igib https://labinfo.ing.he-arc.ch/gitlab/api/v4/projects/2417/packages/pypi/simple ``` Add **ms210** package ```bash poetry add ms210 --source igib ``` ### PIP ```bash pip install pyrsvp --index-url https://labinfo.ing.he-arc.ch/gitlab/api/v4/projects/2417/packages/pypi/simple/ ``` ## Usage ```python dev = MS210("/dev/ttyUSB0") # Set the red value success, msg = dev.set_value("R", 500) if not success: print(msg) # Get the green value green = dev.get_value("G") print(green) ``` No newline at end of file app/ms210_controller.py +12 −4 Original line number Diff line number Diff line import sys from typing import Literal from PyQt6.QtGui import QCloseEvent from PyQt6.QtWidgets import QApplication, QMainWindow, QMessageBox from ms210_ui import Ui_MainWindow from app.ms210_ui import Ui_MainWindow from ms210.driver import MS210, MS210InitFailed import serial.tools.list_ports Loading Loading @@ -141,9 +142,16 @@ class Window(QMainWindow, Ui_MainWindow): def error_msg(self, msg : str): QMessageBox.critical(None, "Error", msg) if "__main__" == __name__: def closeEvent(self, a0: QCloseEvent) -> None: self._ms210 = None return super().closeEvent(a0) def main(): app = QApplication(sys.argv) win = Window() win.show() sys.exit(app.exec()) if "__main__" == __name__: main() No newline at end of file img/ms210_ui.png 0 → 100644 +10.2 KiB Loading image diff... ms210/driver.py +31 −20 Original line number Diff line number Diff line # This module contains a class to control the MS210 device """This module contains a class to control the MS210 device """ import serial from typing import Literal from dataclasses import dataclass import serial _BAUD = 19200 _CHANNELS = ["IR", "R", "B", "G"] _MAX_VALUE = 1000 _MIN_VALUE = 0 @dataclass class Channel: """Holds channel attributes""" name: str index: int value: int class MS210InitFailed(Exception): pass """MS210 serial communication failed""" class MS210: """MS210 driver class""" def __init__(self, port: str = "/dev/ttyUSB0"): """Constructor of the MS210 object Loading Loading @@ -52,8 +59,7 @@ class MS210: raise MS210InitFailed(msg) def __del__(self): """Destructor """ """Destructor""" for channel in _CHANNELS: self.set_value(channel, 0) Loading Loading @@ -124,7 +130,9 @@ class MS210: return [x for x in self.channels if x.name == channel][0].value def set_value(self, channel: Literal["IR", "R", "B", "G"], value : int = 0) -> tuple[bool, str]: def set_value( self, channel: Literal["IR", "R", "B", "G"], value: int = 0 ) -> tuple[bool, str]: """Set the value of a channel Parameters Loading @@ -141,7 +149,10 @@ class MS210: """ if not self.__check_limits(value): return (False, f"Value {value} is out of bound. {_MIN_VALUE} < value < {_MAX_VALUE}") return ( False, f"Value {value} is out of bound. {_MIN_VALUE} < value < {_MAX_VALUE}", ) _channel = [x for x in self.channels if x.name == channel][0] if value == _channel.value: Loading Loading
.gitlab-ci.yml 0 → 100644 +67 −0 Original line number Diff line number Diff line # This file is a template, and might need editing before it works on your project. # This is a sample GitLab CI/CD configuration file that should run without any modifications. # It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts, # it uses echo commands to simulate the pipeline execution. # # A pipeline is composed of independent jobs that run scripts, grouped into stages. # Stages run in sequential order, but jobs within stages run in parallel. # # For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages # # You can copy and paste this template into a new `.gitlab-ci.yml` file. # You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. # # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: # https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml stages: # List of stages for jobs, and their order of execution - lint - deploy default: image: name: labinfo.ing.he-arc.ch:5050/igib/shared/ci-docker/poetry1.2.0-python3.10 entrypoint: [ "" ] lint-black: stage: lint tags: - docker rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always allow_failure: false script: - poetry install --only lint - poetry run black --check ms210/ lint-pylint: stage: lint tags: - docker rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always allow_failure: false script: - poetry install --only lint - poetry run pylint --fail-under=8 ms210/ deploy-job: # This job runs only when the merge is accepted stage: deploy # It only runs when *both* jobs in the test stage complete successfully. tags: - docker environment: production rules: - if: $CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push" when: always - when: never script: - poetry install --only main - poetry config repositories.gitlab https://labinfo.ing.he-arc.ch/gitlab/api/v4/projects/$CI_PROJECT_ID/packages/pypi - poetry config http-basic.gitlab gitlab-ci-token "$CI_JOB_TOKEN" - poetry build - poetry publish --repository gitlab No newline at end of file
README.md +57 −5 Original line number Diff line number Diff line Loading @@ -2,6 +2,14 @@ This repository contains a small python module to control the MS210 Channel Mixer by Advanced Illumination > **NOTE**: > > For linux user add your user to the *dialout* group to access the serial port > > ```bash > sudo usermod -aG dialout $USER > ``` ## Dependencies | Name | Version | Loading @@ -9,13 +17,57 @@ This repository contains a small python module to control the MS210 Channel Mixe | Python | >= 3.10 | | Poetry | >= 1.2.0 | ## Add package Install python dependencies: ```bash poetry install ``` TODO: ## App ## Linux For linux user add your user to the *dialout* group to access the serial port A QT based graphical interface to control the **MS210** is available and can be run with the following command: ```bash sudo usermod -aG dialout $USER poetry run controller ```  ## Add the MS210 package Instructions to add the MS210 package to your project ### Poetry Add a secondary source to your poetry project ```bash poetry source add -s igib https://labinfo.ing.he-arc.ch/gitlab/api/v4/projects/2417/packages/pypi/simple ``` Add **ms210** package ```bash poetry add ms210 --source igib ``` ### PIP ```bash pip install pyrsvp --index-url https://labinfo.ing.he-arc.ch/gitlab/api/v4/projects/2417/packages/pypi/simple/ ``` ## Usage ```python dev = MS210("/dev/ttyUSB0") # Set the red value success, msg = dev.set_value("R", 500) if not success: print(msg) # Get the green value green = dev.get_value("G") print(green) ``` No newline at end of file
app/ms210_controller.py +12 −4 Original line number Diff line number Diff line import sys from typing import Literal from PyQt6.QtGui import QCloseEvent from PyQt6.QtWidgets import QApplication, QMainWindow, QMessageBox from ms210_ui import Ui_MainWindow from app.ms210_ui import Ui_MainWindow from ms210.driver import MS210, MS210InitFailed import serial.tools.list_ports Loading Loading @@ -141,9 +142,16 @@ class Window(QMainWindow, Ui_MainWindow): def error_msg(self, msg : str): QMessageBox.critical(None, "Error", msg) if "__main__" == __name__: def closeEvent(self, a0: QCloseEvent) -> None: self._ms210 = None return super().closeEvent(a0) def main(): app = QApplication(sys.argv) win = Window() win.show() sys.exit(app.exec()) if "__main__" == __name__: main() No newline at end of file
ms210/driver.py +31 −20 Original line number Diff line number Diff line # This module contains a class to control the MS210 device """This module contains a class to control the MS210 device """ import serial from typing import Literal from dataclasses import dataclass import serial _BAUD = 19200 _CHANNELS = ["IR", "R", "B", "G"] _MAX_VALUE = 1000 _MIN_VALUE = 0 @dataclass class Channel: """Holds channel attributes""" name: str index: int value: int class MS210InitFailed(Exception): pass """MS210 serial communication failed""" class MS210: """MS210 driver class""" def __init__(self, port: str = "/dev/ttyUSB0"): """Constructor of the MS210 object Loading Loading @@ -52,8 +59,7 @@ class MS210: raise MS210InitFailed(msg) def __del__(self): """Destructor """ """Destructor""" for channel in _CHANNELS: self.set_value(channel, 0) Loading Loading @@ -124,7 +130,9 @@ class MS210: return [x for x in self.channels if x.name == channel][0].value def set_value(self, channel: Literal["IR", "R", "B", "G"], value : int = 0) -> tuple[bool, str]: def set_value( self, channel: Literal["IR", "R", "B", "G"], value: int = 0 ) -> tuple[bool, str]: """Set the value of a channel Parameters Loading @@ -141,7 +149,10 @@ class MS210: """ if not self.__check_limits(value): return (False, f"Value {value} is out of bound. {_MIN_VALUE} < value < {_MAX_VALUE}") return ( False, f"Value {value} is out of bound. {_MIN_VALUE} < value < {_MAX_VALUE}", ) _channel = [x for x in self.channels if x.name == channel][0] if value == _channel.value: Loading