Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the internal package structure and change how driver classes inherit functionality #333

Merged
merged 56 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
84c6f3c
ci: Update testing/linting to use official Python 3.13 versions
nfelt14 Oct 10, 2024
6159c95
refactor: Converted the SignalGenerator class into a mixin class call…
nfelt14 Oct 10, 2024
e37c44a
refactor: Remove previously deprecated write_buffers() method
nfelt14 Oct 10, 2024
dbadfcf
refactor: Refactor the TekScope inheritance to remove methods from th…
nfelt14 Oct 10, 2024
3cb5734
refactor: Moved the REST API class into a mixin.
nfelt14 Oct 11, 2024
9f0194f
refactor: Moved the Device, PIDevice, and TSPDevice class into device…
nfelt14 Oct 11, 2024
19a6e63
refactor: Remove deprecated `DEVICE_DRIVER_MODEL_MAPPING` constant.
nfelt14 Oct 11, 2024
7f2a399
refactor: Updated most device types to no longer inherit from the con…
nfelt14 Oct 11, 2024
537df72
docs: Updated docs with the new references to the refactored object l…
nfelt14 Oct 11, 2024
c6b49f5
refactor: Checkpoint commit during the process of converting the PIDe…
nfelt14 Oct 12, 2024
f9e6c60
refactor: Checkpoint commit during the process of converting the PIDe…
nfelt14 Oct 14, 2024
c0d589b
refactor: First revision completed of converting the PIDevice/TSPDevi…
nfelt14 Oct 14, 2024
237a712
refactor: Converted the verify_values(), raise_error(), and raise_fai…
nfelt14 Oct 14, 2024
f048e71
refactor: Updated the ReadOnlyCachedProperty decorator to be able to …
nfelt14 Oct 14, 2024
1a5fb36
refactor: Simplified the sharing of common method implementations by …
nfelt14 Oct 14, 2024
a84abe4
refactor: Removed the get_eventlog_status() method and replaced it wi…
nfelt14 Oct 14, 2024
b5ec0bf
refactor: Removed the device_type_classes.py file since it was no lon…
nfelt14 Oct 15, 2024
35129c9
docs: Added more detail to the architecture document page.
nfelt14 Oct 15, 2024
6445a08
test: Fix test for DeviceTypes enum that verifies all device types ar…
nfelt14 Oct 15, 2024
c5d9087
ci: Use python -m poetry when installing the package dependencies dur…
nfelt14 Oct 15, 2024
0df48db
refactor: Checkpoint commit during the refactor of the expect_esr() m…
nfelt14 Oct 16, 2024
6564f12
refactor: Checkpoint commit during the refactor of the expect_esr() m…
nfelt14 Oct 16, 2024
3f41332
refactor: Converted the expect_esr() method into a final method that …
nfelt14 Oct 16, 2024
ff418ac
test: Refactored the test for proper driver inheritance and abstracti…
nfelt14 Oct 16, 2024
deeac5f
docs: Update inheritance diagrams and fix an abstract class that wasn…
nfelt14 Oct 16, 2024
9aec0c6
fix: Updated the hostname lookup to use concurrent.futures to impleme…
nfelt14 Oct 16, 2024
301e7ab
refactor: Update TODO comments and remove an unneeded dependency
nfelt14 Oct 17, 2024
e95f3a4
refactor: Made a handful of the new mixins private since they don't r…
nfelt14 Oct 17, 2024
6b35233
refactor: Converted the private class constant `_DEVICE_TYPE` into an…
nfelt14 Oct 17, 2024
5d2d667
refactor: checkpoint commit in the process of removing the unneeded i…
nfelt14 Oct 18, 2024
3a87809
docs: Update the two contributing guides based on the new folder stru…
nfelt14 Oct 18, 2024
c93ab4e
docs: Update signal_generators.md to include links to the API documen…
nfelt14 Oct 21, 2024
c3f17ab
docs: Update custom template with newer code to enable rendering clas…
nfelt14 Oct 21, 2024
a318ad2
refactor: Re-generated the commands subpackage to use the new class s…
nfelt14 Oct 21, 2024
e9c698d
docs: Update the changelog
nfelt14 Oct 21, 2024
5f7812e
refactor: Fixes after pulling latest main branch
nfelt14 Oct 21, 2024
7dda7f6
chore: Update development dependencies
nfelt14 Oct 21, 2024
e694945
chore: Update development dependencies
nfelt14 Oct 21, 2024
0d135ca
chore: Fixes from pull request review
nfelt14 Oct 21, 2024
064ce8f
chore: Fixes from pull request review
nfelt14 Oct 21, 2024
ce752b2
chore: Removed some unnecessary init methods
nfelt14 Oct 21, 2024
2e0315e
chore(python-deps): Remove safety-schemas pinned version since the br…
nfelt14 Oct 22, 2024
5549a05
docs: Add macro to docs for converting github flavored markdown alert…
nfelt14 Oct 22, 2024
42219f1
docs: Made breaking change callout in changelog bold and italics
nfelt14 Oct 22, 2024
87a7e34
Merge remote-tracking branch 'origin/main' into inheritance-rearchite…
nfelt14 Oct 23, 2024
493413c
docs: Update changelog
nfelt14 Oct 23, 2024
c1f0dab
docs: Update changelog
nfelt14 Oct 23, 2024
112bffb
docs: Switch to lawngreen for the inheritance tree color
nfelt14 Oct 24, 2024
1b06457
Merge remote-tracking branch 'origin/main' into inheritance-rearchite…
nfelt14 Oct 24, 2024
5442f36
refactor: Update code and comments after reviewing pull request
nfelt14 Oct 28, 2024
fb4ff16
Merge remote-tracking branch 'origin/main' into inheritance-rearchite…
nfelt14 Oct 28, 2024
5bbdd0c
ci: Update testing dependency to remove vulnerability
nfelt14 Oct 28, 2024
079bc45
fix: Fix example to use proper SCPI syntax
nfelt14 Oct 29, 2024
66d1fd1
docs: Change the note by the device inheritance tree to simply highli…
nfelt14 Oct 29, 2024
8f7a119
chore: Merge conflicts
nfelt14 Oct 30, 2024
b1948ba
docs: Fix changelog after merge conflicts
nfelt14 Oct 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 2 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ repos:
- id: remove-tabs
- id: forbid-tabs
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: aa1acdb72677dfbc5f507d2dfd45d8380bbcc2e0 # frozen: 0.29.3
rev: 37cd56d9d154dfb0648eaee8efc1040512700c47 # frozen: 0.29.4
hooks:
- id: check-readthedocs
- id: check-dependabot
Expand All @@ -60,11 +60,6 @@ repos:
rev: 8072181c0f2eab9f2dd8db2eb3b9556d7cd0bd74 # frozen: 1.17.0
hooks:
- id: yamlfix
# TODO: get this working
# - repo: https://github.com/motet-a/jinjalint
# rev: "0.5"
# hooks:
# - id: jinjalint
- repo: https://github.com/thibaudcolas/curlylint
rev: 71adf4d34c290684fd9f94a4d21ac55bcfe640f0 # frozen: v0.13.1
hooks:
Expand Down Expand Up @@ -138,7 +133,7 @@ repos:
always_run: true
args: [audit, --json, --ignore-code=CVE-2019-8341]
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: 75b98813cfb7e663870a28c74366a1e99d7bfe79 # frozen: v0.6.9
rev: 8983acb92ee4b01924893632cf90af926fa608f0 # frozen: v0.7.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
45 changes: 40 additions & 5 deletions CHANGELOG.md
nfelt14 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,43 @@ Things to be included in the next release go here.

