keithley-tempcontrol 0.17.3__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.
@@ -1,22 +1,52 @@
1
- import logging
1
+ """
2
+ Keithley DAQ6510 Synchronous Control Server.
3
+
4
+ This module implements a Control Server to command and monitor a Keithley DAQ6510 Data Acquisition System.
5
+
6
+ The control server can be started from the command line interface as follows:
7
+
8
+ $ daq6510_cs start
9
+
10
+ It can also be stopped, queried for status information, etc. Use the --help option to see all available commands:
11
+
12
+ $ daq6510_cs --help
13
+
14
+ Functions:
15
+
16
+ is_daq6510_cs_active(timeout: float = 0.5) -> bool
17
+ Checks if the DAQ6510 Control Server is running.
18
+ Classes:
19
+ DAQ6510ControlServer(ControlServer)
20
+ Keithley DAQ6510ControlServer - Command and monitor the Keithley Data Acquisition System.
21
+
22
+ """
23
+
2
24
  import multiprocessing
3
25
  import sys
4
26
 
5
- import click
6
- import invoke
7
27
  import rich
28
+ import typer
8
29
  import zmq
30
+
31
+ from egse.connect import get_endpoint
9
32
  from egse.control import ControlServer
10
33
  from egse.control import is_control_server_active
34
+ from egse.log import logger
35
+ from egse.logger import remote_logging
36
+ from egse.registry.client import RegistryClient
11
37
  from egse.settings import Settings
38
+ from egse.storage import store_housekeeping_information
12
39
  from egse.tempcontrol.keithley.daq6510 import DAQ6510Proxy
13
40
  from egse.tempcontrol.keithley.daq6510_protocol import DAQ6510Protocol
14
41
  from egse.zmq_ser import connect_address
15
- from prometheus_client import start_http_server
16
42
 
17
- logger = logging.getLogger(__name__)
43
+ cs_settings = Settings.load("Keithley Control Server")
18
44
 
19
- CTRL_SETTINGS = Settings.load("Keithley Control Server")
45
+ PROTOCOL = cs_settings.get("PROTOCOL", "tcp")
46
+ HOSTNAME = cs_settings.get("HOSTNAME", "localhost")
47
+ COMMANDING_PORT = cs_settings.get("COMMANDING_PORT", 0)
48
+ STORAGE_MNEMONIC = cs_settings.get("STORAGE_MNEMONIC", "DAQ6510")
49
+ SERVICE_TYPE = cs_settings.get("SERVICE_TYPE", "daq6510")
20
50
 
21
51
 
22
52
  def is_daq6510_cs_active(timeout: float = 0.5) -> bool:
@@ -28,7 +58,14 @@ def is_daq6510_cs_active(timeout: float = 0.5) -> bool:
28
58
  Returns: True if the Control Server is running and replied with the expected answer; False otherwise.
29
59
  """
30
60
 
31
- endpoint = connect_address(CTRL_SETTINGS.PROTOCOL, CTRL_SETTINGS.HOSTNAME, CTRL_SETTINGS.COMMANDING_PORT)
61
+ if COMMANDING_PORT == 0:
62
+ with RegistryClient() as client:
63
+ endpoint = client.get_endpoint(SERVICE_TYPE)
64
+ if endpoint is None:
65
+ logger.debug(f"No endpoint for {SERVICE_TYPE}")
66
+ return False
67
+ else:
68
+ endpoint = connect_address(PROTOCOL, HOSTNAME, COMMANDING_PORT)
32
69
 
33
70
  return is_control_server_active(endpoint, timeout)
34
71
 
@@ -50,7 +87,7 @@ class DAQ6510ControlServer(ControlServer):
50
87
  """
51
88
 
52
89
  def __init__(self):
53
- """Initialisation of a DAQ6510 Control Server."""
90
+ """Initialization of a DAQ6510 Control Server."""
54
91
 
55
92
  super().__init__()
56
93
 
@@ -62,13 +99,15 @@ class DAQ6510ControlServer(ControlServer):
62
99
 
63
100
  self.poller.register(self.dev_ctrl_cmd_sock, zmq.POLLIN)
64
101
 
102
+ self.register_service(service_type=cs_settings.SERVICE_TYPE)
103
+
65
104
  def get_communication_protocol(self) -> str:
