keithley-tempcontrol 0.17.0__py3-none-any.whl → 0.17.2__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/daq6510_cs.py +3 -0
- egse/tempcontrol/keithley/daq6510_dev.py +10 -8
- egse/tempcontrol/keithley/daq6510_sim.py +115 -58
- {keithley_tempcontrol-0.17.0.dist-info → keithley_tempcontrol-0.17.2.dist-info}/METADATA +1 -1
- {keithley_tempcontrol-0.17.0.dist-info → keithley_tempcontrol-0.17.2.dist-info}/RECORD +7 -7
- {keithley_tempcontrol-0.17.0.dist-info → keithley_tempcontrol-0.17.2.dist-info}/WHEEL +1 -1
- {keithley_tempcontrol-0.17.0.dist-info → keithley_tempcontrol-0.17.2.dist-info}/entry_points.txt +0 -0
|
@@ -166,6 +166,7 @@ def start():
|
|
|
166
166
|
|
|
167
167
|
with remote_logging():
|
|
168
168
|
from egse.env import setup_env
|
|
169
|
+
|
|
169
170
|
setup_env()
|
|
170
171
|
|
|
171
172
|
try:
|
|
@@ -198,6 +199,7 @@ def stop():
|
|
|
198
199
|
multiprocessing.current_process().name = "daq6510_cs (stop)"
|
|
199
200
|
|
|
200
201
|
from egse.env import setup_env
|
|
202
|
+
|
|
201
203
|
setup_env()
|
|
202
204
|
|
|
203
205
|
try:
|
|
@@ -217,6 +219,7 @@ def status():
|
|
|
217
219
|
multiprocessing.current_process().name = "daq6510_cs (status)"
|
|
218
220
|
|
|
219
221
|
from egse.env import setup_env
|
|
222
|
+
|
|
220
223
|
setup_env()
|
|
221
224
|
|
|
222
225
|
endpoint = get_endpoint(SERVICE_TYPE, PROTOCOL, HOSTNAME, COMMANDING_PORT)
|
|
@@ -23,6 +23,9 @@ DEV_HOST = dev_settings.get("HOSTNAME")
|
|
|
23
23
|
DEV_PORT = dev_settings.get("PORT")
|
|
24
24
|
READ_TIMEOUT = dev_settings.get("TIMEOUT") # [s], can be smaller than timeout (for DAQ6510Proxy) (e.g. 1s)
|
|
25
25
|
|
|
26
|
+
SEPARATOR = b"\n"
|
|
27
|
+
SEPARATOR_STR = SEPARATOR.decode()
|
|
28
|
+
|
|
26
29
|
|
|
27
30
|
class DAQ6510Command(ClientServerCommand):
|
|
28
31
|
def get_cmd_string(self, *args, **kwargs) -> str:
|
|
@@ -36,7 +39,7 @@ class DAQ6510Command(ClientServerCommand):
|
|
|
36
39
|
"""
|
|
37
40
|
|
|
38
41
|
out = super().get_cmd_string(*args, **kwargs)
|
|
39
|
-
return out +
|
|
42
|
+
return out + SEPARATOR_STR
|
|
40
43
|
|
|
41
44
|
|
|
42
45
|
class DAQ6510(DeviceInterface, DeviceTransport):
|
|
@@ -107,6 +110,8 @@ class DAQ6510(DeviceInterface, DeviceTransport):
|
|
|
107
110
|
logger.debug(f"Sending {cmd}...")
|
|
108
111
|
self.write(cmd)
|
|
109
112
|
|
|
113
|
+
return responses
|
|
114
|
+
|
|
110
115
|
def is_simulator(self) -> bool:
|
|
111
116
|
return False
|
|
112
117
|
|
|
@@ -243,7 +248,7 @@ class DAQ6510(DeviceInterface, DeviceTransport):
|
|
|
243
248
|
"""
|
|
244
249
|
|
|
245
250
|
try:
|
|
246
|
-
command +=
|
|
251
|
+
command += SEPARATOR_STR if not command.endswith(SEPARATOR_STR) else ""
|
|
247
252
|
|
|
248
253
|
self._sock.sendall(command.encode())
|
|
249
254
|
|
|
@@ -258,7 +263,7 @@ class DAQ6510(DeviceInterface, DeviceTransport):
|
|
|
258
263
|
raise DeviceConnectionError(DEVICE_NAME, msg)
|
|
259
264
|
raise
|
|
260
265
|
|
|
261
|
-
def trans(self, command: str) ->
|
|
266
|
+
def trans(self, command: str) -> bytes:
|
|
262
267
|
"""Sends a single command to the device controller and block until a response from the controller.
|
|
263
268
|
|
|
264
269
|
This is seen as a transaction.
|
|
@@ -277,7 +282,7 @@ class DAQ6510(DeviceInterface, DeviceTransport):
|
|
|
277
282
|
try:
|
|
278
283
|
# Attempt to send the complete command
|
|
279
284
|
|
|
280
|
-
command +=
|
|
285
|
+
command += SEPARATOR_STR if not command.endswith(SEPARATOR_STR) else ""
|
|
281
286
|
|
|
282
287
|
self._sock.sendall(command.encode())
|
|
283
288
|
|
|
@@ -323,10 +328,7 @@ class DAQ6510(DeviceInterface, DeviceTransport):
|
|
|
323
328
|
break
|
|
324
329
|
except socket.timeout:
|
|
325
330
|
logger.warning(f"Socket timeout error for {self.hostname}:{self.port}")
|
|
326
|
-
return
|
|
327
|
-
except TimeoutError as exc:
|
|
328
|
-
logger.warning(f"Socket timeout error: {exc}")
|
|
329
|
-
return b"\r\n"
|
|
331
|
+
return SEPARATOR
|
|
330
332
|
finally:
|
|
331
333
|
self._sock.settimeout(saved_timeout)
|
|
332
334
|
|
|
@@ -1,21 +1,32 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
1
|
import contextlib
|
|
4
2
|
import datetime
|
|
5
|
-
import logging
|
|
6
3
|
import re
|
|
7
4
|
import socket
|
|
8
5
|
import time
|
|
6
|
+
from functools import partial
|
|
7
|
+
from typing import Annotated
|
|
9
8
|
|
|
10
9
|
import typer
|
|
10
|
+
|
|
11
|
+
from egse.env import bool_env
|
|
12
|
+
from egse.log import logging
|
|
11
13
|
from egse.settings import Settings
|
|
12
14
|
from egse.system import SignalCatcher
|
|
13
15
|
|
|
14
|
-
logger = logging.getLogger("daq6510-sim")
|
|
16
|
+
logger = logging.getLogger("egse.daq6510-sim")
|
|
15
17
|
|
|
18
|
+
VERSION = "0.1.0"
|
|
19
|
+
VERBOSE_DEBUG = bool_env("VERBOSE_DEBUG")
|
|
16
20
|
HOST = "localhost"
|
|
17
21
|
DAQ_SETTINGS = Settings.load("Keithley DAQ6510")
|
|
18
22
|
|
|
23
|
+
READ_TIMEOUT = 2.0
|
|
24
|
+
"""The timeout set on the connection socket, applicable when reading from the socket with `recv`."""
|
|
25
|
+
CONNECTION_TIMEOUT = 2.0
|
|
26
|
+
"""The timeout set on the socket before accepting a connection."""
|
|
27
|
+
|
|
28
|
+
SEPARATOR = b"\n"
|
|
29
|
+
SEPARATOR_STR = SEPARATOR.decode()
|
|
19
30
|
|
|
20
31
|
device_time = datetime.datetime.now(datetime.timezone.utc)
|
|
21
32
|
reference_time = device_time
|
|
@@ -23,8 +34,8 @@ reference_time = device_time
|
|
|
23
34
|
|
|
24
35
|
app = typer.Typer(help="DAQ6510 Simulator")
|
|
25
36
|
|
|
26
|
-
error_msg: str
|
|
27
|
-
"""Global error message, always contains the last error. Reset in the inner loop of run_simulator."""
|
|
37
|
+
error_msg: str = ""
|
|
38
|
+
"""Global error message, always contains the last error. Reset to an empty string in the inner loop of run_simulator."""
|
|
28
39
|
|
|
29
40
|
|
|
30
41
|
def create_datetime(year, month, day, hour, minute, second):
|
|
@@ -62,8 +73,14 @@ def reset():
|
|
|
62
73
|
logger.info("RESET")
|
|
63
74
|
|
|
64
75
|
|
|
76
|
+
def log(level: int, msg: str):
|
|
77
|
+
logger.log(level, msg)
|
|
78
|
+
|
|
79
|
+
|
|
65
80
|
COMMAND_ACTIONS_RESPONSES = {
|
|
66
|
-
"*IDN?": (None, "KEITHLEY INSTRUMENTS,
|
|
81
|
+
"*IDN?": (None, f"KEITHLEY INSTRUMENTS,DAQ6510,SIMULATOR,{VERSION}"),
|
|
82
|
+
"*ACTION-RESPONSE?": (partial(log, logging.INFO, "Requested action with response."), get_time),
|
|
83
|
+
"*ACTION-NO-RESPONSE": (partial(log, logging.INFO, "Requested action without response."), None),
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
# Check the regex at https://regex101.com
|
|
@@ -79,65 +96,105 @@ COMMAND_PATTERNS_ACTIONS_RESPONSES = {
|
|
|
79
96
|
|
|
80
97
|
|
|
81
98
|
def write(conn, response: str):
|
|
82
|
-
response = f"{response}
|
|
83
|
-
|
|
99
|
+
response = f"{response}{SEPARATOR_STR}".encode()
|
|
100
|
+
if VERBOSE_DEBUG:
|
|
101
|
+
logger.debug(f"write: {response = }")
|
|
84
102
|
conn.sendall(response)
|
|
85
103
|
|
|
86
104
|
|
|
105
|
+
# Keep a receive buffer per connection
|
|
106
|
+
_recv_buffers: dict[int, bytes] = {}
|
|
107
|
+
|
|
108
|
+
|
|
87
109
|
def read(conn) -> str:
|
|
88
110
|
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
The command string with the linefeed stripped off.
|
|
111
|
+
Read bytes from `conn` until a `SEPARATOR` is found (or connection closed / timeout).
|
|
112
|
+
Returns the first chunk (separator stripped). Any bytes after the separator are kept
|
|
113
|
+
in a per-connection buffer for the next call.
|
|
93
114
|
"""
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
buf_size = 1024 * 4
|
|
97
|
-
command_string = bytes()
|
|
115
|
+
fileno = conn.fileno()
|
|
116
|
+
buf = _recv_buffers.get(fileno, b"")
|
|
98
117
|
|
|
99
118
|
try:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
while True:
|
|
120
|
+
# If we already have a full line in the buffer, split and return it.
|
|
121
|
+
if SEPARATOR in buf:
|
|
122
|
+
line, rest = buf.split(SEPARATOR, 1)
|
|
123
|
+
_recv_buffers[fileno] = rest
|
|
124
|
+
logger.info(f"read: {line=}")
|
|
125
|
+
return line.decode().rstrip()
|
|
126
|
+
|
|
127
|
+
# Read more data
|
|
128
|
+
data = conn.recv(1024 * 4)
|
|
129
|
+
if not data:
|
|
130
|
+
# Connection closed by peer; return whatever we have (may be empty)
|
|
131
|
+
_recv_buffers.pop(fileno, None)
|
|
132
|
+
logger.info(f"read (connection closed): {buf=}")
|
|
133
|
+
return buf.decode().rstrip()
|
|
134
|
+
buf += data
|
|
135
|
+
_recv_buffers[fileno] = buf
|
|
136
|
+
|
|
108
137
|
except socket.timeout:
|
|
109
|
-
#
|
|
138
|
+
# If we have accumulated data without a separator, return it (partial read),
|
|
139
|
+
# otherwise propagate the timeout so caller can handle/suppress it.
|
|
140
|
+
if buf:
|
|
141
|
+
_recv_buffers[fileno] = buf
|
|
142
|
+
logger.info(f"read (timeout, partial): {buf=}")
|
|
143
|
+
return buf.decode().rstrip()
|
|
110
144
|
raise
|
|
111
145
|
|
|
112
|
-
logger.info(f"read: {command_string=}")
|
|
113
|
-
|
|
114
|
-
return command_string.decode().rstrip()
|
|
115
|
-
|
|
116
146
|
|
|
117
|
-
def process_command(command_string: str) -> str:
|
|
147
|
+
def process_command(command_string: str) -> str | None:
|
|
148
|
+
"""Process the given command string and return a response."""
|
|
118
149
|
global COMMAND_ACTIONS_RESPONSES
|
|
119
150
|
global COMMAND_PATTERNS_ACTIONS_RESPONSES
|
|
120
151
|
global error_msg
|
|
121
152
|
|
|
122
|
-
|
|
153
|
+
if VERBOSE_DEBUG:
|
|
154
|
+
logger.debug(f"{command_string=}")
|
|
123
155
|
|
|
124
156
|
try:
|
|
125
157
|
action, response = COMMAND_ACTIONS_RESPONSES[command_string]
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
158
|
+
if VERBOSE_DEBUG:
|
|
159
|
+
logger.debug(f"{action=}, {response=}")
|
|
160
|
+
|
|
161
|
+
if action:
|
|
162
|
+
action()
|
|
163
|
+
|
|
164
|
+
if response:
|
|
165
|
+
if error_msg:
|
|
166
|
+
return error_msg
|
|
167
|
+
else:
|
|
168
|
+
return response() if callable(response) else response
|
|
129
169
|
else:
|
|
130
|
-
|
|
170
|
+
if error_msg:
|
|
171
|
+
logger.error(f"Error occurred during process command: {error_msg}")
|
|
172
|
+
return None
|
|
131
173
|
except KeyError:
|
|
132
174
|
# try to match with a value
|
|
133
175
|
for key, value in COMMAND_PATTERNS_ACTIONS_RESPONSES.items():
|
|
134
176
|
if match := re.match(key, command_string, flags=re.IGNORECASE):
|
|
135
|
-
|
|
177
|
+
if VERBOSE_DEBUG:
|
|
178
|
+
logger.debug(f"{match=}, {match.groups()}")
|
|
136
179
|
action, response = value
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
180
|
+
if VERBOSE_DEBUG:
|
|
181
|
+
logger.debug(f"{action=}, {response=}")
|
|
182
|
+
|
|
183
|
+
if action:
|
|
184
|
+
action(*match.groups())
|
|
185
|
+
|
|
186
|
+
if response:
|
|
187
|
+
if error_msg:
|
|
188
|
+
return error_msg
|
|
189
|
+
else:
|
|
190
|
+
return response() if callable(response) else response
|
|
191
|
+
else:
|
|
192
|
+
if error_msg:
|
|
193
|
+
logger.error(f"Error occurred during process command: {error_msg}")
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
logger.error(f"ERROR: unknown command string: {command_string}")
|
|
197
|
+
return None
|
|
141
198
|
|
|
142
199
|
|
|
143
200
|
def run_simulator():
|
|
@@ -150,7 +207,7 @@ def run_simulator():
|
|
|
150
207
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
151
208
|
s.bind((HOST, DAQ_SETTINGS.PORT))
|
|
152
209
|
s.listen()
|
|
153
|
-
s.settimeout(
|
|
210
|
+
s.settimeout(CONNECTION_TIMEOUT)
|
|
154
211
|
while True:
|
|
155
212
|
while True:
|
|
156
213
|
with contextlib.suppress(socket.timeout):
|
|
@@ -161,13 +218,17 @@ def run_simulator():
|
|
|
161
218
|
with conn:
|
|
162
219
|
logger.info(f"Accepted connection from {addr}")
|
|
163
220
|
write(conn, "This is PLATO DAQ6510 X.X.sim")
|
|
164
|
-
conn.settimeout(
|
|
221
|
+
conn.settimeout(READ_TIMEOUT)
|
|
165
222
|
try:
|
|
166
223
|
while True:
|
|
167
224
|
error_msg = ""
|
|
168
225
|
with contextlib.suppress(socket.timeout):
|
|
169
226
|
data = read(conn)
|
|
170
|
-
|
|
227
|
+
if VERBOSE_DEBUG:
|
|
228
|
+
logger.debug(f"{data = }")
|
|
229
|
+
if not data:
|
|
230
|
+
logger.info("Client closed connection, accepting new connection...")
|
|
231
|
+
break
|
|
171
232
|
if data.strip() == "STOP":
|
|
172
233
|
logger.info("Client requested to terminate...")
|
|
173
234
|
s.close()
|
|
@@ -176,9 +237,6 @@ def run_simulator():
|
|
|
176
237
|
response = process_command(cmd.strip())
|
|
177
238
|
if response is not None:
|
|
178
239
|
write(conn, response)
|
|
179
|
-
if not data:
|
|
180
|
-
logger.info("Client closed connection, accepting new connection...")
|
|
181
|
-
break
|
|
182
240
|
if killer.term_signal_received:
|
|
183
241
|
logger.info("Terminating...")
|
|
184
242
|
s.close()
|
|
@@ -196,7 +254,7 @@ def run_simulator():
|
|
|
196
254
|
logger.info(f"{exc.__class__.__name__} caught: {exc.args}")
|
|
197
255
|
|
|
198
256
|
|
|
199
|
-
def send_request(cmd: str,
|
|
257
|
+
def send_request(cmd: str, cmd_type: str = "query") -> str | None:
|
|
200
258
|
from egse.tempcontrol.keithley.daq6510_dev import DAQ6510
|
|
201
259
|
|
|
202
260
|
response = None
|
|
@@ -204,12 +262,12 @@ def send_request(cmd: str, type_: str = "query"):
|
|
|
204
262
|
daq_dev = DAQ6510(hostname="localhost", port=5025)
|
|
205
263
|
daq_dev.connect()
|
|
206
264
|
|
|
207
|
-
if
|
|
265
|
+
if cmd_type.lower().strip() == "query":
|
|
208
266
|
response = daq_dev.query(cmd)
|
|
209
|
-
elif
|
|
267
|
+
elif cmd_type.lower().strip() == "write":
|
|
210
268
|
daq_dev.write(cmd)
|
|
211
269
|
else:
|
|
212
|
-
logger.info(f"Unknown type {
|
|
270
|
+
logger.info(f"Unknown command type {cmd_type} for send_request.")
|
|
213
271
|
|
|
214
272
|
daq_dev.disconnect()
|
|
215
273
|
|
|
@@ -234,15 +292,14 @@ def stop():
|
|
|
234
292
|
|
|
235
293
|
|
|
236
294
|
@app.command()
|
|
237
|
-
def command(
|
|
238
|
-
|
|
295
|
+
def command(
|
|
296
|
+
cmd: str,
|
|
297
|
+
cmd_type: Annotated[str, typer.Argument(help="either 'write', 'query'")] = "query",
|
|
298
|
+
):
|
|
299
|
+
"""Send an SCPI command directly to the simulator. The response will be in the log info."""
|
|
300
|
+
response = send_request(cmd, cmd_type)
|
|
239
301
|
logger.info(f"{response}")
|
|
240
302
|
|
|
241
303
|
|
|
242
304
|
if __name__ == "__main__":
|
|
243
|
-
logging.basicConfig(
|
|
244
|
-
level=logging.DEBUG,
|
|
245
|
-
format="%(asctime)s %(threadName)-12s %(levelname)-8s %(name)-12s %(module)-20s %(message)s",
|
|
246
|
-
)
|
|
247
|
-
|
|
248
305
|
app()
|
|
@@ -3,16 +3,16 @@ egse/tempcontrol/keithley/daq6510.py,sha256=pe12HIC2Yav5ZCGYecoQzhypYcCwecEaJpWZ
|
|
|
3
3
|
egse/tempcontrol/keithley/daq6510.yaml,sha256=dHHVNyUpOQpdrZpnxPbT6slsl-8Gbnhifj4Q8QOfOYg,4400
|
|
4
4
|
egse/tempcontrol/keithley/daq6510_acs.py,sha256=6sGc2E8gg67ZwkNmHtNSsunC6cN0IYNwtJXC3yMYcpM,64
|
|
5
5
|
egse/tempcontrol/keithley/daq6510_adev.py,sha256=WjBuQvhpeXr2WHVB54mACM5WQwnecSY6I3iG2Cajs5c,2247
|
|
6
|
-
egse/tempcontrol/keithley/daq6510_cs.py,sha256=
|
|
7
|
-
egse/tempcontrol/keithley/daq6510_dev.py,sha256=
|
|
6
|
+
egse/tempcontrol/keithley/daq6510_cs.py,sha256=Ga7z8S6z0oTxL_qQP8FXPaNKlJ6o9RrsPFOXeIJ2TT4,7700
|
|
7
|
+
egse/tempcontrol/keithley/daq6510_dev.py,sha256=EKFFDhP8-FdIPBPosc1DfzE3h4DQmNU1d0Fi7Yhx4I4,12906
|
|
8
8
|
egse/tempcontrol/keithley/daq6510_mon.py,sha256=Xbn2U-l9uxPwNN1-aYW72oJodL2sx13suCiPPbDSti0,20932
|
|
9
9
|
egse/tempcontrol/keithley/daq6510_protocol.py,sha256=v8FUrxEm7bnRzM_iQzW0mMCHTgAMZw4f2Ronl8fdKIE,2676
|
|
10
|
-
egse/tempcontrol/keithley/daq6510_sim.py,sha256=
|
|
10
|
+
egse/tempcontrol/keithley/daq6510_sim.py,sha256=Ys5jroT-2i-V_qCR0L6yOYM9CiktnNbB3LvVLGwaD8A,9917
|
|
11
11
|
keithley_tempcontrol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
keithley_tempcontrol/cgse_explore.py,sha256=y_FkFxJW0vdqGNp9yTU0ELBKxby74-ev3fTuf99Vl1s,400
|
|
13
13
|
keithley_tempcontrol/cgse_services.py,sha256=tndviv2rvygkNSsGy1oA43VfFpyVkdB9If-9sVlLbK4,2466
|
|
14
14
|
keithley_tempcontrol/settings.yaml,sha256=wbrgSZQAdqFl6AxiLJIN36UsdiVHQCzdsgi7Hs7dv7o,1467
|
|
15
|
-
keithley_tempcontrol-0.17.
|
|
16
|
-
keithley_tempcontrol-0.17.
|
|
17
|
-
keithley_tempcontrol-0.17.
|
|
18
|
-
keithley_tempcontrol-0.17.
|
|
15
|
+
keithley_tempcontrol-0.17.2.dist-info/METADATA,sha256=wX8ndoLOCImQ_LuO8U2JxzCodlil3X0Ydp6_GB8Dg70,962
|
|
16
|
+
keithley_tempcontrol-0.17.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
keithley_tempcontrol-0.17.2.dist-info/entry_points.txt,sha256=_0j2BwcwPi4LlRrhvEWfp9GO9KT8WhCkJe2gFgMzOPs,491
|
|
18
|
+
keithley_tempcontrol-0.17.2.dist-info/RECORD,,
|
{keithley_tempcontrol-0.17.0.dist-info → keithley_tempcontrol-0.17.2.dist-info}/entry_points.txt
RENAMED
|
File without changes
|