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

Process control switch mixin #35

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ None

### New Features
- support for Zaber (linear) motorized stages (in hardware/motor/zaber_motion)
- Added mixin `qudi.interface.mixins.process_control_switch.ProcessControlSwitchMixin` to provide
optional default implementation satisfying the `SwitchInterface` for process control hardware
modules implementing any of the interfaces contained in `qudi.interface.process_control_interface`

### Other
None
34 changes: 23 additions & 11 deletions src/qudi/default.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ hardware:
extend_hardware_name: True
connect:
switch1: 'switch1_dummy'
switch2: 'switch2_dummy'
switch2: 'process_control_dummy'


switch1_dummy:
Expand All @@ -305,16 +305,6 @@ hardware:
two: ['down', 'up']
three: ['low', 'middle', 'high']

switch2_dummy:
module.Class: 'dummy.switch_dummy.SwitchDummy'
options:
name: 'Second' # optional
remember_states: True # optional
switches:
'An even longer name of the switch itself':
- 'Very long name of a random state'
- 'Another very long name of a random state'

fast_counter_dummy:
module.Class: 'dummy.fast_counter_dummy.FastCounterDummy'
options:
Expand All @@ -327,3 +317,25 @@ hardware:

spectrometer_dummy:
module.Class: 'dummy.spectrometer_dummy.SpectrometerDummy'

process_control_dummy:
module.Class: 'dummy.process_control_dummy.ProcessControlDummy'
options:
process_value_channels:
Temperature:
unit: 'K'
limits: [0, .inf]
dtype: float
Voltage:
unit: 'V'
limits: [-10.0, 10.0]
dtype: float
setpoint_channels:
Current:
unit: 'A'
limits: [-5, 5]
dtype: float
Frequency:
unit: 'Hz'
limits: [100.0e3, 20.0e9]
dtype: float
10 changes: 5 additions & 5 deletions src/qudi/hardware/dummy/process_control_dummy.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@
from qudi.util.mutex import RecursiveMutex
from qudi.core.configoption import ConfigOption
from qudi.interface.process_control_interface import ProcessControlConstraints
from qudi.interface.process_control_interface import ProcessSetpointInterface
from qudi.interface.process_control_interface import ProcessValueInterface
from qudi.interface.process_control_interface import ProcessControlInterface
from qudi.interface.mixins.process_control_switch import ProcessControlSwitchMixin


class ProcessControlDummy(ProcessSetpointInterface, ProcessValueInterface):
class ProcessControlDummy(ProcessControlSwitchMixin, ProcessControlInterface):
""" A dummy class to emulate a process control device (setpoints and process values)

Example config for copy-paste:
Expand Down Expand Up @@ -89,8 +89,8 @@ def on_activate(self):
units.update({ch: d['unit'] for ch, d in self._process_value_channels.items() if 'unit' in d})
limits = {ch: d['limits'] for ch, d in self._setpoint_channels.items() if 'limits' in d}
limits.update({ch: d['limits'] for ch, d in self._process_value_channels.items() if 'limits' in d})
dtypes = {ch: d['dtype'] for ch, d in self._setpoint_channels.items() if 'dtype' in d}
dtypes.update({ch: d['dtype'] for ch, d in self._process_value_channels.items() if 'dtype' in d})
dtypes = {ch: eval(d['dtype']) for ch, d in self._setpoint_channels.items() if 'dtype' in d}
dtypes.update({ch: eval(d['dtype']) for ch, d in self._process_value_channels.items() if 'dtype' in d})
self.__constraints = ProcessControlConstraints(
setpoint_channels=tuple(self._setpoint_channels),
process_channels=tuple(self._process_value_channels),
Expand Down
64 changes: 64 additions & 0 deletions src/qudi/interface/mixins/process_control_switch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-

"""
Copyright (c) 2022, the qudi developers. See the AUTHORS.md file at the top-level directory of this
distribution and on <https://github.com/Ulm-IQO/qudi-iqo-modules/>

This file is part of qudi.

Qudi is free software: you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.