### Added

- `collectgarbage()` is now called during cleanup of `TSPDevice` children.
- Added USB Support for AFG31K and MDO3 models.
- `collectgarbage()` is now called during cleanup of `TSPControl` children.
- Added USBTMC Support for the AFG31K and MDO3 drivers.
- Testing/linting on Python 3.13.
- Added the `get_errors()` method to the `Device` class to enable easy access to the current error code and messages on any device.
- Added more details to the Architectural Overview page of the documentation as well as highlighting to the device driver diagram on the page.
- Added regex matching to the `verify_values()` helper function to allow for more flexible value verification.

### Changed

NOTE: Despite all the officially breaking changes, the actual drivers were only affected in
very minor ways. The primary impact to the drivers was simply the removal of previously
deprecated functionality. Almost all changes only impacted the internal workings of `tm_devices`.
However, please read through all changes to be aware of what may potentially impact your code.

- _**<span style="color:orange">minor breaking change</span>**_: Moved `SignalGenerator` class to the `driver_mixins` submodule and renamed it to `_TektronixPIAFGAWGMixin` (also made it a private mixin).
- _**<span style="color:orange">minor breaking change</span>**_: Renamed the `PIDevice`, `TSPDevice`, and `RESTAPIDevice` classes to `PIControl`, `TSPControl`, and `RESTAPIControl` respectively.
- _**<span style="color:orange">minor breaking change</span>**_: Moved the `PIControl`, `TSPControl`, and `RESTAPIControl` classes into a mixin folder so that they can be used as mixins rather than being part of the required inheritance structure.
- In order to use these control mixins, they must be inherited at the family base class level in the driver hierarchy, along with the device type class (or any class that inherits from the base `Device` class and defines a `device_type` property and the other required abstract property implementations).
- Due to this change, it is recommended that the specific device driver (or at least the family base class) for the device being controlled is used for type hinting.
- _**<span style="color:orange">minor breaking change</span>**_: Moved all device type subpackages (AWGs, AFGs, Scopes, SMUs, etc.) up to the top level of the `drivers` subpackage.
- _**<span style="color:orange">minor breaking change</span>**_: Converted all family base classes to inherit from the device control mixins.
- _**<span style="color:red">BREAKING CHANGE</span>**_: Renamed the `get_eventlog_status()` method to `_get_errors()` and made it a required, abstract method for all devices to implement.
- To get similar functionality to the previous `get_eventlog_status()` method, switch to using the new `get_errors()` method.
- _**<span style="color:red">BREAKING CHANGE</span>**_: Changed the behavior of the `expect_esr()` method to expect an integer error code input and an optional tuple of error messages to compare against the actual error code and messages returned by the `_get_errors()` private method.
- _**<span style="color:orange">minor breaking change</span>**_: Converted the `device_type` property into an abstract, cached property to force all children of the `Device` class to specify what type of device they are.
- Updated the auto-generated command mixin classes to no longer use an `__init__()` method to enable the driver API documentation to render in a more usable way.

