keithley-tempcontrol 0.17.4__py3-none-any.whl → 0.18.1__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.
- egse/tempcontrol/keithley/__init__.py +0 -2
- egse/tempcontrol/keithley/daq6510.py +79 -242
- egse/tempcontrol/keithley/daq6510_adev.py +13 -1
- egse/tempcontrol/keithley/daq6510_amon.py +569 -0
- egse/tempcontrol/keithley/daq6510_cs.py +27 -1
- egse/tempcontrol/keithley/daq6510_dev.py +23 -214
- egse/tempcontrol/keithley/daq6510_mon.py +242 -489
- egse/tempcontrol/keithley/daq6510_protocol.py +29 -7
- egse/tempcontrol/keithley/daq6510_sim.py +4 -4
- keithley_tempcontrol/cgse_services.py +32 -0
- {keithley_tempcontrol-0.17.4.dist-info → keithley_tempcontrol-0.18.1.dist-info}/METADATA +1 -1
- keithley_tempcontrol-0.18.1.dist-info/RECORD +18 -0
- {keithley_tempcontrol-0.17.4.dist-info → keithley_tempcontrol-0.18.1.dist-info}/entry_points.txt +1 -0
- keithley_tempcontrol-0.17.4.dist-info/RECORD +0 -17
- {keithley_tempcontrol-0.17.4.dist-info → keithley_tempcontrol-0.18.1.dist-info}/WHEEL +0 -0
|
@@ -2,25 +2,23 @@ __all__ = [
|
|
|
2
2
|
"DAQ6510",
|
|
3
3
|
"DAQ6510Command",
|
|
4
4
|
]
|
|
5
|
-
import socket
|
|
6
|
-
import time
|
|
7
5
|
|
|
8
6
|
from egse.command import ClientServerCommand
|
|
9
|
-
from egse.device import DeviceConnectionError
|
|
10
7
|
from egse.device import DeviceError
|
|
11
|
-
from egse.
|
|
12
|
-
from egse.device import DeviceTimeoutError
|
|
13
|
-
from egse.device import DeviceTransport
|
|
8
|
+
from egse.env import bool_env
|
|
14
9
|
from egse.log import logger
|
|
15
10
|
from egse.settings import Settings
|
|
11
|
+
from egse.socketdevice import SocketDevice
|
|
12
|
+
|
|
13
|
+
VERBOSE_DEBUG = bool_env("VERBOSE_DEBUG")
|
|
16
14
|
|
|
17
15
|
IDENTIFICATION_QUERY = "*IDN?"
|
|
18
16
|
|
|
19
17
|
dev_settings = Settings.load("Keithley DAQ6510")
|
|
20
18
|
|
|
21
19
|
DEVICE_NAME = dev_settings.get("DEVICE_NAME", "DAQ6510")
|
|
22
|
-
DEV_HOST = dev_settings.get("HOSTNAME")
|
|
23
|
-
DEV_PORT = dev_settings.get("PORT")
|
|
20
|
+
DEV_HOST = dev_settings.get("HOSTNAME", "localhost")
|
|
21
|
+
DEV_PORT = dev_settings.get("PORT", 5025)
|
|
24
22
|
READ_TIMEOUT = dev_settings.get("TIMEOUT") # [s], can be smaller than timeout (for DAQ6510Proxy) (e.g. 1s)
|
|
25
23
|
|
|
26
24
|
SEPARATOR = b"\n"
|
|
@@ -42,27 +40,26 @@ class DAQ6510Command(ClientServerCommand):
|
|
|
42
40
|
return out + SEPARATOR_STR
|
|
43
41
|
|
|
44
42
|
|
|
45
|
-
class DAQ6510(
|
|
43
|
+
class DAQ6510(SocketDevice):
|
|
46
44
|
"""Defines the low-level interface to the Keithley DAQ6510 Controller."""
|
|
47
45
|
|
|
48
46
|
def __init__(self, hostname: str = DEV_HOST, port: int = DEV_PORT):
|
|
49
|
-
"""
|
|
47
|
+
"""Initialization of an Ethernet interface for the DAQ6510.
|
|
50
48
|
|
|
51
49
|
Args:
|
|
52
50
|
hostname(str): Hostname to which to open a socket
|
|
53
51
|
port (int): Port to which to open a socket
|
|
54
52
|
"""
|
|
55
53
|
|
|
56
|
-
super().__init__()
|
|
57
|
-
|
|
58
|
-
self.device_name = DEVICE_NAME
|
|
59
|
-
self.hostname = hostname
|
|
60
|
-
self.port = port
|
|
61
|
-
self._sock = None
|
|
54
|
+
super().__init__(hostname, port, separator=SEPARATOR, read_timeout=READ_TIMEOUT)
|
|
62
55
|
|
|
63
|
-
|
|
56
|
+
@property
|
|
57
|
+
def device_name(self) -> str:
|
|
58
|
+
return DEVICE_NAME
|
|
64
59
|
|
|
65
|
-
def initialize(
|
|
60
|
+
def initialize(
|
|
61
|
+
self, commands: list[tuple[str, bool]] | None = None, reset_device: bool = False
|
|
62
|
+
) -> list[str | None]:
|
|
66
63
|
"""Initialize the device with optional reset and command sequence.
|
|
67
64
|
|
|
68
65
|
Performs device initialization by optionally resetting the device and then
|
|
@@ -112,115 +109,24 @@ class DAQ6510(DeviceInterface, DeviceTransport):
|
|
|
112
109
|
|
|
113
110
|
return responses
|
|
114
111
|
|
|
112
|
+
# FIXME: this device interface might be connecting to a simulator instead of a real device!
|
|
115
113
|
def is_simulator(self) -> bool:
|
|
116
114
|
return False
|
|
117
115
|
|
|
118
|
-
def connect(self) -> None:
|
|
119
|
-
"""Connects the device.
|
|
120
|
-
|
|
121
|
-
Raises:
|
|
122
|
-
DeviceConnectionError: When the connection could not be established. Check the logging messages for more
|
|
123
|
-
details.
|
|
124
|
-
DeviceTimeoutError: When the connection timed out.
|
|
125
|
-
ValueError: When hostname or port number are not provided.
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
# Sanity checks
|
|
129
|
-
|
|
130
|
-
if self._is_connection_open:
|
|
131
|
-
logger.warning(f"{DEVICE_NAME}: trying to connect to an already connected socket.")
|
|
132
|
-
return
|
|
133
|
-
|
|
134
|
-
if self.hostname in (None, ""):
|
|
135
|
-
raise ValueError(f"{DEVICE_NAME}: hostname is not initialized.")
|
|
136
|
-
|
|
137
|
-
if self.port in (None, 0):
|
|
138
|
-
raise ValueError(f"{DEVICE_NAME}: port number is not initialized.")
|
|
139
|
-
|
|
140
|
-
# Create a new socket instance
|
|
141
|
-
|
|
142
|
-
try:
|
|
143
|
-
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
144
|
-
# The following lines are to experiment with blocking and timeout, but there is no need.
|
|
145
|
-
# self._sock.setblocking(1)
|
|
146
|
-
# self._sock.settimeout(3)
|
|
147
|
-
except socket.error as e_socket:
|
|
148
|
-
raise DeviceConnectionError(DEVICE_NAME, "Failed to create socket.") from e_socket
|
|
149
|
-
|
|
150
|
-
# Attempt to establish a connection to the remote host
|
|
151
|
-
|
|
152
|
-
# FIXME: Socket shall be closed on exception?
|
|
153
|
-
|
|
154
|
-
# We set a timeout of 3s before connecting and reset to None (=blocking) after the `connect` method has been
|
|
155
|
-
# called. This is because when no device is available, e.g. during testing, the timeout will take about
|
|
156
|
-
# two minutes, which is way too long. It needs to be evaluated if this approach is acceptable and not causing
|
|
157
|
-
# problems during production.
|
|
158
|
-
|
|
159
|
-
try:
|
|
160
|
-
logger.debug(f'Connecting a socket to host "{self.hostname}" using port {self.port}')
|
|
161
|
-
self._sock.settimeout(3)
|
|
162
|
-
self._sock.connect((self.hostname, self.port))
|
|
163
|
-
self._sock.settimeout(None)
|
|
164
|
-
except ConnectionRefusedError as exc:
|
|
165
|
-
raise DeviceConnectionError(DEVICE_NAME, f"Connection refused to {self.hostname}:{self.port}.") from exc
|
|
166
|
-
except TimeoutError as exc:
|
|
167
|
-
raise DeviceTimeoutError(DEVICE_NAME, f"Connection to {self.hostname}:{self.port} timed out.") from exc
|
|
168
|
-
except socket.gaierror as exc:
|
|
169
|
-
raise DeviceConnectionError(DEVICE_NAME, f"Socket address info error for {self.hostname}") from exc
|
|
170
|
-
except socket.herror as exc:
|
|
171
|
-
raise DeviceConnectionError(DEVICE_NAME, f"Socket host address error for {self.hostname}") from exc
|
|
172
|
-
except socket.timeout as exc:
|
|
173
|
-
raise DeviceTimeoutError(DEVICE_NAME, f"Socket timeout error for {self.hostname}:{self.port}") from exc
|
|
174
|
-
except OSError as exc:
|
|
175
|
-
raise DeviceConnectionError(DEVICE_NAME, f"OSError caught ({exc}).") from exc
|
|
176
|
-
|
|
177
|
-
self._is_connection_open = True
|
|
178
|
-
|
|
179
|
-
# Check that we are connected to the controller by issuing the "VERSION" or
|
|
180
|
-
# "*ISDN?" query. If we don't get the right response, then disconnect automatically.
|
|
181
|
-
|
|
182
|
-
if not self.is_connected():
|
|
183
|
-
raise DeviceConnectionError(DEVICE_NAME, "Device is not connected, check logging messages for the cause.")
|
|
184
|
-
|
|
185
|
-
def disconnect(self) -> None:
|
|
186
|
-
"""Disconnects from the Ethernet connection.
|
|
187
|
-
|
|
188
|
-
Raises:
|
|
189
|
-
DeviceConnectionError when the socket could not be closed.
|
|
190
|
-
"""
|
|
191
|
-
|
|
192
|
-
try:
|
|
193
|
-
if self._is_connection_open:
|
|
194
|
-
logger.debug(f"Disconnecting from {self.hostname}")
|
|
195
|
-
self._sock.close()
|
|
196
|
-
self._is_connection_open = False
|
|
197
|
-
except Exception as e_exc:
|
|
198
|
-
raise DeviceConnectionError(DEVICE_NAME, f"Could not close socket to {self.hostname}") from e_exc
|
|
199
|
-
|
|
200
|
-
def reconnect(self):
|
|
201
|
-
"""Reconnects to the device controller.
|
|
202
|
-
|
|
203
|
-
Raises:
|
|
204
|
-
ConnectionError when the device cannot be reconnected for some reason.
|
|
205
|
-
"""
|
|
206
|
-
|
|
207
|
-
if self._is_connection_open:
|
|
208
|
-
self.disconnect()
|
|
209
|
-
self.connect()
|
|
210
|
-
|
|
211
116
|
def is_connected(self) -> bool:
|
|
212
117
|
"""Checks if the device is connected.
|
|
213
118
|
|
|
214
119
|
This will send a query for the device identification and validate the answer.
|
|
215
120
|
|
|
216
|
-
Returns:
|
|
121
|
+
Returns:
|
|
122
|
+
True is the device is connected and answered with the proper ID; False otherwise.
|
|
217
123
|
"""
|
|
218
124
|
|
|
219
|
-
if not self.
|
|
125
|
+
if not self.is_connection_open:
|
|
220
126
|
return False
|
|
221
127
|
|
|
222
128
|
try:
|
|
223
|
-
version = self.query(IDENTIFICATION_QUERY)
|
|
129
|
+
version = self.query(IDENTIFICATION_QUERY).decode().strip()
|
|
224
130
|
except DeviceError as exc:
|
|
225
131
|
logger.exception(exc)
|
|
226
132
|
logger.error("Most probably the client connection was closed. Disconnecting...")
|
|
@@ -234,107 +140,10 @@ class DAQ6510(DeviceInterface, DeviceTransport):
|
|
|
234
140
|
self.disconnect()
|
|
235
141
|
return False
|
|
236
142
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
def write(self, command: str) -> None:
|
|
240
|
-
"""Senda a single command to the device controller without waiting for a response.
|
|
241
|
-
|
|
242
|
-
Args:
|
|
243
|
-
command (str): Command to send to the controller
|
|
244
|
-
|
|
245
|
-
Raises:
|
|
246
|
-
DeviceConnectionError when the command could not be sent due to a communication problem.
|
|
247
|
-
DeviceTimeoutError when the command could not be sent due to a timeout.
|
|
248
|
-
"""
|
|
249
|
-
|
|
250
|
-
try:
|
|
251
|
-
command += SEPARATOR_STR if not command.endswith(SEPARATOR_STR) else ""
|
|
252
|
-
|
|
253
|
-
self._sock.sendall(command.encode())
|
|
254
|
-
|
|
255
|
-
except socket.timeout as e_timeout:
|
|
256
|
-
raise DeviceTimeoutError(DEVICE_NAME, "Socket timeout error") from e_timeout
|
|
257
|
-
except socket.error as e_socket:
|
|
258
|
-
# Interpret any socket-related error as a connection error
|
|
259
|
-
raise DeviceConnectionError(DEVICE_NAME, "Socket communication error.") from e_socket
|
|
260
|
-
except AttributeError:
|
|
261
|
-
if not self._is_connection_open:
|
|
262
|
-
msg = "The DAQ6510 is not connected, use the connect() method."
|
|
263
|
-
raise DeviceConnectionError(DEVICE_NAME, msg)
|
|
264
|
-
raise
|
|
265
|
-
|
|
266
|
-
def trans(self, command: str) -> bytes:
|
|
267
|
-
"""Sends a single command to the device controller and block until a response from the controller.
|
|
268
|
-
|
|
269
|
-
This is seen as a transaction.
|
|
270
|
-
|
|
271
|
-
Args:
|
|
272
|
-
command (str): Command to send to the controller
|
|
273
|
-
|
|
274
|
-
Returns:
|
|
275
|
-
Either a string returned by the controller (on success), or an error message (on failure).
|
|
276
|
-
|
|
277
|
-
Raises:
|
|
278
|
-
DeviceConnectionError when there was an I/O problem during communication with the controller.
|
|
279
|
-
DeviceTimeoutError when there was a timeout in either sending the command or receiving the response.
|
|
280
|
-
"""
|
|
143
|
+
if VERBOSE_DEBUG:
|
|
144
|
+
logger.debug(f"{self.device_name} connection check successful, version: {version}")
|
|
281
145
|
|
|
282
|
-
|
|
283
|
-
# Attempt to send the complete command
|
|
284
|
-
|
|
285
|
-
command += SEPARATOR_STR if not command.endswith(SEPARATOR_STR) else ""
|
|
286
|
-
|
|
287
|
-
self._sock.sendall(command.encode())
|
|
288
|
-
|
|
289
|
-
# wait for, read and return the response from HUBER (will be at most TBD chars)
|
|
290
|
-
|
|
291
|
-
return_string = self.read()
|
|
292
|
-
|
|
293
|
-
return return_string.decode().rstrip()
|
|
294
|
-
|
|
295
|
-
except socket.timeout as e_timeout:
|
|
296
|
-
raise DeviceTimeoutError(DEVICE_NAME, "Socket timeout error") from e_timeout
|
|
297
|
-
except socket.error as e_socket:
|
|
298
|
-
# Interpret any socket-related error as an I/O error
|
|
299
|
-
raise DeviceConnectionError(DEVICE_NAME, "Socket communication error.") from e_socket
|
|
300
|
-
except ConnectionError as exc:
|
|
301
|
-
raise DeviceConnectionError(DEVICE_NAME, "Connection error.") from exc
|
|
302
|
-
except AttributeError:
|
|
303
|
-
if not self._is_connection_open:
|
|
304
|
-
raise DeviceConnectionError(DEVICE_NAME, "Device not connected, use the connect() method.")
|
|
305
|
-
raise
|
|
306
|
-
|
|
307
|
-
def read(self) -> bytes:
|
|
308
|
-
"""Reads from the device buffer.
|
|
309
|
-
|
|
310
|
-
Returns: Content of the device buffer.
|
|
311
|
-
"""
|
|
312
|
-
|
|
313
|
-
n_total = 0
|
|
314
|
-
buf_size = 2048
|
|
315
|
-
|
|
316
|
-
# Set a timeout of READ_TIMEOUT to the socket.recv
|
|
317
|
-
|
|
318
|
-
saved_timeout = self._sock.gettimeout()
|
|
319
|
-
self._sock.settimeout(READ_TIMEOUT)
|
|
320
|
-
|
|
321
|
-
try:
|
|
322
|
-
for idx in range(100):
|
|
323
|
-
time.sleep(0.001) # Give the device time to fill the buffer
|
|
324
|
-
data = self._sock.recv(buf_size)
|
|
325
|
-
n = len(data)
|
|
326
|
-
n_total += n
|
|
327
|
-
if n < buf_size:
|
|
328
|
-
break
|
|
329
|
-
except socket.timeout:
|
|
330
|
-
logger.warning(f"Socket timeout error for {self.hostname}:{self.port}")
|
|
331
|
-
return SEPARATOR
|
|
332
|
-
finally:
|
|
333
|
-
self._sock.settimeout(saved_timeout)
|
|
334
|
-
|
|
335
|
-
# logger.debug(f"Total number of bytes received is {n_total}, idx={idx}")
|
|
336
|
-
|
|
337
|
-
return data
|
|
146
|
+
return True
|
|
338
147
|
|
|
339
148
|
|
|
340
149
|
def main():
|