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.
- egse/tempcontrol/keithley/__init__.py +0 -2
- egse/tempcontrol/keithley/daq6510.py +101 -255
- egse/tempcontrol/keithley/daq6510_adev.py +27 -35
- egse/tempcontrol/keithley/daq6510_amon.py +569 -0
- egse/tempcontrol/keithley/daq6510_cs.py +115 -44
- egse/tempcontrol/keithley/daq6510_dev.py +77 -210
- egse/tempcontrol/keithley/daq6510_mon.py +242 -453
- egse/tempcontrol/keithley/daq6510_protocol.py +43 -38
- egse/tempcontrol/keithley/daq6510_sim.py +119 -62
- keithley_tempcontrol/cgse_services.py +58 -7
- {keithley_tempcontrol-0.17.3.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.3.dist-info → keithley_tempcontrol-0.18.1.dist-info}/entry_points.txt +1 -0
- keithley_tempcontrol-0.17.3.dist-info/RECORD +0 -17
- {keithley_tempcontrol-0.17.3.dist-info → keithley_tempcontrol-0.18.1.dist-info}/WHEEL +0 -0
|
@@ -1,22 +1,52 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
43
|
+
cs_settings = Settings.load("Keithley Control Server")
|
|
18
44
|
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
"""
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
180
|
+
def after_serve(self):
|
|
181
|
+
self.deregister_service()
|
|
116
182
|
|
|
117
183
|
|
|
118
|
-
|
|
119
|
-
def cli():
|
|
120
|
-
pass
|
|
184
|
+
app = typer.Typer(name="daq6510_cs")
|
|
121
185
|
|
|
122
186
|
|
|
123
|
-
@
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
@
|
|
214
|
+
@app.command()
|
|
146
215
|
def start_bg():
|
|
147
216
|
"""Starts the DAQ6510 Control Server in the background."""
|
|
148
217
|
|
|
149
|
-
|
|
218
|
+
print("Starting the DAQ6510 in the background is not implemented.")
|
|
150
219
|
|
|
151
220
|
|
|
152
|
-
@
|
|
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
|
-
@
|
|
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
|
-
|
|
175
|
-
|
|
176
|
-
|
|
247
|
+
from egse.env import setup_env
|
|
248
|
+
|
|
249
|
+
setup_env()
|
|
177
250
|
|
|
178
|
-
endpoint =
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
sys.exit(cli())
|
|
266
|
+
sys.exit(app())
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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.
|
|
10
|
-
from egse.
|
|
8
|
+
from egse.env import bool_env
|
|
9
|
+
from egse.log import logger
|
|
11
10
|
from egse.settings import Settings
|
|
12
|
-
from egse.
|
|
11
|
+
from egse.socketdevice import SocketDevice
|
|
13
12
|
|
|
14
|
-
|
|
13
|
+
VERBOSE_DEBUG = bool_env("VERBOSE_DEBUG")
|
|
15
14
|
|
|
16
15
|
IDENTIFICATION_QUERY = "*IDN?"
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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 +
|
|
40
|
+
return out + SEPARATOR_STR
|
|
36
41
|
|
|
37
42
|
|
|
38
|
-
class
|
|
43
|
+
class DAQ6510(SocketDevice):
|
|
39
44
|
"""Defines the low-level interface to the Keithley DAQ6510 Controller."""
|
|
40
45
|
|
|
41
|
-
def __init__(self, hostname: str =
|
|
42
|
-
"""
|
|
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
|
-
|
|
56
|
+
@property
|
|
57
|
+
def device_name(self) -> str:
|
|
58
|
+
return DEVICE_NAME
|
|
68
59
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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:
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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():
|