### Removed

- _**<span style="color:red">BREAKING CHANGE</span>**_: Removed previously deprecated `TekScopeSW` alias to the `TekScopePC` class
- _**<span style="color:red">BREAKING CHANGE</span>**_: Removed previously deprecated `write_buffers()` from the `TSPControl` class.
- _**<span style="color:red">BREAKING CHANGE</span>**_: Removed Internal AFG methods from the `TekScopePC` driver, since they wouldn't have worked due to its lack of an IAFG.
- _**<span style="color:red">BREAKING CHANGE</span>**_: Removed previously deprecated `DEVICE_DRIVER_MODEL_MAPPING` constant.
- _**<span style="color:red">BREAKING CHANGE</span>**_: Removed the `DEVICE_TYPE_CLASSES` constant and the `device_type_classes.py` module.
- _**<span style="color:red">BREAKING CHANGE</span>**_: Removed many hacky implementations of `total_channels` and `all_channel_names_list` properties from drivers that don't need them anymore.
- _**<span style="color:red">BREAKING CHANGE</span>**_: Removed the `verify_values()`, `raise_failure()`, and `raise_error()` methods from all device drivers.
- These methods have been converted to helper functions and can be imported from the `tm_devices.helpers` subpackage now.

---

Expand Down Expand Up @@ -185,11 +220,11 @@ Things to be included in the next release go here.

### Changed

