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

Device interface dev #118

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from

Conversation

vasilisniaouris
Copy link
Collaborator

This is the beginning of a new interface class that will be used throughout the project. The goal of creating such an interface is to automate the introduction of new devices to already existing experiments and applications.

Throughout the following I will inadvertently mention problems that may related to different qt3-utils issues. I will do my best to edit this intro message to mention each of the issues at a later time. For now, I want this pull request to serve as a discussion space of how to implement such a big project that is device interfacing (if that is the appropriate way to do it). Happy to move the discussion elsewhere if it is dimmed necessary.

The structure I chose is the following:

  • Device(abc.ABC) is a class that holds all device information, allows the device to connect, disconnect, clear and actuate basic communication with the physical instrument. All devices will use a threading.Lock to safely communicate with the device. The use of the lock is not necessary but strongly recommended. The most important attribute of this class is the mediator, which will hold the object that performs the communication between software and hardware.
  • AcquisitionMixin(abc.ABC) is a mixin class that will be used in addition to Device, to provide acquisition capabilities such as single acquisition, timed single acquisition (single acquisition with time stamps), and time series acquistion (multiple timed single acquisitions).
    • The time series acquisition will take place in a separate class called AcquisitionThread, which will use a pipeline (queue.Queue) to pass the acquired data from the acquisition thread to the device data storage variable. The acquisition thread will be controlled (start, pause, resume, stop) by threading.Events, which will be passed down from the experiment control thread to the acquisition thread through the device object.
    • The time series acquisition has two pipeline modes
      • Stream: put the data in the queue every time they are collected, and a StreamingThread will 'collect' the data to either store, display on a GUI or both. This is best used when we want to observe the time series collection as it takes place.
      • Aggregate: hold data internally in AcquisitionThread, and release them at the end of the acquisition through the pipeline. In that case, the StreamingThread is not necessary. This is best used when time overhead must be avoided as much as possible, and observation of the time series during collection is not important.
      • Happy to discuss other options (like batched streaming).

The main device subclasses that can be helpful are:

  • PyVisaMessagingBasedDevice(abc.ABC, Device): Devices that use the pyvisa library, and specifically MessageBasedInstruments, such as USB, Serial and GPIB, which are very common in our labs. This subclass defines the base methods connect, disconnect and clear. Additionally, it implements classmethods that allow the use to easily connect with the device without necessarily knowing the port, but rather the device IDN, or the manufacttorer device name attribute. This will allow users to be able to use already known devices without needing to change the code every time they change the connections or the computer system.
  • NIDAQDevice(abc.ABC, Device): Not sure if needed or how I would implemented yet, the goal is similar to the above subclass. For example, we can define a device that takes the already defined RateCounter and uses it as a mediator, or a slight variation thereof.
  • DLLbasedDevice(abc.ABC, Device): A device whose mediator is a dll-based library. I have created a universal dll-loading class and methods based on ctypesgen that searches multiple locations in the computer to find the desired dll file. This class is not implemented in this branch but in the hyperspectral-dev brach in my personal github account. I am planning on transfering it soon.

Other useful classes and methods I have developed:

  • get_configured_logger: a method that returns a logger with a given name that is configured based on the logger_config.yaml file settings.
  • LoggerMixin: a mixin class that creates a log method for a given class, that allows tracking of information from specific devices. I believe this will help the user better understand how their experiment is run, as long as we consistently log what our code does! Great for troubleshooting multiple threads and devices.

Future implementations:

  • I want to make a generalized GUI that can take multiple devices, can load settings through a json file, and can perform streaming acquisition for any AcquisitionMixin-based object (much like the oscilloscope application, but generalized).
  • Define specific devices, such as powermeters, wavemeters, counters, sepctrometers, and lasers (these are the devices I use the most in the lab).
  • Re-implement piezo-control through the Device class.
  • Assure already existing code can mesh well with the new code.

Other features that probably refer to existing or should refer to new issues, but would be good to start implementing now:

  • Implement better error handling. I personally prefer defining package-specific errors, as well as some generic "assertion" methods, similar to what I do here.
  • Since the code is expanding, and multiple devices will be added, it would be a good idea to start added optional dependences, similar to what I do here.

Issues I am running into:

  • Not sure if my time series acquisition approach is optimal. Would love to discuss other ways of implementing it. Some of the questions/doubts that come to mind right now:
    • Is the use of queue.Queue the best thing to do? I am not sure how we could implement generators instead? Wouldn't they have potential timing and threading issues?
    • What types of pipeline modes would be most useful to define? Are multiple modes even important?
    • Is the StreamingThread necessary?
  • Having trouble deciding how the mixin classes will be added. I saw some discussion here, but it would be great if we decide what would be the best approach for us.

… as a subclass with the Device interface class to provide data acquisition support for sensor/reader-type devices.
…. Specifically, two methods were added to ease automated resource detection with pyvisa.
…and force-stop functionality in time series acquisition.
In `utils.py`, updated methods resource finder methods to bypass `resource.clear()` errors, and set the write and (automatically) read termination. Also created a force clear method for problematic read buffers.
In `devices.py`, created a `MessageBasedDevice` class, updated AcquisitionMixin with data streaming thread and other optimizations.
… with device communication delays and crashes.
…Mixin classes. Thoroughly documented everything.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant