pychemstation 0.4.7.dev1__py3-none-any.whl → 0.4.7.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pychemstation/control/__init__.py +3 -2
- hein-analytical-control/devices/Agilent/hplc.py → pychemstation/control/comm.py +21 -181
- pychemstation/control/method.py +232 -0
- pychemstation/control/sequence.py +140 -0
- pychemstation/control/table_controller.py +75 -0
- pychemstation/utils/__init__.py +0 -2
- {ag_hplc_macro/control → pychemstation/utils}/chromatogram.py +2 -1
- pychemstation/utils/constants.py +1 -1
- hein_analytical_control/devices/Agilent/hplc_param_types.py → pychemstation/utils/macro.py +5 -69
- pychemstation/utils/method_types.py +44 -0
- pychemstation/utils/sequence_types.py +33 -0
- pychemstation/utils/table_types.py +60 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev3.dist-info}/METADATA +13 -12
- pychemstation-0.4.7.dev3.dist-info/RECORD +30 -0
- ag_hplc_macro/__init__.py +0 -3
- ag_hplc_macro/analysis/__init__.py +0 -1
- ag_hplc_macro/analysis/base_spectrum.py +0 -509
- ag_hplc_macro/analysis/spec_utils.py +0 -304
- ag_hplc_macro/analysis/utils.py +0 -63
- ag_hplc_macro/control/__init__.py +0 -5
- ag_hplc_macro/control/hplc.py +0 -673
- ag_hplc_macro/generated/__init__.py +0 -56
- ag_hplc_macro/generated/dad_method.py +0 -367
- ag_hplc_macro/generated/pump_method.py +0 -519
- ag_hplc_macro/utils/__init__.py +0 -2
- ag_hplc_macro/utils/constants.py +0 -15
- ag_hplc_macro/utils/hplc_param_types.py +0 -185
- hein-analytical-control/__init__.py +0 -3
- hein-analytical-control/analysis/__init__.py +0 -1
- hein-analytical-control/analysis/base_spectrum.py +0 -509
- hein-analytical-control/analysis/spec_utils.py +0 -304
- hein-analytical-control/analysis/utils.py +0 -63
- hein-analytical-control/devices/Agilent/__init__.py +0 -3
- hein-analytical-control/devices/Agilent/chemstation.py +0 -290
- hein-analytical-control/devices/Agilent/chromatogram.py +0 -129
- hein-analytical-control/devices/Agilent/hplc_param_types.py +0 -141
- hein-analytical-control/devices/Magritek/Spinsolve/__init__.py +0 -0
- hein-analytical-control/devices/Magritek/Spinsolve/commands.py +0 -495
- hein-analytical-control/devices/Magritek/Spinsolve/spectrum.py +0 -822
- hein-analytical-control/devices/Magritek/Spinsolve/spinsolve.py +0 -425
- hein-analytical-control/devices/Magritek/Spinsolve/utils/__init__.py +0 -5
- hein-analytical-control/devices/Magritek/Spinsolve/utils/connection.py +0 -168
- hein-analytical-control/devices/Magritek/Spinsolve/utils/constants.py +0 -8
- hein-analytical-control/devices/Magritek/Spinsolve/utils/exceptions.py +0 -25
- hein-analytical-control/devices/Magritek/Spinsolve/utils/parser.py +0 -340
- hein-analytical-control/devices/Magritek/Spinsolve/utils/shimming.py +0 -55
- hein-analytical-control/devices/Magritek/Spinsolve/utils/spinsolve_logging.py +0 -43
- hein-analytical-control/devices/Magritek/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/IR/NIRQuest512.py +0 -90
- hein-analytical-control/devices/OceanOptics/IR/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/IR/ir_spectrum.py +0 -191
- hein-analytical-control/devices/OceanOptics/Raman/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/Raman/raman_control.py +0 -46
- hein-analytical-control/devices/OceanOptics/Raman/raman_spectrum.py +0 -148
- hein-analytical-control/devices/OceanOptics/UV/QEPro2192.py +0 -90
- hein-analytical-control/devices/OceanOptics/UV/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/UV/uv_spectrum.py +0 -227
- hein-analytical-control/devices/OceanOptics/__init__.py +0 -0
- hein-analytical-control/devices/OceanOptics/oceanoptics.py +0 -115
- hein-analytical-control/devices/__init__.py +0 -15
- hein-analytical-control/generated/__init__.py +0 -56
- hein-analytical-control/generated/dad_method.py +0 -367
- hein-analytical-control/generated/pump_method.py +0 -519
- hein_analytical_control/__init__.py +0 -3
- hein_analytical_control/analysis/__init__.py +0 -1
- hein_analytical_control/analysis/base_spectrum.py +0 -509
- hein_analytical_control/analysis/spec_utils.py +0 -304
- hein_analytical_control/analysis/utils.py +0 -63
- hein_analytical_control/devices/Agilent/__init__.py +0 -3
- hein_analytical_control/devices/Agilent/chemstation.py +0 -290
- hein_analytical_control/devices/Agilent/chromatogram.py +0 -129
- hein_analytical_control/devices/Agilent/hplc.py +0 -436
- hein_analytical_control/devices/Magritek/Spinsolve/__init__.py +0 -0
- hein_analytical_control/devices/Magritek/Spinsolve/commands.py +0 -495
- hein_analytical_control/devices/Magritek/Spinsolve/spectrum.py +0 -822
- hein_analytical_control/devices/Magritek/Spinsolve/spinsolve.py +0 -425
- hein_analytical_control/devices/Magritek/Spinsolve/utils/__init__.py +0 -5
- hein_analytical_control/devices/Magritek/Spinsolve/utils/connection.py +0 -168
- hein_analytical_control/devices/Magritek/Spinsolve/utils/constants.py +0 -8
- hein_analytical_control/devices/Magritek/Spinsolve/utils/exceptions.py +0 -25
- hein_analytical_control/devices/Magritek/Spinsolve/utils/parser.py +0 -340
- hein_analytical_control/devices/Magritek/Spinsolve/utils/shimming.py +0 -55
- hein_analytical_control/devices/Magritek/Spinsolve/utils/spinsolve_logging.py +0 -43
- hein_analytical_control/devices/Magritek/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/IR/NIRQuest512.py +0 -90
- hein_analytical_control/devices/OceanOptics/IR/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/IR/ir_spectrum.py +0 -191
- hein_analytical_control/devices/OceanOptics/Raman/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/Raman/raman_control.py +0 -46
- hein_analytical_control/devices/OceanOptics/Raman/raman_spectrum.py +0 -148
- hein_analytical_control/devices/OceanOptics/UV/QEPro2192.py +0 -90
- hein_analytical_control/devices/OceanOptics/UV/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/UV/uv_spectrum.py +0 -227
- hein_analytical_control/devices/OceanOptics/__init__.py +0 -0
- hein_analytical_control/devices/OceanOptics/oceanoptics.py +0 -115
- hein_analytical_control/devices/__init__.py +0 -15
- hein_analytical_control/generated/__init__.py +0 -56
- hein_analytical_control/generated/dad_method.py +0 -367
- hein_analytical_control/generated/pump_method.py +0 -519
- pychemstation-0.4.7.dev1.dist-info/RECORD +0 -109
- /ag_hplc_macro/utils/chemstation.py → /pychemstation/utils/parsing.py +0 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev3.dist-info}/LICENSE +0 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev3.dist-info}/WHEEL +0 -0
- {pychemstation-0.4.7.dev1.dist-info → pychemstation-0.4.7.dev3.dist-info}/top_level.txt +0 -0
@@ -1,340 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Python module to parse the xml messages received from Spinsolve NMR.
|
3
|
-
"""
|
4
|
-
import logging
|
5
|
-
|
6
|
-
import xml.etree.ElementTree as ET
|
7
|
-
from xml.etree.ElementTree import ParseError
|
8
|
-
|
9
|
-
from .exceptions import RequestError, HardwareError, ShimmingError, NMRError
|
10
|
-
from .constants import (
|
11
|
-
CURRENT_SPINSOLVE_VERSION,
|
12
|
-
USER_DATA_TAG,
|
13
|
-
)
|
14
|
-
|
15
|
-
|
16
|
-
class ReplyParser:
|
17
|
-
"""Parses usefull information from the xml reply"""
|
18
|
-
|
19
|
-
### Response tags for easier handling ###
|
20
|
-
HARDWARE_RESPONSE_TAG = "HardwareResponse"
|
21
|
-
AVAILABLE_PROTOCOL_OPTIONS_RESPONSE_TAG = "AvailableProtocolOptionsResponse"
|
22
|
-
STATUS_TAG = "StatusNotification"
|
23
|
-
ESTIMATE_DURATION_RESPONSE_TAG = "EstimateDurationResponse"
|
24
|
-
CHECK_SHIM_RESPONSE_TAG = "CheckShimResponse"
|
25
|
-
QUICK_SHIM_RESPONSE_TAG = "QuickShimResponse"
|
26
|
-
POWER_SHIM_RESPONSE_TAG = "PowerShimResponse"
|
27
|
-
COMPLETED_NOTIFICATION_TAG = "CompletedNotificationType"
|
28
|
-
GET_RESPONSE_TAG = "GetResponse"
|
29
|
-
|
30
|
-
def __init__(self, device_ready_flag, data_folder_queue):
|
31
|
-
"""
|
32
|
-
Args:
|
33
|
-
device_ready_flag (:obj: "threading.Event"): The threading event indicating
|
34
|
-
that the instrument is ready for the next commands
|
35
|
-
data_folder_queue (:obj: "queue.Queue"): A queue object to store the data folder information
|
36
|
-
for subsequent access with Spectrum class
|
37
|
-
"""
|
38
|
-
|
39
|
-
self.device_ready_flag = device_ready_flag
|
40
|
-
self.data_folder_queue = data_folder_queue
|
41
|
-
self.connected_tag = None # String to indicate if the instrument is connected, updated with HardwareRequest
|
42
|
-
|
43
|
-
self.logger = logging.getLogger("spinsolve.parser")
|
44
|
-
|
45
|
-
# For shimming validation
|
46
|
-
self.shimming_line_width_threshold = 1
|
47
|
-
self.shimming_base_width_threshold = 40
|
48
|
-
|
49
|
-
def parse(self, message):
|
50
|
-
"""Parses the message into valuable XML element
|
51
|
-
|
52
|
-
Depending on the element tag, invokes various parsing methods
|
53
|
-
|
54
|
-
Args:
|
55
|
-
message (bytes): The message received from the instrument
|
56
|
-
"""
|
57
|
-
|
58
|
-
self.logger.debug("Obtained the message: \n%s", message)
|
59
|
-
|
60
|
-
# Checking the obtained message
|
61
|
-
try:
|
62
|
-
msg_root = ET.fromstring(message)
|
63
|
-
except ParseError:
|
64
|
-
self.logger.error(
|
65
|
-
"ParseError: invalid XML message received, check the full message \n <%s>",
|
66
|
-
message.decode(),
|
67
|
-
)
|
68
|
-
raise ParseError(
|
69
|
-
"Invalid XML message received from the instrument, please check the log for the full message"
|
70
|
-
) from None
|
71
|
-
if msg_root.tag != "Message" or len(msg_root) > 1:
|
72
|
-
self.logger.error(
|
73
|
-
"ParseError: incorrect message received, check the full message \n <%s>",
|
74
|
-
message.decode(),
|
75
|
-
)
|
76
|
-
raise ParseError(
|
77
|
-
"Incorrect message received from the instrument, please check the log for the full message"
|
78
|
-
)
|
79
|
-
|
80
|
-
# Main message element
|
81
|
-
msg_element = msg_root[0]
|
82
|
-
|
83
|
-
# Invoking specific methods for specific responds
|
84
|
-
if msg_element.tag == self.HARDWARE_RESPONSE_TAG:
|
85
|
-
return self.hardware_processing(msg_element)
|
86
|
-
elif msg_element.tag == self.AVAILABLE_PROTOCOL_OPTIONS_RESPONSE_TAG:
|
87
|
-
return message
|
88
|
-
elif msg_element.tag in [
|
89
|
-
self.CHECK_SHIM_RESPONSE_TAG,
|
90
|
-
self.QUICK_SHIM_RESPONSE_TAG,
|
91
|
-
self.POWER_SHIM_RESPONSE_TAG,
|
92
|
-
]:
|
93
|
-
return self.shimming_processing(msg_element)
|
94
|
-
elif (
|
95
|
-
msg_element.tag == self.STATUS_TAG
|
96
|
-
or msg_element.tag == self.COMPLETED_NOTIFICATION_TAG
|
97
|
-
):
|
98
|
-
return self.status_processing(msg_element)
|
99
|
-
elif msg_element.tag == self.ESTIMATE_DURATION_RESPONSE_TAG:
|
100
|
-
return self.estimate_duration_processing(msg_element)
|
101
|
-
elif msg_element.tag == self.GET_RESPONSE_TAG:
|
102
|
-
return self.data_response_processing(msg_element)
|
103
|
-
else:
|
104
|
-
self.logger.info(
|
105
|
-
"No specific parser requested, returning full decoded message"
|
106
|
-
)
|
107
|
-
return message.decode()
|
108
|
-
|
109
|
-
def hardware_processing(self, element):
|
110
|
-
"""Process the message if the Hardware tag is present
|
111
|
-
|
112
|
-
Args:
|
113
|
-
element (:obj: xml.etree.ElementTree.Element): An element containing all
|
114
|
-
usefull information regarding Hardware response from the instrument
|
115
|
-
|
116
|
-
Returns:
|
117
|
-
dict: Dictionary with usefull hardware information
|
118
|
-
|
119
|
-
Raises:
|
120
|
-
HardwareError: In case the instrument is not connected
|
121
|
-
"""
|
122
|
-
|
123
|
-
# Checking if the instrument is physically connected
|
124
|
-
self.logger.debug("Parsing message with <%s> tag", element.tag)
|
125
|
-
self.connected_tag = element.find(".//ConnectedToHardware").text
|
126
|
-
if self.connected_tag == "false":
|
127
|
-
raise HardwareError("The instrument is not connected!")
|
128
|
-
|
129
|
-
software_tag = element.find(".//SpinsolveSoftware").text
|
130
|
-
|
131
|
-
spinsolve_tag = element.find(".//SpinsolveType").text
|
132
|
-
|
133
|
-
self.logger.info(
|
134
|
-
"The %s NMR instrument is successfully connected \nRunning under %s Spinsolve software version",
|
135
|
-
spinsolve_tag,
|
136
|
-
software_tag,
|
137
|
-
)
|
138
|
-
if software_tag[:6] != CURRENT_SPINSOLVE_VERSION:
|
139
|
-
self.logger.warning(
|
140
|
-
"The current software version <%s> was not tested, please update or use at your own risk",
|
141
|
-
software_tag,
|
142
|
-
)
|
143
|
-
|
144
|
-
# If the instrument is connected, setting the ready flag
|
145
|
-
self.device_ready_flag.set()
|
146
|
-
|
147
|
-
usefull_information_dict = {
|
148
|
-
"Connected": f"{self.connected_tag}",
|
149
|
-
"SoftwareVersion": f"{software_tag}",
|
150
|
-
"InstrumentType": f"{spinsolve_tag}",
|
151
|
-
}
|
152
|
-
|
153
|
-
return usefull_information_dict
|
154
|
-
|
155
|
-
def data_response_processing(self, element):
|
156
|
-
"""Process the message if the user parameters response is present.
|
157
|
-
|
158
|
-
Args:
|
159
|
-
element (:obj: xml.etree.ElementTree.Element): An element containing
|
160
|
-
all usefull information regarding Hardware response from the
|
161
|
-
instrument.
|
162
|
-
|
163
|
-
Returns:
|
164
|
-
Dict: Dictionary with user/experiment specific parameter, e.g.
|
165
|
-
userdata, solvent or sample.
|
166
|
-
"""
|
167
|
-
|
168
|
-
self.logger.debug("Parsing message with <%s> tag", element.tag)
|
169
|
-
# messages with GetResponse have only one child
|
170
|
-
if len(element) > 1:
|
171
|
-
raise RequestError(
|
172
|
-
"Returned response for user data is incorrect, \
|
173
|
-
check log files for details!"
|
174
|
-
)
|
175
|
-
|
176
|
-
chelement = element[0]
|
177
|
-
|
178
|
-
# special case for user data
|
179
|
-
if chelement.tag == USER_DATA_TAG:
|
180
|
-
return {
|
181
|
-
subel.attrib["key"]: subel.attrib["value"]
|
182
|
-
for subel in chelement # iterating over subelements
|
183
|
-
}
|
184
|
-
|
185
|
-
return chelement.text
|
186
|
-
|
187
|
-
def shimming_processing(self, element):
|
188
|
-
"""Process the message if the Shim tag is present
|
189
|
-
|
190
|
-
Args:
|
191
|
-
element (:obj: xml.etree.ElementTree.Element): An element containing all
|
192
|
-
usefull information regarding shimming response from the instrument
|
193
|
-
|
194
|
-
Returns:
|
195
|
-
True if shimming was successfull
|
196
|
-
|
197
|
-
Raises:
|
198
|
-
ShimmingError: If the shimming process failed
|
199
|
-
"""
|
200
|
-
|
201
|
-
self.logger.debug("Parsing message with <%s> tag", element.tag)
|
202
|
-
|
203
|
-
self.device_ready_flag.set()
|
204
|
-
|
205
|
-
error_text = element.get("error")
|
206
|
-
if error_text:
|
207
|
-
self.logger.error(
|
208
|
-
"ShimmingError: check the error message below\
|
209
|
-
\n%s",
|
210
|
-
error_text,
|
211
|
-
)
|
212
|
-
if (
|
213
|
-
self.shimming_base_width_threshold == 40
|
214
|
-
and self.shimming_line_width_threshold == 1
|
215
|
-
):
|
216
|
-
# only raise error if default line widths were used as the
|
217
|
-
# reference point
|
218
|
-
raise ShimmingError(f"Shimming error: {error_text}")
|
219
|
-
|
220
|
-
for child in element:
|
221
|
-
self.logger.info("%s - %s", child.tag, child.text)
|
222
|
-
|
223
|
-
# Obtaining shimming parameters
|
224
|
-
line_width = round(float(element.find(".//LineWidth").text), 2)
|
225
|
-
base_width = round(float(element.find(".//BaseWidth").text), 2)
|
226
|
-
system_ready = element.find(".//SystemIsReady").text
|
227
|
-
|
228
|
-
# Checking shimming criteria
|
229
|
-
if line_width > self.shimming_line_width_threshold:
|
230
|
-
self.logger.critical(
|
231
|
-
"Shimming line width <%.2f> is above requested threshold <%.2f>, consider running another shimming method",
|
232
|
-
line_width,
|
233
|
-
self.shimming_line_width_threshold,
|
234
|
-
)
|
235
|
-
if base_width > self.shimming_base_width_threshold:
|
236
|
-
self.logger.critical(
|
237
|
-
"Shimming line width <%.2f> is above requested threshold <%.2f>, consider running another shimming method",
|
238
|
-
line_width,
|
239
|
-
self.shimming_line_width_threshold,
|
240
|
-
)
|
241
|
-
if system_ready != "true":
|
242
|
-
self.logger.critical(
|
243
|
-
"System is not ready after shimming, consider running another shimming method"
|
244
|
-
)
|
245
|
-
return False
|
246
|
-
else:
|
247
|
-
return True
|
248
|
-
|
249
|
-
def status_processing(self, element):
|
250
|
-
"""Process the message if the Status tag is present
|
251
|
-
|
252
|
-
Logs the incoming messages and manages device_ready_flag
|
253
|
-
|
254
|
-
Args:
|
255
|
-
element (:obj: xml.etree.ElementTree.Element): An element containing all
|
256
|
-
usefull information regarding status response from the instrument
|
257
|
-
|
258
|
-
Returns:
|
259
|
-
str: String containing the path to the saved NMR data
|
260
|
-
|
261
|
-
Raises:
|
262
|
-
NMRError: in case of the protocol errors
|
263
|
-
"""
|
264
|
-
|
265
|
-
self.logger.debug("Parsing message with <%s> tag", element.tag)
|
266
|
-
|
267
|
-
# Valuable data from the message
|
268
|
-
state_tag = element[0].tag
|
269
|
-
state_elem = element[0]
|
270
|
-
protocol_attrib = state_elem.get("protocol")
|
271
|
-
|
272
|
-
# Checking for errors first
|
273
|
-
error_attrib = state_elem.get("error")
|
274
|
-
|
275
|
-
# No error is actually raised, since the instrument
|
276
|
-
# Continues with protocol execution even if an error was returned
|
277
|
-
# TODO add an option to handle the error and stop the execution
|
278
|
-
if error_attrib:
|
279
|
-
self.logger.error(
|
280
|
-
"NMRError: <%s>, check the log for the full message", error_attrib
|
281
|
-
)
|
282
|
-
# raise NMRError(f"Error running the protocol <{protocol_attrib}>: {error_attrib}")
|
283
|
-
|
284
|
-
status_attrib = state_elem.get("status")
|
285
|
-
percentage_attrib = state_elem.get("percentage")
|
286
|
-
seconds_remaining_attrib = state_elem.get("secondsRemaining")
|
287
|
-
|
288
|
-
# Logging the data
|
289
|
-
if state_tag == "State":
|
290
|
-
# Resetting the event to False to block the incoming msg
|
291
|
-
if status_attrib == "Running":
|
292
|
-
self.logger.debug("Device in operation, blocking the incoming messages")
|
293
|
-
self.device_ready_flag.clear()
|
294
|
-
self.logger.info("%s the <%s> protocol", status_attrib, protocol_attrib)
|
295
|
-
if status_attrib == "Ready":
|
296
|
-
# When device is ready, setting the event to True for the next protocol to be executed
|
297
|
-
# Delay the flag setting for the SHIM protocol
|
298
|
-
if protocol_attrib != "SHIM":
|
299
|
-
self.device_ready_flag.set()
|
300
|
-
data_folder = state_elem.get("dataFolder")
|
301
|
-
self.logger.info(
|
302
|
-
"The protocol <%s> is complete, the NMR data is saved in <%s>",
|
303
|
-
protocol_attrib,
|
304
|
-
data_folder,
|
305
|
-
)
|
306
|
-
self.data_folder_queue.put(data_folder)
|
307
|
-
return data_folder
|
308
|
-
|
309
|
-
if state_tag == "Progress":
|
310
|
-
self.logger.info(
|
311
|
-
"The protocol <%s> is performing, %s%% completed, %s seconds remain",
|
312
|
-
protocol_attrib,
|
313
|
-
percentage_attrib,
|
314
|
-
seconds_remaining_attrib,
|
315
|
-
)
|
316
|
-
|
317
|
-
def estimate_duration_processing(self, element):
|
318
|
-
"""Process the message if protocol duration was requested
|
319
|
-
|
320
|
-
Args:
|
321
|
-
element (:obj: xml.etree.ElementTree.Element): An element containing all
|
322
|
-
usefull information regarding duration estimation from the instrument
|
323
|
-
|
324
|
-
Returns:
|
325
|
-
int: Estimated duration for the requested protocol in seconds
|
326
|
-
|
327
|
-
Raises:
|
328
|
-
RequestError: In case the instrument returns an error attribute
|
329
|
-
"""
|
330
|
-
|
331
|
-
self.logger.debug("Parsing message with <%s> tag", element.tag)
|
332
|
-
|
333
|
-
error_text = element.get("error")
|
334
|
-
if error_text:
|
335
|
-
self.logger.error("RequestError: check the log for the full message")
|
336
|
-
raise RequestError(f"Duration request error: {error_text}")
|
337
|
-
|
338
|
-
duration_in_seconds = element.get("durationInSeconds")
|
339
|
-
|
340
|
-
return int(duration_in_seconds)
|
@@ -1,55 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Utility function to record last shimming results
|
3
|
-
"""
|
4
|
-
import time
|
5
|
-
import os
|
6
|
-
import json
|
7
|
-
import queue
|
8
|
-
from functools import wraps
|
9
|
-
|
10
|
-
|
11
|
-
HERE = os.path.dirname(os.path.abspath(__file__))
|
12
|
-
SHIMMING_PARAMETERS = "shim.par"
|
13
|
-
TIME_FORMAT = r"%Y-%m-%dT%H:%M:%S.%f"
|
14
|
-
|
15
|
-
|
16
|
-
def shimming(f):
|
17
|
-
"""Decorator to record shimming data."""
|
18
|
-
|
19
|
-
@wraps(f)
|
20
|
-
def wrapper(*args, **kwargs):
|
21
|
-
spinsolve_instance = args[0]
|
22
|
-
|
23
|
-
# performing shimming
|
24
|
-
result = f(*args, **kwargs)
|
25
|
-
params_path = os.path.join(result, SHIMMING_PARAMETERS)
|
26
|
-
|
27
|
-
# extracting and saving shimming parameters
|
28
|
-
spinsolve_instance.last_shimming_results = (
|
29
|
-
spinsolve_instance.spectrum.extract_parameters(params_path)
|
30
|
-
)
|
31
|
-
|
32
|
-
# appending path
|
33
|
-
spinsolve_instance.last_shimming_results.update(path=params_path)
|
34
|
-
|
35
|
-
# loading and saving timestamp
|
36
|
-
shimming_time = time.strptime(
|
37
|
-
spinsolve_instance.last_shimming_results["CurrentTime"], TIME_FORMAT
|
38
|
-
)
|
39
|
-
spinsolve_instance.last_shimming_results.update(
|
40
|
-
timestamp=time.mktime(shimming_time)
|
41
|
-
)
|
42
|
-
|
43
|
-
# emptying data folder queue, as shimming doesn't need processing
|
44
|
-
try:
|
45
|
-
spinsolve_instance.data_folder_queue.get_nowait()
|
46
|
-
except queue.Empty:
|
47
|
-
pass
|
48
|
-
|
49
|
-
# saving to json
|
50
|
-
with open(os.path.join(HERE, "shimming.json"), "w") as fobj:
|
51
|
-
json.dump(spinsolve_instance.last_shimming_results, fobj)
|
52
|
-
|
53
|
-
return result
|
54
|
-
|
55
|
-
return wrapper
|
@@ -1,43 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
from logging.handlers import RotatingFileHandler
|
3
|
-
import os
|
4
|
-
from datetime import datetime
|
5
|
-
|
6
|
-
|
7
|
-
now = datetime.now().strftime("%d%m%y")
|
8
|
-
|
9
|
-
LEVEL = logging.INFO
|
10
|
-
HERE = os.path.abspath(".")
|
11
|
-
LOG_PATH = os.path.join(HERE, "spinsolve_logs")
|
12
|
-
NAME = "spinsolve"
|
13
|
-
MAX_BYTES_SIZE = 3 * 1000 * 1000 # 2 MB is enough to store ~30 spectra records
|
14
|
-
BACKUP_COUNT = 5 # will give in total logs for ~150 spectra records
|
15
|
-
|
16
|
-
|
17
|
-
def get_logger():
|
18
|
-
"""Returns logger object with predefined console and file handlers."""
|
19
|
-
|
20
|
-
logger = logging.getLogger(NAME)
|
21
|
-
logger.setLevel(logging.DEBUG)
|
22
|
-
logger.handlers = [] # resetting
|
23
|
-
|
24
|
-
ff = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
|
25
|
-
|
26
|
-
# console handler
|
27
|
-
ch = logging.StreamHandler()
|
28
|
-
ch.setLevel(LEVEL)
|
29
|
-
ch.setFormatter(ff)
|
30
|
-
|
31
|
-
# file handler
|
32
|
-
os.makedirs(LOG_PATH, exist_ok=True)
|
33
|
-
file_path = os.path.join(LOG_PATH, f"{NAME}-{now}.log")
|
34
|
-
fh = RotatingFileHandler(
|
35
|
-
filename=file_path, mode="a", maxBytes=MAX_BYTES_SIZE, backupCount=BACKUP_COUNT
|
36
|
-
)
|
37
|
-
fh.setLevel(logging.DEBUG)
|
38
|
-
fh.setFormatter(ff)
|
39
|
-
|
40
|
-
logger.addHandler(ch)
|
41
|
-
logger.addHandler(fh)
|
42
|
-
|
43
|
-
return logger
|
File without changes
|
@@ -1,90 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
.. module:: NIRQuest512
|
3
|
-
:synopsis: Module representing the NIRQUest512 Near-IR spectrometer
|
4
|
-
:platforms: Unix, Windows
|
5
|
-
|
6
|
-
.. moduleauthor:: Graham Keenan (Cronin Lab 2020)
|
7
|
-
|
8
|
-
.. note:: Edit as needed. This is a skeletal implementation for minimal
|
9
|
-
functionality.
|
10
|
-
|
11
|
-
"""
|
12
|
-
|
13
|
-
import json
|
14
|
-
from pathlib import Path
|
15
|
-
from typing import Union, Dict
|
16
|
-
from .ir_spectrum import IRSpectrum
|
17
|
-
from ..oceanoptics import OceanOpticsSpectrometer
|
18
|
-
|
19
|
-
|
20
|
-
class NoReferenceException(Exception):
|
21
|
-
"""Exception for calling spectrum without a reference"""
|
22
|
-
|
23
|
-
|
24
|
-
class NIRQuest512(OceanOpticsSpectrometer):
|
25
|
-
"""Class representing the NIRQuest512 Near-IR spectrometer
|
26
|
-
|
27
|
-
Inherits:
|
28
|
-
OceanOpticsSpectrometer
|
29
|
-
"""
|
30
|
-
|
31
|
-
def __init__(self):
|
32
|
-
super().__init__("IR", name="NIRQuest512 IR Spectrometer")
|
33
|
-
self.reference = {}
|
34
|
-
self.__ref_called = False
|
35
|
-
|
36
|
-
def load_reference(self, ref: Union[str, Dict]):
|
37
|
-
"""Loads a pre-existing reference from disk or from dict.
|
38
|
-
|
39
|
-
Args:
|
40
|
-
ref (Union[str, Dict]): Reference as either a dictionary
|
41
|
-
or JSON filepath
|
42
|
-
"""
|
43
|
-
|
44
|
-
# Filepath, load and set
|
45
|
-
if isinstance(ref, str) or isinstance(ref, Path):
|
46
|
-
with open(ref) as fd:
|
47
|
-
self.reference = json.load(fd)
|
48
|
-
self.__ref_called = True
|
49
|
-
|
50
|
-
# Dict, set
|
51
|
-
elif isinstance(ref, dict):
|
52
|
-
self.reference = ref
|
53
|
-
self.__ref_called = True
|
54
|
-
|
55
|
-
# Not supported
|
56
|
-
else:
|
57
|
-
self.logger.warning(f"Reference {ref} is unsupported. Not loading")
|
58
|
-
|
59
|
-
def obtain_reference_spectrum(self) -> IRSpectrum:
|
60
|
-
"""Obtain a reference spectrum
|
61
|
-
|
62
|
-
Returns:
|
63
|
-
IRSpectrum: Reference IR spectrum
|
64
|
-
"""
|
65
|
-
|
66
|
-
wavelength, intensities = self.scan()
|
67
|
-
self.reference["wavelength"] = wavelength
|
68
|
-
self.reference["intensities"] = intensities
|
69
|
-
self.__ref_called = True
|
70
|
-
|
71
|
-
return IRSpectrum(wavelength, intensities)
|
72
|
-
|
73
|
-
def obtain_spectrum(self) -> IRSpectrum:
|
74
|
-
"""Obtain an IR spectrum of a sample.
|
75
|
-
|
76
|
-
Raises:
|
77
|
-
NoReferenceException: Attempting to measure a sample without#
|
78
|
-
reference data.
|
79
|
-
|
80
|
-
Returns:
|
81
|
-
IRSpectrum: Sample IR spectrum.
|
82
|
-
"""
|
83
|
-
|
84
|
-
if not self.__ref_called:
|
85
|
-
raise NoReferenceException(
|
86
|
-
"Attempting to call a spectrum without a valid reference."
|
87
|
-
)
|
88
|
-
|
89
|
-
wavelength, intensities = self.scan()
|
90
|
-
return IRSpectrum(wavelength, intensities, ref=self.reference)
|
File without changes
|