Qudi 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 Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with qudi.
If not, see <https://www.gnu.org/licenses/>.
"""

__all__ = ['ProcessControlSwitchMixin']

from typing import Dict, Tuple

from qudi.interface.switch_interface import SwitchInterface


class ProcessControlSwitchMixin(SwitchInterface):
""" Mixin to inherit alongside interfaces contained in qudi.interface.process_control_interface
to automatically provide a SwitchInterface for enabling/disabling process control hardware.

Use like this:

class MyHardwareModule(ProcessControlSwitchMixin, ProcessControlInterface):
...
"""

@property
def name(self) -> str:
return self.module_name

@property
def available_states(self) -> Dict[str, Tuple[str, ...]]:
return {'state': ('disabled', 'enabled')}

def get_state(self, switch: str) -> str:
""" Query state of single switch by name

@param str switch: name of the switch to query the state for
@return str: The current switch state
"""
return self.available_states['state'][int(self.is_active)]

def set_state(self, switch: str, state: str) -> None:
""" Query state of single switch by name

@param str switch: name of the switch to change
@param str state: name of the state to set
"""
try:
active = bool(self.available_states[switch].index(state))
except (KeyError, ValueError) as err:
raise ValueError('Invalid switch name or state descriptor') from err
self.set_activity_state(active)
23 changes: 14 additions & 9 deletions src/qudi/interface/switch_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
If not, see <https://www.gnu.org/licenses/>.
"""

__all__ = ['SwitchInterface']

from typing import Dict, Mapping, Sequence, Tuple
from abc import abstractmethod

from qudi.core.module import Base


Expand All @@ -33,7 +37,7 @@ class SwitchInterface(Base):

@property
@abstractmethod
def name(self):
def name(self) -> str:
""" Name of the hardware as string.

@return str: The name of the hardware
Expand All @@ -42,7 +46,7 @@ def name(self):

@property
@abstractmethod
def available_states(self):
def available_states(self) -> Dict[str, Tuple[str, ...]]:
""" Names of the states as a dict of tuples.

The keys contain the names for each of the switches. The values are tuples of strings
Expand All @@ -53,7 +57,7 @@ def available_states(self):
pass

@abstractmethod
def get_state(self, switch):
def get_state(self, switch: str) -> str:
""" Query state of single switch by name

@param str switch: name of the switch to query the state for
Expand All @@ -62,7 +66,7 @@ def get_state(self, switch):
pass

@abstractmethod
def set_state(self, switch, state):
def set_state(self, switch: str, state: str) -> None:
""" Query state of single switch by name

@param str switch: name of the switch to change
Expand All @@ -73,23 +77,23 @@ def set_state(self, switch, state):
# Non-abstract default implementations below

@property
def number_of_switches(self):
def number_of_switches(self) -> int:
""" Number of switches provided by the hardware.

@return int: number of switches
"""
return len(self.available_states)

@property
def switch_names(self):
def switch_names(self) -> Tuple[str, ...]:
""" Names of all available switches as tuple.

@return str[]: Tuple of strings of available switch names.
"""
return tuple(self.available_states)

@property
def states(self):
def states(self) -> Dict[str, str]:
""" The current states the hardware is in as state dictionary with switch names as keys and
state names as values.

Expand All @@ -98,7 +102,7 @@ def states(self):
return {switch: self.get_state(switch) for switch in self.available_states}

@states.setter
def states(self, state_dict):
def states(self, state_dict: Mapping[str, str]) -> None:
""" The setter for the states of the hardware.

The states of the system can be set by specifying a dict that has the switch names as keys
Expand All @@ -111,7 +115,8 @@ def states(self, state_dict):
self.set_state(switch, state)

@staticmethod
def _chk_refine_available_switches(switch_dict):
def _chk_refine_available_switches(switch_dict: Dict[str, Sequence[str]]
) -> Dict[str, Tuple[str, ...]]:
""" Perform some general checking of the configured available switches and their possible
states. When implementing a hardware module, you can overwrite this method to include
custom checks, but make sure to call this implementation first via super().
Expand Down