Source code for trspectrometer.hardware
# Copyright 2020 Patrick C. Tapping
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
"""
Module for management of various spectrometer hardware devices.
This does not actually control any hardware itself, but instead stores
references to plugin modules which do the hard work.
A hardware plugin is a normal plugin module, but must also:
- Implement a ``init()`` method to connect to and initialise the device(s).
- Implement a ``close()`` method to disconnect from devices and free any used resources.
- Implement a ``statuspanel`` property to return a QWidget class type
(not an instance of the class!) to display device status information in the
hardware status panel. This may be ``None`` if no panel is required.
- Add a reference to itself to the :data:`modules` dictionary, for example using
``hardware.modules[__name__] = sys.modules[__name__]``.
Calling of the plugin's ``init()`` and ``close()`` methods will be handled automatically,
as will creation and display of the ``statuspanel`` if provided.
"""
import os
import logging
from collections import namedtuple
import time
from PySide6 import QtCore, QtGui, QtWidgets
from PySide6.QtUiTools import loadUiType
_log = logging.getLogger(__name__)
#: Dictionary of configured hardware device modules.
#: The keys are strings containing the name of the module, with values being
#: the loaded module instance.
#: Hardware plugin modules should add themselves to this, for example by using
#: ``hardware.modules[__name__] = sys.modules[__name__]``.
modules = {}
[docs]def init():
"""Initialise all hardware devices."""
global modules
_log.info("Initialising hardware devices.")
for name, module in modules.items():
try:
module.init()
except Exception:
_log.exception(f"Error initialising module: {name}")
# Wait for final init to occur, such as delay homing operations
_log.info("Waiting for all device initialisations to complete...")
# Give a second for devices to update their status etc
time.sleep(1.0)
ready = False
clock = time.monotonic()
while not ready:
if time.monotonic() > clock + 60.0:
_log.warning("Timeout waiting for device initialisations to complete!")
break
ready = True
# Wait for delay(s) to finish homing
if "delay" in modules:
for delay in modules["delay"].devices:
if not delay is None and delay.is_initialised():
ready &= not delay.is_moving()
time.sleep(0.5)
_log.info("Hardware initialisation finished.")
[docs]def close():
"""Close connections to all hardware devices."""
global modules
_log.info("Closing hardware devices.")
for name, module in modules.items():
try:
module.close()
except:
_log.exception(f"Error closing module: {name}")
[docs]class HardwareStatusPanel(QtWidgets.QScrollArea):
"""
Window in which to place the individual hardware status panels.
"""
def __init__(self, parent=None):
super().__init__(parent)
# Build the UI
self.setWindowTitle("Hardware Status")
self.setWindowIcon(QtGui.QIcon("trspectrometer.png"))
self.setWidgetResizable(True)
self.setMinimumSize(500, 50)
self.panel = QtWidgets.QWidget(self)
self.layout = QtWidgets.QVBoxLayout(self)
# Add the various hardware status panels
global modules
for _, module in modules.items():
if module.statuspanel:
w = module.statuspanel(self)
self.layout.addWidget(w)
self.layout.addStretch(1)
self.panel.setLayout(self.layout)
self.setWidget(self.panel)