Coverage for C:\Users\hjanssen\HOME\pyCharmProjects\ethz_hvl\hvl_ccb\hvl_ccb\experiment_manager.py : 38%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (c) 2019-2020 ETH Zurich, SIS ID and HVL D-ITET
2#
3"""
4Main module containing the top level ExperimentManager class.
5Inherit from this class to implement your own experiment functionality in another
6project and it will help you start, stop and manage your devices.
7"""
9import logging
10from enum import Enum
11from typing import Dict
13from .dev import Device, DeviceSequenceMixin
16class ExperimentStatus(Enum):
17 """
18 Enumeration for the experiment status
19 """
21 INITIALIZING = -1
22 INITIALIZED = 0
23 STARTING = 1
24 RUNNING = 2
25 FINISHING = 3
26 FINISHED = 4
27 ERROR = 5
30class ExperimentError(Exception):
31 """
32 Exception to indicate that the current status of the experiment manager is on
33 ERROR and thus no operations can be made until reset.
34 """
37class ExperimentManager(DeviceSequenceMixin):
38 """
39 Experiment Manager can start and stop communication protocols and devices.
40 It provides methods to queue commands to
41 devices and collect results.
42 """
44 def __new__(cls, *args, **kwargs) -> "ExperimentManager":
45 """
46 Construct a new `ExperimentManager` instance.
48 :param args: `__init__` arguments
49 :param kwargs: `__init__` keyword arguments
50 :return: `ExperimentManager` with status set to `ExperimentStatus.INITIALIZING`.
51 """
52 obj = super().__new__(cls)
53 # experiment manager status
54 obj._status = ExperimentStatus.INITIALIZING
55 return obj
57 def __init__(self, devices: Dict[str, Device]) -> None:
58 """
59 Initialize `ExperimentManager`. Takes a dictionary of instantiated devices
60 and initializes the experiment status to `ExperimentStatus.INITIALIZED`.
62 :param devices: Devices sequenced in the experiment.
63 """
64 super().__init__(devices)
65 self._change_status(ExperimentStatus.INITIALIZED)
67 def _change_status(self, new_status: ExperimentStatus) -> None:
68 """
69 Change the status of this experiment manager to a new value. Includes logging.
71 :param new_status: is the Enum of the new experiment status.
72 """
74 if new_status is not self._status:
75 log_msg = f"Experiment Status: {new_status} {new_status.name}"
76 # new status is different, log
77 if new_status is ExperimentStatus.ERROR:
78 logging.error(log_msg)
79 else:
80 logging.info(log_msg)
81 self._status: ExperimentStatus = new_status
83 def run(self) -> None:
84 """
85 Start experimental setup, start all devices.
86 """
88 self._change_status(ExperimentStatus.STARTING)
90 try:
91 super().start()
92 except Exception as e:
93 logging.error(e)
94 self._change_status(ExperimentStatus.ERROR)
95 raise ExperimentError from e
96 else:
97 self._change_status(ExperimentStatus.RUNNING)
99 def finish(self) -> None:
100 """
101 Stop experimental setup, stop all devices.
102 """
104 self._change_status(ExperimentStatus.FINISHING)
106 try:
107 super().stop()
108 except Exception as e:
109 logging.error(e)
110 self._change_status(ExperimentStatus.ERROR)
111 raise ExperimentError from e
112 else:
113 self._change_status(ExperimentStatus.FINISHED)
115 def start(self) -> None:
116 """
117 Alias for ExperimentManager.run()
118 """
119 self.run()
121 def stop(self) -> None:
122 """
123 Alias for ExperimentManager.finish()
124 """
125 self.finish()
127 @property
128 def status(self) -> ExperimentStatus:
129 """
130 Get experiment status.
132 :return: experiment status enum code.
133 """
135 return self._status
137 def is_finished(self) -> bool:
138 """
139 Returns true, if the status of the experiment manager is `finished`.
141 :return: True if finished, false otherwise
142 """
143 return self.status == ExperimentStatus.FINISHED
145 def is_running(self) -> bool:
146 """
147 Returns true, if the status of the experiment manager is `running`.
149 :return: True if running, false otherwise
150 """
151 return self.status == ExperimentStatus.RUNNING
153 def is_error(self) -> bool:
154 """
155 Returns true, if the status of the experiment manager is `error`.
157 :return: True if on error, false otherwise
158 """
159 return self.status == ExperimentStatus.ERROR
161 def add_device(self, name: str, device: Device) -> None:
162 """
163 Add a new device to the manager. If the experiment is running, automatically
164 start the device. If a device with this name already exists, raise an exception.
166 :param name: is the name of the device.
167 :param device: is the instantiated Device object.
168 :raise DeviceExistingException:
169 """
171 if self.status == ExperimentStatus.ERROR:
172 logging.error("Experiment is on ERROR, cannot add device")
173 raise ExperimentError
175 super().add_device(name, device)
177 # check experiment status and start device if started
178 if self.status == ExperimentStatus.RUNNING:
179 logging.info(f"Experiment is already RUNNING, start Device {device}")
180 try:
181 device.start()
182 except Exception as e:
183 logging.error(e)
184 self._change_status(ExperimentStatus.ERROR)
185 raise ExperimentError from e