66
105
  """Returns the communication protocol used by the Control Server.
67
106
 
68
107
  Returns: Communication protocol used by the Control Server, as specified in the settings.
69
108
  """
70
109
 
71
- return CTRL_SETTINGS.PROTOCOL
110
+ return cs_settings.PROTOCOL
72
111
 
73
112
  def get_commanding_port(self) -> int:
74
113
  """Returns the commanding port used by the Control Server.
@@ -76,7 +115,7 @@ class DAQ6510ControlServer(ControlServer):
76
115
  Returns: Commanding port used by the Control Server, as specified in the settings.
77
116
  """
78
117
 
79
- return CTRL_SETTINGS.COMMANDING_PORT
118
+ return cs_settings.COMMANDING_PORT
80
119
 
81
120
  def get_service_port(self):
82
121
  """Returns the service port used by the Control Server.
@@ -84,7 +123,7 @@ class DAQ6510ControlServer(ControlServer):
84
123
  Returns: Service port used by the Control Server, as specified in the settings.
85
124
  """
86
125
 
87
- return CTRL_SETTINGS.SERVICE_PORT
126
+ return cs_settings.SERVICE_PORT
88
127
 
89
128
  def get_monitoring_port(self):
90
129
  """Returns the monitoring port used by the Control Server.
@@ -92,7 +131,7 @@ class DAQ6510ControlServer(ControlServer):
92
131
  Returns: Monitoring port used by the Control Server, as specified in the settings.
93
132
  """
94
133
 
95
- return CTRL_SETTINGS.MONITORING_PORT
134
+ return cs_settings.MONITORING_PORT
96
135
 
97
136
  def get_storage_mnemonic(self):
98
137
  """Returns the storage mnemonics used by the Control Server.
@@ -104,57 +143,91 @@ class DAQ6510ControlServer(ControlServer):
104
143
  settings, "DAQ6510" will be used.
105
144
  """
106
145
 
107
- try:
108
- return CTRL_SETTINGS.STORAGE_MNEMONIC
109
- except AttributeError:
110
- return "DAQ6510"
146
+ return STORAGE_MNEMONIC
147
+
148
+ def is_storage_manager_active(self):
149
+ from egse.storage import is_storage_manager_active
150
+
151
+ return is_storage_manager_active()
152
+
153
+ def store_housekeeping_information(self, data):
154
+ """Send housekeeping information to the Storage manager."""
155
+
156
+ origin = self.get_storage_mnemonic()
157
+ store_housekeeping_information(origin, data)
158
+
159
+ def register_to_storage_manager(self):
160
+ from egse.storage import register_to_storage_manager
161
+ from egse.storage.persistence import TYPES
162
+
163
+ register_to_storage_manager(
164
+ origin=self.get_storage_mnemonic(),
165
+ persistence_class=TYPES["CSV"],
166
+ prep={
167
+ "column_names": list(self.device_protocol.get_housekeeping().keys()),
168
+ "mode": "a",
169
+ },
170
+ )
171
+
172
+ def unregister_from_storage_manager(self):
173
+ from egse.storage import unregister_from_storage_manager
174
+
175
+ unregister_from_storage_manager(origin=self.get_storage_mnemonic())
111
176
 
112
177
  def before_serve(self):
113
178
  """Steps to take before the Control Server is activated."""
114
179
 
115
- start_http_server(CTRL_SETTINGS.METRICS_PORT)
180
+ def after_serve(self):
181
+ self.deregister_service()
116
182
 
117
183
 
118
- @click.group()
119
- def cli():
120
- pass
184
+ app = typer.Typer(name="daq6510_cs")
121
185
 
122
186
 
123
- @cli.command()
187
+ @app.command()
124
188
  def start():
125
189
  """Starts the Keithley DAQ6510 Control Server."""
126
190
 
127
191
  multiprocessing.current_process().name = "daq6510_cs (start)"
128
192
 