- <span style="color:red">BREAKING CHANGE</span>. Changed the term "signal source" to "signal generator".
- _**<span style="color:red">BREAKING CHANGE</span>**_. Changed the term "signal source" to "signal generator".
- All uses of this term are changed. Import paths now use `signal_generator` instead of `signal_source`.
- <span style="color:red">BREAKING CHANGE</span>. Changed the function name of `generate_waveform()` to `generate_function()`.
- _**<span style="color:red">BREAKING CHANGE</span>**_. Changed the function name of `generate_waveform()` to `generate_function()`.
- `generate_waveform()` only exists on AWGs now, however the functionality is entirely changed.
- <span style="color:red">BREAKING CHANGE</span>. Changed the `generate_function()` function by removing burst functionality.
- _**<span style="color:red">BREAKING CHANGE</span>**_. Changed the `generate_function()` function by removing burst functionality.
- Any use of burst now must use `setup_burst()` and `generate_burst()` instead.
- Updated AWGs such that the `family_base_class` is at the series level.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ This template iterates on members of a given object and renders them.
It can group members by category (attributes, classes, functions, modules) or render them in a flat list.

Context:
obj (griffe.dataclasses.Object): The object to render.
obj (griffe.Object): The object to render.
config (dict): The configuration options.
root_members (bool): Whether the object is the root object.
heading_level (int): The HTML heading level to use.
Expand All @@ -31,7 +31,7 @@ Context:
</table>
{%- endmacro -%}

