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

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

## 73 statements

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"""

4 Main module containing the top level ExperimentManager class.

5 Inherit from this class to implement your own experiment functionality in another

6 project and it will help you start, stop and manage your devices.

7"""

8

9 import logging

10 from enum import Enum

11 from typing import Dict

12

13 from .dev import Device, DeviceSequenceMixin

14

15

16 class ExperimentStatus(Enum):

17 """

18 Enumeration for the experiment status

19 """

20

21 INITIALIZING = -1

22 INITIALIZED = 0

23 STARTING = 1

24 RUNNING = 2

25 FINISHING = 3

26 FINISHED = 4

27 ERROR = 5

28

29

30 class 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 """

35

36

37 class 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 """

43

44 def __new__(cls, *args, **kwargs) -> "ExperimentManager":

45 """

46 Construct a new `ExperimentManager` instance.

47

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

56

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`.

61

62 :param devices: Devices sequenced in the experiment.

63 """

64 super().__init__(devices)

65 self._change_status(ExperimentStatus.INITIALIZED)

66

67 def _change_status(self, new_status: ExperimentStatus) -> None:

68 """

69 Change the status of this experiment manager to a new value. Includes logging.

70

71 :param new_status: is the Enum of the new experiment status.

72 """

73

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

82

83 def run(self) -> None:

84 """

85 Start experimental setup, start all devices.

86 """

87

88 self._change_status(ExperimentStatus.STARTING)

89

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)

98

99 def finish(self) -> None:

100 """

101 Stop experimental setup, stop all devices.

102 """

103

104 self._change_status(ExperimentStatus.FINISHING)

105

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)

114

115 def start(self) -> None:

116 """

117 Alias for ExperimentManager.run()

118 """

119 self.run()

120

121 def stop(self) -> None:

122 """

123 Alias for ExperimentManager.finish()

124 """

125 self.finish()

126

127 @property

128 def status(self) -> ExperimentStatus:

129 """

130 Get experiment status.

131

132 :return: experiment status enum code.

133 """

134

135 return self._status

136

137 def is_finished(self) -> bool:

138 """

139 Returns true, if the status of the experiment manager is `finished`.

140

141 :return: True if finished, false otherwise

142 """

143 return self.status == ExperimentStatus.FINISHED

144

145 def is_running(self) -> bool:

146 """

147 Returns true, if the status of the experiment manager is `running`.

148

149 :return: True if running, false otherwise

150 """

151 return self.status == ExperimentStatus.RUNNING

152

153 def is_error(self) -> bool:

154 """

155 Returns true, if the status of the experiment manager is `error`.

156

157 :return: True if on error, false otherwise

158 """

159 return self.status == ExperimentStatus.ERROR

160

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.

165

166 :param name: is the name of the device.

167 :param device: is the instantiated Device object.

168 :raise DeviceExistingException:

169 """

170

171 if self.status == ExperimentStatus.ERROR:

172 logging.error("Experiment is on ERROR, cannot add device")

173 raise ExperimentError

174

175 super().add_device(name, device)

176

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