diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 00000000..d9ee7902 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,22 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# We recommend specifying your dependencies to enable reproducible builds: +# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html +# python: +# install: +# - requirements: docs/requirements.txt \ No newline at end of file diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index f374e3ee..00000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,20 +0,0 @@ -# .readthedocs.yml -# Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details - -# Required -version: 2 - -# Build documentation in the docs/ directory with Sphinx -sphinx: - configuration: docs/conf.py - -# Optionally build your docs in additional formats such as PDF -formats: - - pdf - -# Optionally set the version of Python and requirements required to build your docs -python: - version: 3.7 - install: - - requirements: docs/requirements.txt \ No newline at end of file diff --git a/docs/develop/guide.rst b/docs/develop/guide.rst index 8d5b49ed..79367217 100644 --- a/docs/develop/guide.rst +++ b/docs/develop/guide.rst @@ -12,7 +12,7 @@ This document provides a guide for setting up a development environment for deve Hardware Connections #################### -For proper debugging capabilities you will need a Segger J-Link adapter. Unfortunately original J-Link adapters are quite expensive. A more affordable option is the J-Link EDU adapter at around $60 or the J-Link EDU mini adapter at around $20. In addition, there are J-Link clones that can be purchased for very low prices on ebay or Aliexpress. However, reliability of these clones is not guaranteed. +For proper debugging capabilities you will need a Segger J-Link adapter. Unfortunately original J-Link adapters are quite expensive. A more affordable option is the J-Link EDU adapter at around $60 or the J-Link EDU mini adapter at around $20. In addition, there are J-Link clones that can be purchased for very low prices on ebay or Aliexpress. However, reliability of these clones is not guaranteed, and we do not provide support when using these clones. With the board and J-Link adapter powered off, connect the J-Link to Tinymovr as shown below: @@ -42,7 +42,7 @@ Connection with SWD adapter (e.g. isolator) for R5: (diagrams made with `Wireviz `_) -As of Tinymovr 1.0.1, it is no longer necessary to patch the `JLinkDevices.xml` file with the PAC additions, in order for JLinkGDBServer to work properly. Relevant files are included in the repo. +As of Tinymovr 1.0.1, it is no longer necessary to patch the ``JLinkDevices.xml`` file with the PAC additions, in order for JLinkGDBServer to work properly. Relevant files are included in the repo. .. _develop-preparation: @@ -64,7 +64,7 @@ The file comes in a zipped installer exe (!), which all it does is extract the c cp -r /firmware/pac55xx_sdk/ -In any case, the target directory should be named `pac55xx_sdk`. The above copy command ensures this. +In any case, the target directory should be named ``pac55xx_sdk``. The above copy command ensures this. Now you have the required PAC SDK almost ready. There is a small patch that you will need to apply in the pac55xx_sdk directory. It is suggested to use the `Python patch package `_, which is cross-platform. If you do not have the package, install using pip: @@ -100,7 +100,7 @@ We take great pride in creating this in-house, cross-platform development soluti Configuring *********** -You'll need to configure the path to the JLink debug server in `.vscode/launch.json`. The configuration blocks look like this: +You'll need to configure the path to the JLink debug server in ``.vscode/launch.json``. The configuration blocks look like this: .. code-block:: javascript @@ -135,7 +135,7 @@ Select the board revision against which you are compiling from the list. The bui More about Hardware Revisions ----------------------------- -Tinymovr passes the `BOARD_REV_XX` argument regarding the board revision to the compiler. This is used in the firmware to configure the hardware accordingly. Invoking a make command with the REV argument is as follows: +Tinymovr passes the ``BOARD_REV_XX`` argument regarding the board revision to the compiler. This is used in the firmware to configure the hardware accordingly. Invoking a make command with the REV argument is as follows: .. code-block:: console @@ -157,7 +157,7 @@ Flashing and Debugging Before debugging, make sure the J-Link drivers and software is installed. The drivers and software, together with instructions, can be found in the `Qorvo website `_, under the download 'Segger J-Link Support'. This download includes a nacessary patch to enable J-Link to work with Qorvo devices. Instructions on how to apply the patch are included in the download. -The Tinymovr repo includes all VSCode settings configured, except for the JLink `serverpath` variable in `launch.json`, which you'll need to update to reflect your system. Note that there are multiple instances in the file, you'll need to update all of them. +The Tinymovr repo includes all VSCode settings configured, except for the JLink ``serverpath`` variable in ``launch.json``, which you'll need to update to reflect your system. Note that there are multiple instances in the file, you'll need to update all of them. We offer various VSCode launch configurations to suit different development and debugging tasks, including remote Tinymovr flashing debugging using a remote JLink server. These are are briefly outlined below. @@ -216,7 +216,7 @@ Eclipse is no longer supported. Consider :ref:`develop-setting-up-vscode` instea Setup Studio for Development ############################ -Tinymovr Studio is a Python application and as such can be easily set up to facilitate development. The approach is to use `pip` to install Tinymovr in develop mode, from a local copy of the (`git repo `_). This allows any changes you make to the local code to be immediately available when you run the executable (`tinymovr`, `tinymovr_cli`, or `tinymovr_dfu`). +Tinymovr Studio is a Python application and as such can be easily set up to facilitate development. The approach is to use ``pip`` to install Tinymovr in develop mode, from a local copy of the (``git repo ``_). This allows any changes you make to the local code to be immediately available when you run the executable (``tinymovr``, ``tinymovr_cli``, or ``tinymovr_dfu``). .. note:: We recommend installing Tinymovr in a virtual environment. `Here is a quick tutorial on how to setup a virtual environment using Conda `_. @@ -234,7 +234,28 @@ or to enable GUI support: pip3 install -e .[GUI] -This will install the Tinymovr Studio in develop mode. Now, `tinymovr`, `tinymovr_cli` and `tinymovr_dfu` will use the local Tinymovr Studio code. +This will install the Tinymovr Studio in develop mode. Now, ``tinymovr``, ``tinymovr_cl`i`` and ``tinymovr_dfu`` will use the local Tinymovr Studio code. Happy coding! + +Custom Device Definitions +######################### + +Tinymovr uses YAML files for device specification (the spec). Device spec files reside in ``{tinymovr_package_dir}/studio/Python/tinymovr/specs/``. This is where files covering Tinymovr firmware 1.3.x to 1.6.x are situated by default. + +You can add custom device spec files in this directory to tell Tinymovr Studio GUI, CLI and library how to communicate with custom devices. For instance, assuming that you have installed Tinymovr Studio using ``pip3``, you will need to find the directory of the tinymovr package first using the command below: + +Windows + +.. code-block:: console + + python -c "import os, tinymovr; print(os.path.dirname(tinymovr.__file__))" + +Macos and Linux + +.. code-block:: console + + python3 -c "import os, tinymovr; print(os.path.dirname(tinymovr.__file__))" + +Then, paste your custom spec file to ``{tinymovr_package_dir}/studio/Python/tinymovr/specs/``. Tinymovr should correctly discover your custom device. \ No newline at end of file diff --git a/docs/features/features.rst b/docs/features/features.rst index 161132c4..63c641d9 100644 --- a/docs/features/features.rst +++ b/docs/features/features.rst @@ -30,7 +30,7 @@ Before using the planner, the desired acceleration, deceleration and max velocit tm1.traj_planner.max_decel = {max_deceleration} # ticks/sec^2 tm1.traj_planner.max_vel = {mac_velocity} # ticks/sec -Once you set the desired acceleration and deceleration parameters, they do not need to be re-set. The parameters can be saved in NVRAM using `tmx.save_config()`. +Once you set the desired acceleration and deceleration parameters, they do not need to be re-set. The parameters can be saved in NVRAM using ``tmx.save_config()``. Once the parameters are set, you can execute a plan to a target position: @@ -70,7 +70,7 @@ Now the time-limited trajectory is executed as follows: Multi-axis Synchronization ******************************************** -Time-limited trajectories are useful for synchronizing the acceleration, cruise and deceleration phases for multiple axes. For instance, to synchronize three axes with different setpoints, you would configure the same `t_acc`, `t_cruise`, `t_dec` values. This guarantees that the trajectory phases are synchronized. Then you would issue the `move_to_tlimit` commands to each of the three different controllers in sequence: +Time-limited trajectories are useful for synchronizing the acceleration, cruise and deceleration phases for multiple axes. For instance, to synchronize three axes with different setpoints, you would configure the same ``t_acc``, ``t_cruise``, ``t_dec`` values. This guarantees that the trajectory phases are synchronized. Then you would issue the ``move_to_tlimit`` commands to each of the three different controllers in sequence: .. code-block:: python @@ -95,14 +95,14 @@ Because the homing planner relies on mechanical resistance of the structure, it There are six parameters in total that control the homing behavior: -* `tmx.homing.velocity`: The velocity at which the motor performs homing -* `tmx.homing.max_homing_t`: The maximum time the motor is allowed to travel before aborting homing -* `tmx.homing.retract_dist`: The retraction distance the motor travels after the endstop has been found -* `tmx.homing.stall_detect.velocity`: The velocity below which (and together with `stall_detect.delta_pos`) stall detection mode is triggered -* `tmx.homing.stall_detect.delta_pos`: The position error above which (and together with `stall_detect.velocity`) stall detection mode is triggered -* `tmx.homing.stall_detect.t`: The time to remain in stall detection mode before the motor is considered stalled +* ``tmx.homing.velocity``: The velocity at which the motor performs homing +* ``tmx.homing.max_homing_t``: The maximum time the motor is allowed to travel before aborting homing +* ``tmx.homing.retract_dist``: The retraction distance the motor travels after the endstop has been found +* ``tmx.homing.stall_detect.velocity``: The velocity below which (and together with ``stall_detect.delta_pos``) stall detection mode is triggered +* ``tmx.homing.stall_detect.delta_pos``: The position error above which (and together with ``stall_detect.velocity``) stall detection mode is triggered +* ``tmx.homing.stall_detect.t``: The time to remain in stall detection mode before the motor is considered stalled -In addition to the above, the phase current while the motor is stopped, until `stall_detect.t` time passes is the maximum allowed phase current, as defined in `tmx.controller.current.Iq_limit`. It is advisable to set this value so that mechanical damage or fatigue is avoided. +In addition to the above, the phase current while the motor is stopped, until ``stall_detect.t`` time passes is the maximum allowed phase current, as defined in ``tmx.controller.current.Iq_limit``. It is advisable to set this value so that mechanical damage or fatigue is avoided. Operation ********* @@ -125,7 +125,7 @@ FOC decouples the torque-producing and magnetizing currents by aligning the stat Two parameters control flux braking: -1. `tmx.controller.current.max_Ibus_regen`: The maximum current (in amperes) allowed to be fed back to the power source before flux braking activates. By adjusting this value, you can control the regenerative braking threshold and determine when flux braking should take effect. +1. ``tmx.controller.current.max_Ibus_regen``: The maximum current (in amperes) allowed to be fed back to the power source before flux braking activates. By adjusting this value, you can control the regenerative braking threshold and determine when flux braking should take effect. -2. `tmx.controller.current.max_Ibrake`: The maximum current (in amperes) allowed to be dumped to the motor windings during flux braking. By setting this value to zero, you can deactivate flux braking. Adjusting this parameter allows you to manage the braking torque and the heat generated during the braking process. +2. ``tmx.controller.current.max_Ibrake``: The maximum current (in amperes) allowed to be dumped to the motor windings during flux braking. By setting this value to zero, you can deactivate flux braking. Adjusting this parameter allows you to manage the braking torque and the heat generated during the braking process. diff --git a/docs/hardware/daisy_chain.png b/docs/hardware/daisy_chain.png new file mode 100644 index 00000000..f101bb82 Binary files /dev/null and b/docs/hardware/daisy_chain.png differ diff --git a/docs/hardware/guide.rst b/docs/hardware/guide.rst index 9fba5d5f..59aaff11 100644 --- a/docs/hardware/guide.rst +++ b/docs/hardware/guide.rst @@ -137,4 +137,15 @@ Connecting Power With the power source off/disconnected, connect the power leads observing correct polarity. Turn on/connect the power source. Upon successful power-up, the onboard LED should light up. .. note:: - Each Tinymovr board has a capacitance of around 500μF (R3.x) / 200μF (R5) / 50μF (M5). Such capacitance can introduce significant inrush current upon power-on, especially if several boards are connected to the same power supply. To prevent damage to components from overcurrent, the use of an inrush current limiter or a current-limited power supply is advised. We offer a `Power Distribution and protection device `_ which is suitable for this task. + Each Tinymovr board has a capacitance of around 500μF (R3.x) / 240μF (R5) / 50μF (M5). Such capacitance can introduce significant inrush current upon power-on, especially if several boards are connected to the same power supply. To prevent damage to components from overcurrent, the use of an inrush current limiter or a current-limited power supply is advised. We offer a `Power Distribution and protection device `_ which is suitable for this task. + +.. _daisy-chain: + +Connecting Multiple Nodes (Daisy-Chaining) +########################################## + +Multiple nodes can be connected in a single CAN Bus network by means of daisy-chaining. Tinymovr R5 and Tinymovr M5 offer two CAN Bus ports for this purpose, which makes it easy to daisy-chain units. Note that for networks with long cable lengths, you may need termination on both ends. On one end this can be achieved with the onboard termination resistor of CANine, but on the other end you will have to provide your own. + +.. image:: daisy_chain.png + :width: 800 + :alt: Connecting multiple nodes by daisy-chaining \ No newline at end of file diff --git a/docs/sensors/sensors.rst b/docs/sensors/sensors.rst index 4911997c..b00a4d9c 100644 --- a/docs/sensors/sensors.rst +++ b/docs/sensors/sensors.rst @@ -100,7 +100,7 @@ At this point, you are ready to perform motor/sensor calibration. This will meas .. code-block:: python - tmx.calibrate() + tmx.controller.calibrate() After calibration finishes, you should be able to control the motor: @@ -115,6 +115,6 @@ Once you have determined that the motor behaves as expected, set to idle and per .. code-block:: python - tmx.idle() + tmx.controller.idle() tmx.save_config() diff --git a/docs/upgrade/upgrade.rst b/docs/upgrade/upgrade.rst index e62a0661..fa1b6f73 100644 --- a/docs/upgrade/upgrade.rst +++ b/docs/upgrade/upgrade.rst @@ -72,6 +72,24 @@ Flashing the Firmware Ensure your CAN interface is connected and configured correctly with Tinymovr. +2. **Enter DFU Mode**: + + Launch the Tinymovr CLI: + + .. code-block:: bash + + tinymovr_cli + + Once inside teh CLI, type the following to enter DFU Mode: + + .. code-block:: python + + tmx.enter_dfu() + + Then exit the CLI by typing ``exit``. + + If you are getting an exception after this command, it means that either your Tinymovr does not come with the DFU bootloader, so you will have to :ref:`upgrade-using-activeflashlight`, or your board is already in DFU mode. + 2. **Run the Script**: With the firmware .bin file you downloaded, execute the DFU script: diff --git a/studio/Python/tests/test_base_function.py b/studio/Python/tests/test_base_function.py index c204587d..a56c6837 100644 --- a/studio/Python/tests/test_base_function.py +++ b/studio/Python/tests/test_base_function.py @@ -22,15 +22,17 @@ def test_position_control(self): Test position control """ self.check_state(0) + self.tm.motor.I_cal = 5 + self.tm.controller.current.Iq_limit = 5 self.try_calibrate() self.tm.controller.position_mode() self.check_state(2) for i in range(5): - self.tm.controller.position.setpoint = i * 10000 * ticks + self.tm.controller.position.setpoint = i * 3000 * ticks time.sleep(0.25) self.assertAlmostEqual( - i * 10000 * ticks, self.tm.encoder.position_estimate, delta=1000 * ticks + i * 3000 * ticks, self.tm.encoder.position_estimate, delta=1000 * ticks ) diff --git a/studio/Python/tests/test_dfu.py b/studio/Python/tests/test_dfu.py index 344e6a54..c560b576 100644 --- a/studio/Python/tests/test_dfu.py +++ b/studio/Python/tests/test_dfu.py @@ -22,8 +22,7 @@ from tinymovr import init_tee, destroy_tee from tinymovr.config import ( get_bus_config, - create_device, - definitions + create_device ) import unittest diff --git a/studio/Python/tinymovr/cli.py b/studio/Python/tinymovr/cli.py index 3c70a9d7..03a7ea16 100644 --- a/studio/Python/tinymovr/cli.py +++ b/studio/Python/tinymovr/cli.py @@ -1,16 +1,18 @@ """Tinymovr Studio CLI Usage: - tinymovr_cli [--bus=] [--chan=] [--bitrate=] + tinymovr_cli [--bus=] [--chan=] [--spec=] [--bitrate=] tinymovr_cli -h | --help tinymovr_cli --version Options: --bus= One or more interfaces to use, first available is used [default: canine,slcan_disco]. --chan= The bus device "channel". + --spec= A custom device spec to be added to the list of discoverable spec. --bitrate= CAN bitrate [default: 1000000]. """ +import yaml import can import pkg_resources import IPython @@ -20,7 +22,7 @@ from tinymovr import init_tee, destroy_tee from tinymovr.discovery import Discovery from tinymovr.constants import app_name -from tinymovr.config import get_bus_config, configure_logging +from tinymovr.config import get_bus_config, configure_logging, add_spec """ Tinymovr CLI Module @@ -49,6 +51,12 @@ def spawn(): logger = configure_logging() + spec_file = arguments["--spec"] + if spec_file: + with open(spec_file, 'r') as file: + spec_data = yaml.safe_load(file) + add_spec(spec_data, logger) + buses = arguments["--bus"].rsplit(sep=",") channel = arguments["--chan"] bitrate = int(arguments["--bitrate"]) diff --git a/studio/Python/tinymovr/config/__init__.py b/studio/Python/tinymovr/config/__init__.py index f48375b6..87c5bf66 100644 --- a/studio/Python/tinymovr/config/__init__.py +++ b/studio/Python/tinymovr/config/__init__.py @@ -1,8 +1,7 @@ from tinymovr.config.config import ( get_bus_config, configure_logging, - definitions, create_device, create_device_with_hash_msg, - ProtocolVersionError, + add_spec, ) diff --git a/studio/Python/tinymovr/config/config.py b/studio/Python/tinymovr/config/config.py index 91dc61d0..28ab4698 100644 --- a/studio/Python/tinymovr/config/config.py +++ b/studio/Python/tinymovr/config/config.py @@ -25,29 +25,30 @@ from tinymovr.codec import DataType from tinymovr.channel import CANChannel -definitions = {"hash_uint32": {}, "name": {}} - -for yaml_file in Path(files("tinymovr").joinpath("specs/")).glob("*.yaml"): - with open(str(yaml_file)) as def_raw: - definition = yaml.safe_load(def_raw) - tmp_node = deserialize(definition) - definitions["hash_uint32"][tmp_node.hash_uint32] = definition - definitions["name"][definition["name"]] = definition - - -class ProtocolVersionError(Exception): - def __init__(self, dev_id, version_str, *args, **kwargs): - self.dev_id = dev_id - self.version_str = cleanup_incomplete_version(version_str) - msg = ( - "Incompatible protocol versions (hash mismatch) for device {}. " - "Firmware is compatible with Studio version {}.\n\n" - "Either upgrade studio and firmware, or install a compatible Studio version like so:\n\n" - "pip3 uninstall tinymovr\npip3 install tinymovr=={}".format( - self.dev_id, self.version_str, self.version_str - ) - ) - super().__init__(msg, *args, **kwargs) +specs = {"hash_uint32": {}} + + +def init_specs_dict(): + global specs + for yaml_file in Path(files("tinymovr").joinpath("specs/")).glob("*.yaml"): + with open(str(yaml_file)) as def_raw: + spec = yaml.safe_load(def_raw) + add_spec(spec) + + +def add_spec(spec, logger=None): + if logger is None: + logger = logging.getLogger("tinymovr") + + tmp_node = deserialize(spec) + hash_value = tmp_node.hash_uint32 + if hash_value in specs["hash_uint32"]: + logger.warning("Provided spec with hash {} already exists in hash/name dictionary".format(hash_value)) + else: + specs["hash_uint32"][hash_value] = spec + + +init_specs_dict() def get_bus_config(suggested_types=None): @@ -70,24 +71,22 @@ def create_device(node_id): """ chan = CANChannel(node_id) - # Temporarily using a default definition to get the protocol_hash - # This assumes that `protocol_hash` is standard across different definitions - # Get the first definition as a temp - tmp_definition = list(definitions["hash_uint32"].values())[0] - node = deserialize(tmp_definition) + # Temporarily using a default spec to get the protocol_hash + # This assumes that `protocol_hash` is standard across different specs + # Get the first spec as a temp + tmp_spec = list(specs["hash_uint32"].values())[0] + node = deserialize(tmp_spec) node._channel = chan - # Check for the correct definition using the remote hash + # Check for the correct spec using the remote hash protocol_hash = node.protocol_hash - device_definition = definitions["hash_uint32"].get(protocol_hash) + device_spec = specs["hash_uint32"].get(protocol_hash) - if not device_definition: - raise ValueError(f"No device definition found for hash {protocol_hash}.") + if not device_spec: + raise ValueError(f"No device spec found for hash {protocol_hash}.") - node = deserialize(device_definition) + node = deserialize(device_spec) node._channel = chan - if node.hash_uint32 != protocol_hash: - raise ProtocolVersionError(node_id, "") return node @@ -101,17 +100,12 @@ def create_device_with_hash_msg(heartbeat_msg): chan = CANChannel(node_id) hash, *_ = chan.serializer.deserialize(heartbeat_msg.data[:4], DataType.UINT32) - device_definition = definitions["hash_uint32"].get(hash) + device_spec = specs["hash_uint32"].get(hash) - if not device_definition: - raise ValueError(f"No device definition found for hash {hash}.") + if not device_spec: + raise ValueError(f"No device spec found for hash {hash}.") - node = deserialize(device_definition) - if node.hash_uint32 != hash: - version_str = "".join([chr(n) for n in heartbeat_msg.data[4:]]) - if not version_str.strip(): - version_str = "1.3.1" - raise ProtocolVersionError(node_id, version_str) + node = deserialize(device_spec) node._channel = chan return node diff --git a/studio/Python/tinymovr/discovery.py b/studio/Python/tinymovr/discovery.py index 4a643354..f4f946f0 100644 --- a/studio/Python/tinymovr/discovery.py +++ b/studio/Python/tinymovr/discovery.py @@ -21,7 +21,7 @@ from tinymovr.channel import ResponseError from tinymovr.tee import get_tee from tinymovr.constants import HEARTBEAT_BASE -from tinymovr.config import create_device_with_hash_msg, ProtocolVersionError +from tinymovr.config import create_device_with_hash_msg class Discovery: @@ -72,7 +72,7 @@ def _recv_cb(self, frame): self._append_to_queue((node, node_id)) except ResponseError as e: self.logger.error(e) - except ProtocolVersionError as e: + except ValueError as e: self.logger.error(e) self.incompatible_nodes.add(node_id) self.pending_nodes.remove(node_id) diff --git a/studio/Python/tinymovr/gui/gui.py b/studio/Python/tinymovr/gui/gui.py index 0d1902ca..99539b00 100644 --- a/studio/Python/tinymovr/gui/gui.py +++ b/studio/Python/tinymovr/gui/gui.py @@ -1,23 +1,26 @@ """Tinymovr Studio GUI Usage: - tinymovr [--bus=] [--chan=] [--bitrate=] [--max-timeouts=] + tinymovr [--bus=] [--chan=] [--spec=] [--bitrate=] [--max-timeouts=] tinymovr -h | --help tinymovr --version Options: --bus= One or more interfaces to use, first available is used [default: canine,slcan_disco]. --chan= The bus device "channel". + --spec= A custom device spec to be added to the list of discoverable specs. --bitrate= CAN bitrate [default: 1000000]. --max-timeouts= Max timeouts before nodes are rescanned [default: 5]. """ import sys +import yaml import pkg_resources from docopt import docopt from PySide6.QtWidgets import QApplication from tinymovr.gui import MainWindow, app_stylesheet, app_stylesheet_dark, is_dark_mode from tinymovr.constants import app_name +from tinymovr.config import configure_logging, add_spec """ @@ -41,11 +44,20 @@ def spawn(): version = pkg_resources.require("tinymovr")[0].version arguments = docopt(__doc__, version=app_name + " " + str(version)) + + logger = configure_logging() + + spec_file = arguments["--spec"] + if spec_file: + with open(spec_file, 'r') as file: + spec_data = yaml.safe_load(file) + add_spec(spec_data, logger) + app = QApplication(sys.argv) if is_dark_mode(): app.setStyleSheet(app_stylesheet_dark) else: app.setStyleSheet(app_stylesheet) - w = MainWindow(app, arguments) + w = MainWindow(app, arguments, logger) w.show() sys.exit(app.exec_()) diff --git a/studio/Python/tinymovr/gui/window.py b/studio/Python/tinymovr/gui/window.py index 6d7b2659..bee0cf0e 100644 --- a/studio/Python/tinymovr/gui/window.py +++ b/studio/Python/tinymovr/gui/window.py @@ -16,6 +16,7 @@ """ import time +import logging import pkg_resources from contextlib import suppress import json @@ -37,7 +38,7 @@ import pyqtgraph as pg from tinymovr.constants import app_name from tinymovr.channel import ResponseError as ChannelResponseError -from tinymovr.config import get_bus_config, configure_logging +from tinymovr.config import get_bus_config from avlos import get_registry from avlos.json_codec import AvlosEncoder from tinymovr.gui import ( @@ -58,14 +59,17 @@ class MainWindow(QMainWindow): TreeItemCheckedSignal = Signal(dict) - def __init__(self, app, arguments): + def __init__(self, app, arguments, logger): super(MainWindow, self).__init__() # set units default format get_registry().default_format = ".6f~" self.start_time = time.time() - self.logger = configure_logging() + if logger is None: + self.logger = logging.getLogger("tinymovr") + else: + self.logger = logger self.attr_widgets_by_id = {} self.graphs_by_id = {}