diff --git a/docs/changelog.md b/docs/changelog.md
index 07e07f209..3e25edcd5 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -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
\ No newline at end of file
diff --git a/src/qudi/default.cfg b/src/qudi/default.cfg
index b6eafb2e4..9c9ffee7c 100644
--- a/src/qudi/default.cfg
+++ b/src/qudi/default.cfg
@@ -292,7 +292,7 @@ hardware:
extend_hardware_name: True
connect:
switch1: 'switch1_dummy'
- switch2: 'switch2_dummy'
+ switch2: 'process_control_dummy'
switch1_dummy:
@@ -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:
@@ -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
diff --git a/src/qudi/hardware/dummy/process_control_dummy.py b/src/qudi/hardware/dummy/process_control_dummy.py
index 13e149656..6e9b764ec 100644
--- a/src/qudi/hardware/dummy/process_control_dummy.py
+++ b/src/qudi/hardware/dummy/process_control_dummy.py
@@ -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:
@@ -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),
diff --git a/src/qudi/interface/mixins/process_control_switch.py b/src/qudi/interface/mixins/process_control_switch.py
new file mode 100644
index 000000000..3b576d5be
--- /dev/null
+++ b/src/qudi/interface/mixins/process_control_switch.py
@@ -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
+
+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 .
+"""
+
+__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)
diff --git a/src/qudi/interface/switch_interface.py b/src/qudi/interface/switch_interface.py
index 26d21fe5d..70b8d5fd0 100644
--- a/src/qudi/interface/switch_interface.py
+++ b/src/qudi/interface/switch_interface.py
@@ -18,7 +18,11 @@
If not, see .
"""
+__all__ = ['SwitchInterface']
+
+from typing import Dict, Mapping, Sequence, Tuple
from abc import abstractmethod
+
from qudi.core.module import Base
@@ -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
@@ -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
@@ -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
@@ -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
@@ -73,7 +77,7 @@ 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
@@ -81,7 +85,7 @@ def number_of_switches(self):
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.
@@ -89,7 +93,7 @@ def switch_names(self):
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.
@@ -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
@@ -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().