129
- try:
130
- control_server = DAQ6510ControlServer()
131
- control_server.serve()
132
- except KeyboardInterrupt:
133
- logger.debug("Shutdown requested...exiting")
134
- except SystemExit as exit_code:
135
- logger.debug("System Exit with code {}.".format(exit_code))
136
- sys.exit(exit_code)
137
- except Exception:
138
- msg = "Cannot start the DAQ6510 Control Server"
139
- logger.exception(msg)
140
- rich.print(f"[red]{msg}.")
193
+ with remote_logging():
194
+ from egse.env import setup_env
195
+
196
+ setup_env()
197
+
198
+ try:
199
+ control_server = DAQ6510ControlServer()
200
+ control_server.serve()
201
+ except KeyboardInterrupt:
202
+ logger.debug("Shutdown requested...exiting")
203
+ except SystemExit as exit_code:
204
+ logger.debug("System Exit with code {}.".format(exit_code))
205
+ sys.exit(exit_code.code)
206
+ except Exception:
207
+ msg = "Cannot start the DAQ6510 Control Server"
208
+ logger.exception(msg)
209
+ rich.print(f"[red]{msg}.")
141
210
 
142
211
  return 0
143
212
 
144
213
 
145
- @cli.command()
214
+ @app.command()
146
215
  def start_bg():
147
216
  """Starts the DAQ6510 Control Server in the background."""
148
217
 
149
- invoke.run("daq6510_cs start", disown=True)
218
+ print("Starting the DAQ6510 in the background is not implemented.")
150
219
 
151
220
 
152
- @cli.command()
221
+ @app.command()
153
222
  def stop():
154
223
  """Sends a 'quit_server' command to the Keithley DAQ6510 Control Server."""
155
224
 
156
225
  multiprocessing.current_process().name = "daq6510_cs (stop)"
157
226
 
227
+ from egse.env import setup_env
228
+
229
+ setup_env()
230
+
158
231
  try:
159
232
  with DAQ6510Proxy() as daq:
160
233
  sp = daq.get_service_proxy()
@@ -165,17 +238,17 @@ def stop():
165
238
  rich.print(f"[red]{msg}, could not send the Quit command. [black]Check log messages.")
166
239
 
167
240
 
168
- @cli.command()
241
+ @app.command()
169
242
  def status():
170
243
  """Requests status information from the Control Server."""
171
244
 
172
245
  multiprocessing.current_process().name = "daq6510_cs (status)"
173
246
 
174
- protocol = CTRL_SETTINGS.PROTOCOL
175
- hostname = CTRL_SETTINGS.HOSTNAME
176
- port = CTRL_SETTINGS.COMMANDING_PORT
247
+ from egse.env import setup_env
248
+
249
+ setup_env()
177
250
 
178
- endpoint = connect_address(protocol, hostname, port)
251
+ endpoint = get_endpoint(SERVICE_TYPE, PROTOCOL, HOSTNAME, COMMANDING_PORT)
179
252
 
180
253
  if is_control_server_active(endpoint):
181
254
  rich.print("DAQ6510 CS: [green]active")
@@ -190,6 +263,4 @@ def status():
190
263
 
191
264
 
192
265
  if __name__ == "__main__":
193
- logging.basicConfig(level=logging.DEBUG, format=Settings.LOG_FORMAT_FULL)
194
-
195
- sys.exit(cli())
266
+ sys.exit(app())
@@ -1,23 +1,28 @@
1
- import logging
2
- import socket
3
- import time
1
+ __all__ = [
2
+ "DAQ6510",
3
+ "DAQ6510Command",
4
+ ]
4
5
 
5
6
  from egse.command import ClientServerCommand
6
- from egse.device import DeviceConnectionError
7
- from egse.device import DeviceConnectionInterface
8
7
  from egse.device import DeviceError
9
- from egse.device import DeviceTimeoutError
10
- from egse.device import DeviceTransport
8
+ from egse.env import bool_env
9
+ from egse.log import logger
11
10
  from egse.settings import Settings
12
- from egse.system import Timer
11
+ from egse.socketdevice import SocketDevice
13
12
 
14
- logger = logging.getLogger(__name__)
13
+ VERBOSE_DEBUG = bool_env("VERBOSE_DEBUG")
15
14
 
16
15
  IDENTIFICATION_QUERY = "*IDN?"
17
16
 
18
- DEVICE_SETTINGS = Settings.load("Keithley DAQ6510")
19
- DEVICE_NAME = "DAQ6510"
20
- READ_TIMEOUT = DEVICE_SETTINGS.TIMEOUT # [s], can be smaller than timeout (for DAQ6510Proxy) (e.g. 1s)
17
+ dev_settings = Settings.load("Keithley DAQ6510")
18
+
19
+ DEVICE_NAME = dev_settings.get("DEVICE_NAME", "DAQ6510")
20
+ DEV_HOST = dev_settings.get("HOSTNAME", "localhost")
21
+ DEV_PORT = dev_settings.get("PORT", 5025)
22
+ READ_TIMEOUT = dev_settings.get("TIMEOUT") # [s], can be smaller than timeout (for DAQ6510Proxy) (e.g. 1s)
23
+
24
+ SEPARATOR = b"\n"
25
+ SEPARATOR_STR = SEPARATOR.decode()
21
26
 