{% if obj.members %}
{% if obj.all_members %}
{% block logs scoped %}
{#- Logging block.

Expand Down Expand Up @@ -127,7 +127,7 @@ Context:
{% endif %}
{% with heading_level = heading_level + extra_level %}
{% for attribute in attributes|order_members(config.members_order, members_list) %}
{% if members_list is not none or attribute.is_public %}
{% if members_list is not none or (not attribute.is_imported or attribute.is_public) %}
{% include attribute|get_template with context %}
{% endif %}
{% endfor %}
Expand All @@ -147,7 +147,7 @@ Context:
{% endif %}
{% with heading_level = heading_level + extra_level %}
{% for class in classes|order_members(config.members_order, members_list) %}
{% if members_list is not none or class.is_public %}
{% if members_list is not none or (not class.is_imported or class.is_public) %}
{% include class|get_template with context %}
{% endif %}
{% endfor %}
Expand All @@ -168,7 +168,7 @@ Context:
{% with heading_level = heading_level + extra_level %}
{% for function in functions|order_members(config.members_order, members_list) %}
{% if not (obj.kind.value == "class" and function.name == "__init__" and config.merge_init_into_class) %}
{% if members_list is not none or function.is_public %}
{% if members_list is not none or (not function.is_imported or function.is_public) %}
{% include function|get_template with context %}
{% endif %}
{% endif %}
Expand All @@ -189,8 +189,8 @@ Context:
{% filter heading(heading_level, id=html_id ~ "-modules") %}Modules{% endfilter %}
{% endif %}
{% with heading_level = heading_level + extra_level %}
{% for module in modules|order_members(config.members_order, members_list) %}
{% if members_list is not none or module.is_public %}
{% for module in modules|order_members(config.members_order.alphabetical, members_list) %}
{% if members_list is not none or (not module.is_alias or module.is_public) %}
{% include module|get_template with context %}
{% endif %}
{% endfor %}
Expand Down
79 changes: 57 additions & 22 deletions docs/advanced/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,16 @@ information about the device, and methods, which provide various functionality.

The [`DeviceManager`][tm_devices.DeviceManager] uses a configuration parser
([`DMConfigParser`][tm_devices.components.DMConfigParser]) to read in
connection information from an optional config file as well as to store
connection information that is provided directly via python code.
connection information from an optional [config file](../configuration.md#config-file) or
[environment variable](../configuration.md#environment-variable) as well as to store
connection information that is provided directly via [python code](../configuration.md#python-code).
This information contains the device type (e.g., SCOPE, AFG, SMU) as well as the address
(e.g., hostname, IP address, or model and serial number for USBTMC). The config parser also reads in
optional configuration settings that apply globally, such as turning on verbose VISA logging. The
[`DeviceManager`][tm_devices.DeviceManager] then connects to all the devices that the config parser
has listed and provides access to the [Python driver](#device-drivers) for each device. The
Python device driver is the class responsible for actual communication with the physical
(or virtual) device.

### Block Diagram

Expand All @@ -32,35 +40,62 @@ classDiagram

---

## Main device types
## Device Types

There are currently 9 different supported device types: **Scopes**, **Arbitrary
Waveform Generators (AWGs)**, **Arbitrary Function Generators (AFGs)**, **Source
Measure Units (SMUs)**, **Power Supplies (PSUs)**, **Data Acquisition Systems
(DAQs)**, **Digital Multimeters (DMMs)**, **Systems Switches (SSs)**, and
**Margin Testers**.
These are the currently supported device types:

The driver class structure uses inheritance, which means that common attributes
and methods can be defined in higher-level classes (sometimes called parent
classes) and inherited by subclasses (sometimes called child classes), to reduce
the lines of code needed to create new device drivers.

Every single device driver within a particular device type is guaranteed to have
the same class signature (attribute and method names). Often times specific
device drivers will need to implement the functionality for specific methods or
even overwrite inherited functionality due to the differences that can occur
between different models of the same device type.
1. Arbitrary Function Generators (AFGs)
2. Arbitrary Waveform Generators (AWGs)
3. Data Acquisition Systems (DAQs)
4. Digital Multimeters (DMMs)
5. Margin Testers
6. Power Supplies (PSUs)
7. Scopes (Oscilloscopes)
8. Source Measure Units (SMUs)
9. Systems Switches

### Block Diagram

{{ auto_class_diagram('tm_devices.drivers.device_type_classes', full=True, namespace='tm_devices.drivers') }}
{{ auto_class_diagram('tm_devices.drivers.device.Device', full=False, namespace='tm_devices.drivers', tree_direction='down') }}

---

## All device drivers
## Device Drivers

This package supports many devices, zoom in to see them all!
### Object-Oriented Design Principles

The drivers are the biggest part of this package; each unique series of instrument gets a uniquely
named Python driver class (see [`tm_devices.drivers`][tm_devices.drivers] for the list of
available driver classes). These classes are implemented using object-oriented programming
principles and make extensive use of inheritance. This allows common attributes and methods to be
defined in higher-level, abstract classes that are then inherited by the individual driver classes.

Mixin classes are also used to provide shared implementations and abstract method signatures for the
drivers. This allows multiple device types to all inherit from the same abstract class (mixin class)
without sharing the exact same inheritance tree (since they might not share much else). The mixin
classes define attributes and methods, some of which the driver will overwrite or implement for itself.

### Family Base Classes

One other technique for enabling code reuse in the drivers is by using a “family base class”. Each
family base class is an abstract class that defines the signature for all child classes that
inherit from it. Occasionally a child class will need to overwrite an implementation due to the
differences that can occur between different models of the same device type, but the class signature
will not change. This means that all drivers that inherit from a common family base class are
guaranteed to have the same class signature (attributes, methods, etc.), and this rule is enforced by unit tests.

### Object-Oriented Design Benefits

These object-oriented principles result in individual driver classes that are quite simple to
read, as they have very few actual lines of code. Code duplication is extremely low, which makes
drivers easy to create and update.

### Block Diagram

{{ auto_class_diagram('tm_devices.drivers', full=True, namespace='tm_devices.drivers') }}
This package supports many devices, zoom in to see them all!

!!! note
- Family Base Classes are outlined in **<span style="color: orangered;">orange red</span>**.
- Device Drivers are highlighted in **<span style="background-color: lawngreen;">lawn green</span>**.

{{ auto_class_diagram('tm_devices.drivers', full=True, namespace='tm_devices.drivers', highlight_family_base_classes=True, highlight_device_drivers=True, chart_direction='LR') }}
Loading
Loading