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.
@@ -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.device import DeviceInterface
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(DeviceInterface, DeviceTransport):
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
- """Initialisation of an Ethernet interface for the DAQ6510.
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
- self._is_connection_open = False
56
+ @property
57
+ def device_name(self) -> str:
58
+ return DEVICE_NAME
64
59
 
65
- def initialize(self, commands: list[tuple[str, bool]] = None, reset_device: bool = False) -> list[str | None]:
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: True is the device is connected and answered with the proper ID; False otherwise.
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._is_connection_open:
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
- return True
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
- try:
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():