22
27
 
23
28
  class DAQ6510Command(ClientServerCommand):
@@ -32,134 +37,96 @@ class DAQ6510Command(ClientServerCommand):
32
37
  """
33
38
 
34
39
  out = super().get_cmd_string(*args, **kwargs)
35
- return out + "\n"
40
+ return out + SEPARATOR_STR
36
41
 
37
42
 
38
- class DAQ6510EthernetInterface(DeviceConnectionInterface, DeviceTransport):
43
+ class DAQ6510(SocketDevice):
39
44
  """Defines the low-level interface to the Keithley DAQ6510 Controller."""
40
45
 
41
- def __init__(self, hostname: str = None, port: int = None):
42
- """Initialisation of an Ethernet interface for the DAQ6510.
46
+ def __init__(self, hostname: str = DEV_HOST, port: int = DEV_PORT):
47
+ """Initialization of an Ethernet interface for the DAQ6510.
43
48
 
44
49
  Args:
45
50
  hostname(str): Hostname to which to open a socket
46
51
  port (int): Port to which to open a socket
47
52
  """
48
53
 
49
- super().__init__()
50
-
51
- self.hostname = DEVICE_SETTINGS.HOSTNAME if hostname is None else hostname
52
- self.port = DEVICE_SETTINGS.PORT if port is None else port
53
- self._sock = None
54
-
55
- self._is_connection_open = False
56
-
57
- def connect(self) -> None:
58
- """Connects the device.
59
-
60
- Raises:
61
- DeviceConnectionError: When the connection could not be established. Check the logging messages for more
62
- details.
63
- DeviceTimeoutError: When the connection timed out.
64
- ValueError: When hostname or port number are not provided.
65
- """
54
+ super().__init__(hostname, port, separator=SEPARATOR, read_timeout=READ_TIMEOUT)
66
55
 
67
- # Sanity checks
56
+ @property
57
+ def device_name(self) -> str:
58
+ return DEVICE_NAME
68
59
 
69
- if self._is_connection_open:
70
- logger.warning(f"{DEVICE_NAME}: trying to connect to an already connected socket.")
71
- return
60
+ def initialize(
61
+ self, commands: list[tuple[str, bool]] | None = None, reset_device: bool = False
62
+ ) -> list[str | None]:
63
+ """Initialize the device with optional reset and command sequence.
72
64
 
73
- if self.hostname in (None, ""):
74
- raise ValueError(f"{DEVICE_NAME}: hostname is not initialized.")
65
+ Performs device initialization by optionally resetting the device and then
66
+ executing a sequence of commands. Each command can optionally expect a
67
+ response that will be logged for debugging purposes.
75
68
 
76
- if self.port in (None, 0):
77
- raise ValueError(f"{DEVICE_NAME}: port number is not initialized.")
78
-
79
- # Create a new socket instance
80
-
81
- try:
82
- self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
83
- # The following lines are to experiment with blocking and timeout, but there is no need.
84
- # self._sock.setblocking(1)
85
- # self._sock.settimeout(3)
86
- except socket.error as e_socket:
87
- raise DeviceConnectionError(DEVICE_NAME, "Failed to create socket.") from e_socket
88
-
89
- # Attempt to establish a connection to the remote host
90
-
91
- # FIXME: Socket shall be closed on exception?
92
-
93
- # We set a timeout of 3s before connecting and reset to None (=blocking) after the `connect` method has been
94
- # called. This is because when no device is available, e.g. during testing, the timeout will take about
95
- # two minutes, which is way too long. It needs to be evaluated if this approach is acceptable and not causing
96
- # problems during production.
69
+ Args:
70
+ commands: List of tuples containing (command_string, expects_response).
71
+ Each tuple specifies a command to send and whether to wait for and
72
+ log the response. Defaults to None (no commands executed).
73
+ reset_device: Whether to send a reset command (*RST) before executing
74
+ the command sequence. Defaults to False.
97
75
 
98
- try:
99
- logger.debug(f'Connecting a socket to host "{self.hostname}" using port {self.port}')
100
- self._sock.settimeout(3)
101
- self._sock.connect((self.hostname, self.port))
102
- self._sock.settimeout(None)
103
- except ConnectionRefusedError as exc:
104
- raise DeviceConnectionError(DEVICE_NAME, f"Connection refused to {self.hostname}:{self.port}.") from exc
105
- except TimeoutError as exc:
106
- raise DeviceTimeoutError(DEVICE_NAME, f"Connection to {self.hostname}:{self.port} timed out.") from exc
107
- except socket.gaierror as exc:
108
- raise DeviceConnectionError(DEVICE_NAME, f"Socket address info error for {self.hostname}") from exc
109
- except socket.herror as exc:
110
- raise DeviceConnectionError(DEVICE_NAME, f"Socket host address error for {self.hostname}") from exc
111
- except socket.timeout as exc:
112
- raise DeviceTimeoutError(DEVICE_NAME, f"Socket timeout error for {self.hostname}:{self.port}") from exc
113
- except OSError as exc:
114
- raise DeviceConnectionError(DEVICE_NAME, f"OSError caught ({exc}).") from exc
115
-
116
- self._is_connection_open = True
117
-
118
- # Check that we are connected to the controller by issuing the "VERSION" or
119
- # "*ISDN?" query. If we don't get the right response, then disconnect automatically.
120
-
121
- if not self.is_connected():
122
- raise DeviceConnectionError(DEVICE_NAME, "Device is not connected, check logging messages for the cause.")
123
-
124
- def disconnect(self) -> None:
125
- """Disconnects from the Ethernet connection.
76
+ Returns:
77
+ Response for each of the commands, or None when no response was expected.
126
78
 
127
79
  Raises:
128
- DeviceConnectionError when the socket could not be closed.
80
+ Any exceptions raised by the underlying write() or trans() methods,
81
+ typically communication errors or device timeouts.
82
+
83
+ Example:
84
+ responses = device.initialize(
85
+ [
86
+ ("*IDN?", True), # Query device ID, expect response
87
+ ("SYST:ERR?", True), # Check for errors, expect response
88
+ ("OUTP ON", False) # Enable output, no response expected
89
+ ],
90
+ reset_device=True
91
+ )
129
92
  """
130
93
 
131
- try:
132
- if self._is_connection_open:
133
- logger.debug(f"Disconnecting from {self.hostname}")
134
- self._sock.close()
135
- self._is_connection_open = False
136
- except Exception as e_exc:
137
- raise DeviceConnectionError(DEVICE_NAME, f"Could not close socket to {self.hostname}") from e_exc
94
+ commands = commands or []
95
+ responses = []
138
96
 
139
- def reconnect(self):
140
- """Reconnects to the device controller.
97
+ if reset_device:
98
+ logger.info(f"Resetting the {self.device_name}...")
99
+ self.write("*RST") # this also resets the user-defined buffer
141
100
 
142
- Raises:
143
- ConnectionError when the device cannot be reconnected for some reason.
144
- """
101
+ for cmd, expects_response in commands:
102
+ if expects_response:
103
+ logger.debug(f"Sending {cmd}...")
104
+ response = self.trans(cmd).decode().strip()
105
+ logger.debug(f"{response = }")
106
+ else:
107
+ logger.debug(f"Sending {cmd}...")
108
+ self.write(cmd)
145
109
 
146
- if self._is_connection_open:
147
- self.disconnect()
148
- self.connect()
110
+ return responses
111
+
112
+ # FIXME: this device interface might be connecting to a simulator instead of a real device!
113
+ def is_simulator(self) -> bool:
114
+ return False
149
115
 
150
116
  def is_connected(self) -> bool:
151
117
  """Checks if the device is connected.
152
118
 
153
119
  This will send a query for the device identification and validate the answer.
154
120
 
155
- 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.
156
123
  """
157
124
 
158
- if not self._is_connection_open:
125
+ if not self.is_connection_open:
159
126
  return False
160
127
 
161
128
  try:
162
- version = self.query(IDENTIFICATION_QUERY)
129
+ version = self.query(IDENTIFICATION_QUERY).decode().strip()
163
130
  except DeviceError as exc:
164
131
  logger.exception(exc)
165
132
  logger.error("Most probably the client connection was closed. Disconnecting...")
@@ -173,110 +140,10 @@ class DAQ6510EthernetInterface(DeviceConnectionInterface, DeviceTransport):
173
140
  self.disconnect()
174
141
  return False
175
142
 
176
- return True
177
-
178
- def write(self, command: str) -> None:
179
- """Senda a single command to the device controller without waiting for a response.
180
-
181
- Args:
182
- command (str): Command to send to the controller
183
-
184
- Raises:
185
- DeviceConnectionError when the command could not be sent due to a communication problem.
186
- DeviceTimeoutError when the command could not be sent due to a timeout.
187
- """
188
-
189
- try:
190
- command += "\n" if not command.endswith("\n") else ""
191
-
192
- self._sock.sendall(command.encode())
193
-
194
- except socket.timeout as e_timeout:
195
- raise DeviceTimeoutError(DEVICE_NAME, "Socket timeout error") from e_timeout
196
- except socket.error as e_socket:
197
- # Interpret any socket-related error as a connection error
198
- raise DeviceConnectionError(DEVICE_NAME, "Socket communication error.") from e_socket
199
- except AttributeError:
200
- if not self._is_connection_open:
201
- msg = "The DAQ6510 is not connected, use the connect() method."
202
- raise DeviceConnectionError(DEVICE_NAME, msg)
203
- raise
204
-
205
- def trans(self, command: str) -> str:
206
- """Sends a single command to the device controller and block until a response from the controller.
207
-
208
- This is seen as a transaction.
209
-
210
- Args:
211
- command (str): Command to send to the controller
212
-
213
- Returns:
214
- Either a string returned by the controller (on success), or an error message (on failure).
215
-
216
- Raises:
217
- DeviceConnectionError when there was an I/O problem during communication with the controller.
218
- DeviceTimeoutError when there was a timeout in either sending the command or receiving the response.
219
- """
220
-
221
- try:
222
- # Attempt to send the complete command
223
-
224
- command += "\n" if not command.endswith("\n") else ""
225
-
226
- self._sock.sendall(command.encode())
227
-
228
- # wait for, read and return the response from HUBER (will be at most TBD chars)
229
-
230
- return_string = self.read()
231
-
232
- return return_string.decode().rstrip()
233
-
234
- except socket.timeout as e_timeout:
235
- raise DeviceTimeoutError(DEVICE_NAME, "Socket timeout error") from e_timeout
236
- except socket.error as e_socket:
237
- # Interpret any socket-related error as an I/O error
238
- raise DeviceConnectionError(DEVICE_NAME, "Socket communication error.") from e_socket
239
- except ConnectionError as exc:
240
- raise DeviceConnectionError(DEVICE_NAME, "Connection error.") from exc
241
- except AttributeError:
242
- if not self._is_connection_open:
243
- raise DeviceConnectionError(DEVICE_NAME, "Device not connected, use the connect() method.")
244
- raise
245
-
246
- def read(self) -> bytes:
247
- """Reads from the device buffer.
143
+ if VERBOSE_DEBUG:
144
+ logger.debug(f"{self.device_name} connection check successful, version: {version}")
248
145
 
249
- Returns: Content of the device buffer.
250
- """
251
-
252
- n_total = 0
253
- buf_size = 2048
254
-
255
- # Set a timeout of READ_TIMEOUT to the socket.recv
256
-
257
- saved_timeout = self._sock.gettimeout()
258
- self._sock.settimeout(READ_TIMEOUT)
259
-
260
- try:
261
- for idx in range(100):
262
- time.sleep(0.001) # Give the device time to fill the buffer
263
- data = self._sock.recv(buf_size)
264
- n = len(data)
265
- n_total += n
266
- if n < buf_size:
267
- break
268
- except socket.timeout:
269
- logger.warning(f"Socket timeout error for {self.hostname}:{self.port}")
270
- return b"\r\n"
271
- except TimeoutError as exc:
272
- logger.warning(f"Socket timeout error: {exc}")
273
- return b"\r\n"
274
- finally:
275
- self._sock.settimeout(saved_timeout)
276
-
277
- # logger.debug(f"Total number of bytes received is {n_total}, idx={idx}")
278
-
279
- return data
146
+ return True
280
147
 
281
148
 
282
149
  def main():